Was ist der Sinn von 'const' im Haskell-Präludium?

91

Wenn ich durch das Haskell-Präludium schaue, sehe ich eine Funktionconst :

const x _ = x

Ich kann anscheinend nichts Relevantes zu dieser Funktion finden.

Was ist der Punkt? Kann jemand ein Beispiel geben, wo diese Funktion verwendet werden könnte?

Stusmith
quelle
10
Ein Beispiel: backgroundColor :: Text -> Colorist für michbackgroundColor = const White
Zhen

Antworten:

83

Dies ist nützlich, um Funktionen höherer Ordnung zu übergeben, wenn Sie nicht die gesamte Flexibilität benötigen. Beispielsweise kann der monadische Sequenzoperator >>in Bezug auf den monadischen Bindungsoperator als definiert werden

x >> y = x >>= const y

Es ist etwas ordentlicher als ein Lambda

x >> y = x >>= \_ -> y

und Sie können es sogar punktfrei verwenden

(>>) = (. const) . (>>=)

obwohl ich das in diesem Fall nicht besonders empfehle.

Hammar
quelle
9
+1. Es kommt auch häufig vor, wenn Parser-Kombinatoren verwendet werden.
Fred Foo
47
Ahh, es ist eher ein 'Funktionsgenerator' - ich benutze es mit einem Argument und es gibt mir eine Funktion (mit einem Argument), die immer einen konstanten Wert zurückgibt. So map (const 42) [1..5]ergibt sich [42, 42, 42, 42, 42].
Stusmith
2
Stusmith: Du hast es verstanden. constist nützlich, um auf ein einzelnes Argument anzuwenden, um eine Funktion zu erhalten, bei der eine benötigt wird (z. B. Übergabe an map).
Conal
8
@ Stusmith: Sie können es auf einige interessante Arten verwenden:head = foldr const (error "Prelude.head: empty list")
Rampion
27

Um die ausgezeichnete direkte Antwort von Hammar zu ergänzen: Demütige Funktionen mögen constund idsind als Funktion höherer Ordnung aus demselben Grund wirklich nützlich, aus dem sie in der SKI-Kombinatorrechnung von grundlegender Bedeutung sind .

Nicht, dass ich denke, dass Haskells Vorspielfunktionen bewusst diesem formalen System oder irgendetwas nachempfunden waren. Es ist nur so, dass das Erstellen umfangreicher Abstraktionen in Haskell sehr einfach ist. Daher sehen Sie diese Art von theoretischen Dingen oft als praktisch nützlich an.

Schamloser Plug, aber ich habe darüber gebloggt, wie die Applicative-Instanz (->)tatsächlich die Sund KKombinatoren hier sind , wenn Sie so etwas mögen.

jberryman
quelle
8
Nun, die SKI-Kombinatoren haben das Prelude sicherlich beeinflusst. Ich erinnere mich, wie ich mit Joe Fasel darüber gestritten habe, ob der S-Kombinator enthalten sein sollte oder nicht.
August
4
Übrigens ((->) e)ist auch die Lesermonade - mit Readerund dergleichen nur newtypeWrapper - und die askFunktion ist dann id, also ist das auch der IKombinator. Wenn Sie sich stattdessen die ursprüngliche BCKW-Basis von Haskell Curry ansehen B,K und Wsind fmap, returnund joinjeweils.
CA McCann
1
Der Blog-Link in der Antwort ist tot. Es sollte jetzt hier zeigen: brandon.si/code/…
nsxt
22

Ein einfaches Beispiel für die Verwendung constist Data.Functor.(<$). Mit dieser Funktion kann man sagen: Ich habe hier einen Funktor mit etwas Langweiligem, aber stattdessen möchte ich das andere interessante Ding darin haben, ohne die Form des Funktors zu ändern. Z.B

import Data.Functor

42 <$ Just "boring"
--> Just 42

42 <$ Nothing
--> Nothing

"cool" <$ ["nonsense","stupid","uninteresting"]
--> ["cool","cool","cool"]

Die Definition lautet:

(<$) :: a -> f b -> f a
(<$) =  fmap . const

oder nicht so sinnlos geschrieben:

cool <$ uncool =  fmap (const cool) uncool

Sie sehen, wie consthier verwendet wird, um die Eingabe zu "vergessen".

Landei
quelle
21

Ich kann anscheinend nichts Relevantes zu dieser Funktion finden.

Viele der anderen Antworten diskutieren relativ esoterische (zumindest für den Neuling) Anwendungen von const. Hier ist eine einfache: Sie können verwendenconst ein Lambda loswerden, das zwei Argumente akzeptiert, das erste wegwirft, aber mit dem zweiten etwas Interessantes macht.

Zum Beispiel die folgende (ineffiziente, aber lehrreiche) Implementierung von length:

length' = foldr (\_ acc -> 1 + acc) 0

kann umgeschrieben werden als

length' = foldr (const (1+)) 0

das ist vielleicht eleganter.

Der Ausdruck const (1+)ist in der Tat semantisch äquivalent zu \_ acc -> 1 + acc, da er ein Argument nimmt, es wegwirft und den Abschnitt zurückgibt (1+).

jub0bs
quelle
4
Ich habe 5 Minuten gebraucht, um zu verstehen, wie das funktioniert :)
Mukesh Soni
15

Eine andere Verwendung besteht darin, Klassenelementfunktionen zu implementieren, die ein Dummy-Argument haben, das nicht ausgewertet werden sollte (zum Auflösen mehrdeutiger Typen). Beispiel, das in Data.bits sein könnte:

instance Bits Int where
  isSigned = const True
  bitSize  = const wordSize
  ...

Mit const sagen wir ausdrücklich, dass wir konstante Werte definieren.

Persönlich mag ich die Verwendung von Dummy-Parametern nicht, aber wenn sie in einer Klasse verwendet werden, ist dies eine ziemlich gute Art, Instanzen zu schreiben.

Jonas Duregård
quelle
Proxy-Argumente sind in der Tat viel besser, und wenn Sie auf aktuelle GHC abzielen, machen Typanwendungen den Trick ordentlich.
Feuer
2

constMöglicherweise ist dies nur die Implementierung, nach der Sie in Verbindung mit anderen Funktionen suchen. Hier ist ein Beispiel, das ich entdeckt habe.

Angenommen, wir möchten eine Struktur von 2-Tupeln in eine andere Struktur von 2-Tupeln umschreiben. Ich könnte dies so ausdrücken:

((a,b),(c,d))  (a,(c,(5,a)))

Ich kann eine einfache Definition mit Mustervergleich geben:

f ((a,b),(c,d)) = (a,(c,(5,a)))

Was ist, wenn ich eine sinnlose (stillschweigende) Lösung für diese Art von Umschreibungen möchte? Einige denken und fummeln später, die Antwort ist, dass wir alle Umschreibungen mit ausdrücken können (&&&), const, (.), fst, snd. Beachten Sie, dass von (&&&)istControl.Arrow .

Die Lösung des Beispiels mit diesen Funktionen lautet:

(fst.fst &&& (fst.snd &&& (const 5 &&& fst.fst)))

Beachten Sie die Ähnlichkeit mit (a,(c,(5,a))). Was passiert , wenn wir ersetzen &&&mit ,? Dann heißt es:

(fst.fst, (fst.snd, (const 5, fst.fst)))

Beachten Sie, wie adas erste Element des ersten Elements ist und was fst.fstProjekte sind. Beachten Sie, wie cdas erste Element des zweiten Elements ist und was fst.sndProjekte sind. Das heißt, Variablen werden zum Pfad zu ihrer Quelle.

consterlaubt uns, Konstanten einzuführen. Interessant, wie der Name mit der Bedeutung übereinstimmt!

Ich habe dann diese Idee mit Applicative verallgemeinert , so dass Sie jede Funktion in einem sinnlosen Stil schreiben kann (so lange , wie Sie als Funktionen Fallanalyse zur Verfügung haben, wie maybe, either, bool). Wieder constspielt die Rolle der Einführung von Konstanten. Sie können diese Arbeit im Data.Function.Tacit- Paket sehen.

Wenn Sie abstrakt am Ziel beginnen und dann auf eine Implementierung hinarbeiten, können Sie von den Antworten überrascht sein. Das heißt, jede Funktion kann so mysteriös sein wie jedes Zahnrad in einer Maschine. Wenn Sie sich jedoch zurückziehen, um die gesamte Maschine sichtbar zu machen, können Sie den Kontext verstehen, in dem dieses Zahnrad erforderlich ist.

erisco
quelle
2

Angenommen, Sie möchten eine Liste erstellen Nothings, die der Länge einer Zeichenfolge entspricht. Da constdas erste Argument zurückgegeben wird, können Sie unabhängig vom zweiten Folgendes tun:

listOfNothings :: String -> [Maybe Char]
listOfNothings = (map . const) Nothing

oder genauer gesagt:

listOfNothing st = map (const Nothing) st
A. Saramet
quelle
0

Angenommen, Sie möchten eine Liste drehen. Dies ist eine idiomatische Methode in Haskell:

rotate :: Int -> [a] -> [a] rotate _ [] = [] rotate n xs = zipWith const (drop n (cycle xs)) xs

Diese Funktion komprimiert zwei Arrays mit der Funktion const, wobei das erste ein unendliches zyklisches Array und das zweite das Array ist, mit dem Sie begonnen haben.

const fungiert als Grenzüberprüfung und verwendet das ursprüngliche Array, um das zyklische Array zu beenden.

Siehe: Eine Liste in Haskell drehen

Berühmte Jameis
quelle
0

Ich kann anscheinend nichts Relevantes zu dieser Funktion finden.

Angenommen, Sie möchten alle Teilsequenzen einer bestimmten Liste generieren.

Für jedes Listenelement haben Sie an einem bestimmten Punkt die Wahl zwischen True (in die aktuelle Teilsequenz aufnehmen) oder False (nicht einschließen). Dies kann mit der Funktion filterM erfolgen .

So was:

 λ> import Control.Monad
 λ> :t filterM
 filterM :: Applicative m => (a -> m Bool) -> [a] -> m [a]
 λ> 

Zum Beispiel wollen wir alle Teilsequenzen von [1..4].

 λ> filterM  (const [True, False])  [1..4]
 [[1,2,3,4],[1,2,3],[1,2,4],[1,2],[1,3,4],[1,3],[1,4],[1],[2,3,4],[2,3],[2,4],[2],[3,4],[3],[4],[]]
 λ> 
jpmarinier
quelle