Tak. Nazywa się to „stylem przekazywania słownika”. Czasami, gdy robię pewne szczególnie trudne rzeczy, muszę zeskrobać klasę i przekształcić ją w słownik, ponieważ przekazywanie słowników jest bardziej wydajne 1 , ale często dość kłopotliwe, co sprawia, że prosty koncepcyjnie kod wygląda na dość skomplikowany. Czasami używam stylu przekazywania słownika w językach, które nie są Haskellem, do symulacji klas (ale nauczyłem się, że to zwykle nie jest tak świetny pomysł, jak się wydaje).
Oczywiście, ilekroć występuje różnica w sile ekspresji, następuje kompromis. Chociaż możesz użyć danego interfejsu API na wiele sposobów, jeśli jest napisany przy użyciu DPS, interfejs API otrzymuje więcej informacji, jeśli nie możesz. Jednym ze sposobów jest to w praktyce Data.Set
, które polega na tym, że istnieje tylko jeden Ord
słownik dla każdego typu. W Set
sklepach jej elementy sortowane według Ord
, a jeśli zbudować zestaw z jednego słownika, a następnie wstawiony element używając innego jednego, jak byłoby to możliwe z DPS, można złamać Set
„s niezmienne i spowodować jego awarię. Ten problem wyjątkowości można złagodzić za pomocą fantomu egzystencjalnegowpisz, aby zaznaczyć słownik, ale znowu kosztem dość irytującej złożoności interfejsu API. Widoczne jest to również w prawie taki sam sposób w Typeable
interfejsie API.
Bit wyjątkowości nie pojawia się zbyt często. W typowych klasach pisanie kodu jest dla ciebie świetne. Na przykład,
catProcs :: (i -> Maybe String) -> (i -> Maybe String) -> (i -> Maybe String)
catProcs f g = f <> g
który pobiera dwa „procesory”, które pobierają dane wejściowe i mogą dawać dane wyjściowe, i konkatenuje je, spłaszczając Nothing
, musiałyby być zapisane w DPS mniej więcej tak:
catProcs f g = (<>) (funcSemi (maybeSemi listSemi)) f g
Zasadniczo musieliśmy przeliterować typ, w którym go ponownie używamy, nawet jeśli już przeliterowaliśmy go w podpisie typu, a nawet to było zbędne, ponieważ kompilator już zna wszystkie typy. Ponieważ istnieje tylko jeden sposób skonstruowania danego Semigroup
typu, kompilator może to zrobić za Ciebie. Ma to efekt typu „odsetki złożone”, gdy zaczynasz definiować wiele instancji parametrycznych i używasz struktury swoich typów do obliczania dla ciebie, jak w Data.Functor.*
kombinatorach, i jest to używane z doskonałym skutkiem deriving via
, gdy można uzyskać zasadniczo wszystkie „standardowa” struktura algebraiczna twojego typu napisana dla Ciebie.
I nawet nie zaczynaj mnie od MPTC i fundeps, które dostarczają informacje z powrotem do sprawdzania typów i wnioskowania. Nigdy nie próbowałem przekonwertować czegoś takiego na DPS - podejrzewam, że wymagałoby to przekazania wielu dowodów równości typu - ale w każdym razie jestem pewien, że dla mojego mózgu byłoby to o wiele więcej pracy niż byłoby mi wygodnie z.
-
1 U nless użyć reflection
w tym przypadku stają się one równoważne w siłę - ale reflection
może być również kłopotliwe w użyciu.