Ich lerne haskell
und bin auf diesen Ausdruck gestoßen, den ich nicht verstehen konnte.
Das Endergebnis ist 5, aber ich habe keine Ahnung, wie es bewertet wird.
Nach Definition von (.)
:
Nach Definition von const
:
Nach Definition von flip
:
Nach Definition von const
:
Nach Definition von flip
:
Was ist 5
.
(Können Sie als kleine Bonuseinsicht herausfinden, warum flip const y
nur id
für alle y
ist? Dies reduziert Ihren Ausdruck auf (id . id) 5
.)
Jedes Mal, wenn Sie einen "cleveren" Funktionsaufruf in Haskell sehen, überprüfen Sie immer seine Typen und versuchen Sie dann, mit ihm in GHCi zu spielen.
Die einzige Möglichkeit, Haskell zu lernen, ist, es auszuprobieren, und Sie können es nicht einfach durch das Lesen von StackOverflow-Antworten oder Online-Tutorials tun. Sie müssen Ausdrücke in GHCi schreiben, kläglich scheitern und versuchen, den Fehler selbst herauszufinden Versuchen Sie, verschiedene Werte an verschiedenen Stellen zu ersetzen, damit der Fehler verschwindet, versuchen Sie, das Problem in Abschnitte zu unterteilen und sie getrennt zu verstehen, und so weiter.
Es ist unmöglich, Haskell zu codieren, ohne sein Typsystem zu verstehen (was ich verspreche, ist nicht schwer). Wenn Sie nicht verstehen, was zum Beispiel eine "type variable" bedeutet, lesen Sie das Kapitel Typen und Typklassen , und stellen Sie sicher, dass Sie jeden Ausdruck in GHCi sorgfältig ausprobieren.
Dein ursprünglicher Ausdruck ist:
%Vor% (.)
ist function composition , und wenn der Ausdruck typechecks ist (dh es wird kein Typfehler für die Kompilierung ausgegeben ), dann müssen flip const 1
und const flip 3 const 4
Funktionen sein.
Übung: Öffnen Sie nun GHCi und geben Sie eins nach dem anderen ein und prüfen Sie die Typen (
%Vor%:t
GHCi Kommando ist, einen Typ eines Wertes zu überprüfen):
Erinnern Sie sich zuerst an diese Tatsache über Haskells Typsystem. Wenn in einer Typdeklaration eine Typvariable wie a
vorhanden ist, muss die Funktion einen beliebigen Typ akzeptieren. Wenn es eine Typvariable gibt, die durch eine Typklasse begrenzt ist, z. f :: Num a => a -> a
, dann muss die Funktion einen beliebigen Typ akzeptieren, dh Instanz der Typklasse Num
(d. h. eine beliebige Zahl). Sie können keine Funktion haben, die a
zurückgibt, aber in deren Körper Sie eine String
zurückgeben.
Zweitens, wenn es in der Typdeklaration mehr als 1 Typvariablen gibt, ist ihr Typ immer gleich. Wenn also eine Funktion den Typ f :: a -> a
hat und Sie ihr ein Int
gegeben haben, muss Int
zurückgegeben werden. Da Haskell keine Überprüfung des Laufzeittyps hat, können Sie von den Typen nichts annehmen, wenn Sie eine Typvariable akzeptieren.
Diese 2 Fakten beschränken, was eine Funktion tun kann, wenn sie einen bestimmten Typ hat. Also:
f :: a -> a
gibt immer das Argument zurück; es kann keinen Grund für das Argument haben, weil es seinen Typ nicht kennt, es kann keine Funktionen mit spezifischeren Typen anwenden, also muss es nur das Argument f :: (a, b) -> a
ist fst
und nichts anderes, es weiß nichts über das erste Element eines Paares, also muss es es einfach zurückgeben f :: (a, b) -> b
ist snd
, aus dem gleichen Grund f :: a -> b -> a
benötigt 2 Argumente und gibt das erste f :: b -> a -> a
benötigt 2 Argumente und gibt das zweite Diese Einschränkung ist eine gute Sache, denn nur wenn man sich den Typ ansieht, kann man viel über die Funktion erraten. Einige Typen sind einfach unmöglich, zum Beispiel f :: a -> b
, es gibt keine Funktion eines solchen Typs (denn was wäre b
überhaupt?).
Denken Sie daran, ->
in der Typdeklaration ist rechtsassoziativ , also:
sind alle gleichwertig. Sie können also eine Funktion vom Typ f :: a -> b -> c
so behandeln, dass sie 2 Argumente annimmt und einen Wert zurückgibt, oder Sie können sie als 1 Argument behandeln und eine Funktion von 1 Argument zurückgeben.
"Warte", könnten Sie sagen, "Sie haben gesagt, dass eine Funktion vom Typ a -> b
unmöglich ist. Wie kann f
vom Typ (a -> b -> c) -> b -> (a -> c)
sein, also eine Funktion vom Typ a -> c
?" Nun, f
gibt tatsächlich niemals eine Funktion vom Typ a -> c
zurück, weil Sie niemals eine Funktion vom Typ a -> b -> c
daran übergeben. Stattdessen könnten Sie eine Funktion vom Typ Int -> a -> [a]
übergeben und eine Funktion vom Typ a -> (Int -> [a])
erhalten.
Dann übergeben Sie einen anderen Wert, sagen wir vom Typ String
, an diese neue Funktion. Sie erhalten als Ergebnis eine Funktion vom Typ Int -> [String]
. Jetzt ist Int -> [String]
keine unmögliche Funktion, oder?
Technisch gesehen können Sie eine Funktion vom Typ a -> b
zurückgeben, die undefined
heißt. undefined
ist ein Wert vom Typ a
, dh, er ist in den beliebigen -Typ eingebettet. Aber das ist ein anderes Thema.
Übung: Lassen Sie uns weiter mit den Typen in GHCi spielen:
%Vor%
Was haben Sie von den Typen gelernt? Welche Argumente werden diese Funktionen ignorieren? Welche Werte (oder Funktionen) werden sie zurückgeben?
Übung: Wenn Sie immer noch nicht verstehen, wohin ich gehe, versuchen Sie, diese in GHCi in dieser Reihenfolge einzugeben, um zu sehen, wie sich die Anwendung teilweise ändert:
%Vor%
Tags und Links haskell