Wydaje mi się, że zawsze można przekazywać argumenty funkcji, a nie klasę. Na przykład zamiast definiowania klasy równości:
class Eq a where
(==) :: a -> a -> Bool
Używanie go w innych funkcjach do wskazywania argumentu typu musi być instancją Eq
:
elem :: (Eq a) => a -> [a] -> Bool
Czy nie możemy po prostu zdefiniować naszej elem
funkcji bez użycia klasy znaków i zamiast tego przekazać argument funkcji, który wykonuje zadanie?
Monad m
Ograniczenie mówi dla mnie więcej niż przekazując dodatkowe argumenty funkcji typówa -> m a
im a -> (a -> m b) -> m b
.TypeApplications
Rozszerzenie pozwala tworzyć niejawny argument, jednoznaczne.(==) @Int 3 5
porównuje,3
a5
konkretnie jakoInt
wartości. Możesz myśleć o@Int
kluczu w słowniku funkcji równości specyficznych dla typu, a nie oInt
samej funkcji porównania specyficznej dla danego typu .Odpowiedzi:
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 jedenOrd
słownik dla każdego typu. WSet
sklepach jej elementy sortowane wedługOrd
, 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 wTypeable
interfejsie API.Bit wyjątkowości nie pojawia się zbyt często. W typowych klasach pisanie kodu jest dla ciebie świetne. Na przykład,
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: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 wData.Functor.*
kombinatorach, i jest to używane z doskonałym skutkiemderiving 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łę - alereflection
może być również kłopotliwe w użyciu.źródło
Tak. To (zwane przekazywaniem słownika) jest w zasadzie tym, co kompilator robi, by i tak pisać na typach klas. Dla tej funkcji, wykonanej dosłownie, wyglądałoby to trochę tak:
Połączenia
elemBy (==) x xs
są teraz równoważne zelem x xs
. W tym konkretnym przypadku możesz pójść o krok dalej:eq
za każdym razem ma ten sam pierwszy argument, więc możesz sprawić, że osoba dzwoniąca zastosuje to i skończy z tym:Połączenia
elemBy2 (x ==) xs
są teraz równoważne zelem x xs
....Zaczekaj. To jest po prostu
any
. (I w rzeczywistości w standardowej biblioteceelem = any . (==)
.)źródło
implicit
a kompilator wstrzyknie je dla ciebie z zakresu.