Ich bin immer noch ein Anfänger, wenn es um Haskell Syntax und funktionale Programmiersprachen kommt, so wenn ich die Typdeklaration für Data.Function.on
betrachten, die on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
ist, meine Interpretation ist, dass es vier Parameter dauert: , (a -> b)
, a
, a
und gibt c
zurück. Wenn ich jedoch die Syntax für allgemeine Verwendung für Data.Function.on
anschaue, die (*) `on` f = \x y -> f x * f y
ist, nimmt sie nur zwei Funktionsparameter, nicht vier, ab. Wie also bezieht sich die Typensignatur auf die Verwendungssyntax?Understanding Data.Function.on Typ Unterschrift
Antwort
Der Haskell Pfeil für Funktionstypen verbirgt eine einfache, aber clevere Idee. Sie müssen an ->
als Operator denken, wie +
und -
, aber für Typen. Es nimmt zwei Arten als Argumente und gibt Ihnen einen neuen Typ, der aus einer Funktion besteht. So in
Int -> String
Sie haben die Typen Int
und String
, und Sie eine Funktion von einem Int in einen String zu bekommen.
Genau wie jeder andere Operator benötigen Sie eine Regel für eine Kette von ihnen. Wenn Sie an -
denken, was bedeutet das?
10 - 6 - 4
Bedeutet es (10 - 6) - 4 = 0
, oder es 10 - (6 - 4) = 8
bedeutet? Die Antwort ist die erste, weshalb wir sagen, dass -
"links assoziativ" ist.
Der ->
Operator ist richtig assoziativ, so
foo :: Int -> String -> String
bedeutet eigentlich
foo :: Int -> (String -> String)
Überlegen Sie, was das bedeutet. Das bedeutet, dass foo
keine 2 Argumente akzeptiert und ein Ergebnis vom Typ String
zurückgibt. Es benötigt 1 Argument (Int
) und gibt eine neue Funktion zurück, die das zweite Argument (String
) übernimmt und das letzte String
zurückgibt.
Funktion Anwendung funktioniert auf die gleiche Weise, außer dass assoziativ gelassen wird.So
foo 15 "wibble"
bedeutet eigentlich
(foo 15) "wibble"
foo
So wird 15
angewendet und gibt eine neue Funktion, die dann auf "wibble"
aufgebracht ist.
Dies führt zu einem netten Trick: anstatt alle Parameter beim Aufruf einer Funktion angeben zu müssen (wie Sie es in fast jeder anderen Programmiersprache tun), können Sie einfach die erste oder die ersten paar angeben Holen Sie sich eine neue Funktion zurück, die den Rest der Parameter erwartet.
Dies ist, was passiert mit on
. Ich werde eine konkretere Version verwenden, bei der 'f' durch 'length' ersetzt wird.
(*) on
Länge
Sie on
seine ersten beiden Parameter geben. Das Ergebnis ist eine neue Funktion, die die anderen beiden erwartet. Bei den Typen,
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
In diesem Fall (*)
hat Num n => n -> n -> n
Typ (I verschiedenen Buchstaben bin mit dieser weniger verwirrend machen), so dass mit dem Typ des ersten Arguments zu on
abgestimmt, zu dem Schluss führt, dass Wenn der Typ b
durch n
ersetzt wird, dann muss der Typ c
auch so sein und muss auch eine Num
Instanz sein. Daher muss length
einen numerischen Typ zurückgeben. Wie es der Fall ist der Typ length
ist [d] -> Int
, und Int
ist eine Instanz von Num
, so dass funktioniert. So am Ende dieses erhalten Sie:
(*) `on` length :: [d] -> [d] -> Int
meine Interpretation ist, dass es vier Parameter
Alle Haskell Funktionen übernehmen ein Argument. Einige von ihnen geben nur andere Funktionen zurück.
Der beste Weg, um die Signatur für on
zu betrachten, ist als eine Funktion höherer Ordnung: (b -> b -> c) -> (a -> b) -> (a -> a -> c)
. Dieses sagt "wenn Sie mir einen binären Operator geben, der b
s nimmt und c
gibt und eine Weise, b
s von a
s zu erhalten, gebe ich Ihnen einen binären Operator, der a
s nimmt und eine c
gibt". Sie können dies in der Definition sehen:
- 1. GHCi ignoriert Typ Unterschrift
- 2. Methode Unterschrift Haskell benutzerdefinierten Typ
- 3. Swift-Typ (von :) Funktion Unterschrift
- 4. Verständnis Elm-Typ Unterschrift Rückgabetypen
- 5. Erzwingen Typ Unterschrift für toString
- 6. Kontrolle dokumentiert Typ Unterschrift reexportiert Funktion
- 7. GHC nicht akzeptiert GADT Typ Unterschrift
- 8. Binding Name in Typ Unterschrift mit DataKind
- 9. Understanding Haskell
- 10. Unterschrift Mismatch auf observable.bindcallback
- 11. Typ Unterschrift für Funktion mit `runST` und Klassen
- 12. Typoskript - können einen Ausdruck, dessen Typ fehlende Unterschrift
- 13. Understanding Metaklassen in Python
- 14. Unterschrift Panel
- 15. Class.asSubclass Unterschrift
- 16. Understanding goroutines
- 17. Understanding Indirection
- 18. Understanding Mergesort
- 19. Understanding tf.global_variables_initializer
- 20. Understanding MsgWaitForMultipleObjects
- 21. Rekursion Understanding
- 22. Understanding Accounts.validateLoginAttempt
- 23. Understanding WebRequest
- 24. Understanding Deferred.pipe()
- 25. Understanding groupJoin
- 26. Understanding Elm Union Typen
- 27. Understanding BinaryObjects in ignite
- 28. Understanding RxJava basics
- 29. Understanding equals method
- 30. Understanding Downstream-Reduktion-Implementierung verstehen
Die '(->)' ist rechtsassoziativ, so '(b -> b -> c) -> (a -> b) -> a - > a -> c' ist gleichbedeutend mit '(b -> b -> c) -> (a -> b) -> (a -> a -> c)'. Beachten Sie die Klammern um das 'a -> a -> c 'am Ende. – 4castle
Dies entspricht "on (*) f x y = f x * f y". – chi