Die absurd
Funktion in Data.Void
hat die folgende Signatur, wobei Void
der logisch unbewohnte Typ von diesem Paket exportiert wird:
-- | Since 'Void' values logically don't exist, this witnesses the logical
-- reasoning tool of \"ex falso quodlibet\".
absurd :: Void -> a
Ich kenne genug Logik, um die Bemerkung der Dokumentation zu erhalten, dass dies durch die Entsprechung von Aussagen als Typen der gültigen Formel entspricht ⊥ → a
.
Was mich verwirrt und neugierig macht, ist: Bei welchen praktischen Programmierproblemen ist diese Funktion nützlich? Ich denke, dass es in einigen Fällen vielleicht nützlich ist, um Fälle, die "nicht passieren können", erschöpfend zu behandeln, aber ich weiß nicht genug über die praktische Verwendung von Curry-Howard, um festzustellen, ob diese Idee auf dem Spiel steht überhaupt der richtige Weg.
EDIT: Beispiele vorzugsweise in Haskell, aber wenn jemand eine abhängig getippte Sprache verwenden möchte, werde ich mich nicht beschweren ...
quelle
absurd
Funktion in diesem Artikel verwendet wurde, der sich mit derCont
Monade befasst: haskellforall.com/2012/12/the-continuation-monad.htmlabsurd
als eine Richtung des Isomorphismus zwischenVoid
und sehenforall a. a
.Antworten:
Das Leben ist ein bisschen hart, da Haskell nicht streng ist. Der allgemeine Anwendungsfall besteht darin, unmögliche Pfade zu behandeln. Beispielsweise
Dies erweist sich als etwas nützlich. Betrachten Sie einen einfachen Typ für
Pipes
Dies ist eine streng und vereinfachte Version des Standard-Rohrtyps aus der
Pipes
Bibliothek von Gabriel Gonzales . Jetzt können wir eine Pipe codieren, die niemals ergibt (dh einen Verbraucher) alsdas gibt wirklich nie nach. Dies impliziert, dass die richtige Faltregel für a
Consumer
istoder alternativ, dass Sie den Ertragsfall im Umgang mit Verbrauchern ignorieren können . Dies ist die allgemeine Version dieses Entwurfsmusters: Verwenden Sie polymorphe Datentypen und
Void
beseitigen Sie bei Bedarf die Möglichkeiten.Die wahrscheinlich klassischste Verwendung
Void
ist CPS.Das heißt, a
Continuation
ist eine Funktion, die niemals zurückkehrt.Continuation
ist die Typversion von "nicht". Daraus erhalten wir eine CPS-Monade (entsprechend der klassischen Logik)Da Haskell rein ist, können wir nichts aus diesem Typ herausholen.
quelle
Void
unbewohnt. In Haskell enthält es_|_
. In einer strengen Sprache kann ein Datenkonstruktor, der ein Argument vom TypVoid
verwendet, niemals angewendet werden, sodass die rechte Seite der Musterübereinstimmung nicht erreichbar ist. In Haskell müssen Sie a verwenden!
, um dies zu erzwingen, und GHC wird wahrscheinlich nicht bemerken, dass der Pfad nicht erreichbar ist._|_
? und leidet es dann unter der gleichen Einschränkung?Betrachten Sie diese Darstellung für Lambda-Begriffe, die durch ihre freien Variablen parametrisiert sind. (Siehe Artikel von Bellegarde und Hook 1994, Bird und Paterson 1999, Altenkirch und Reus 1999.)
Sie können dies sicherlich zu einem
Functor
Begriff machen , der den Begriff des Umbenennens undMonad
den Begriff der Substitution erfasst.Betrachten Sie nun die geschlossenen Begriffe: Dies sind die Einwohner von
Tm Void
. Sie sollten in der Lage sein, die geschlossenen Begriffe in Begriffe mit beliebigen freien Variablen einzubetten. Wie?Der Haken ist natürlich, dass diese Funktion den Begriff durchquert und genau nichts tut. Aber es ist ein Hauch ehrlicher als
unsafeCoerce
. Und deshalbvacuous
wurde hinzugefügt, umData.Void
...Oder schreiben Sie einen Bewerter. Hier sind Werte mit freien Variablen in
b
.Ich habe gerade Lambdas als Verschlüsse dargestellt. Der Evaluator wird durch eine Umgebung parametrisiert, in der freie Variablen
a
Werten zugeordnet werdenb
.Du hast es erraten. Um einen geschlossenen Begriff an einem beliebigen Ziel zu bewerten
Im Allgemeinen wird
Void
es nur selten alleine verwendet, ist jedoch praktisch, wenn Sie einen Typparameter auf eine Weise instanziieren möchten, die auf eine Art Unmöglichkeit hinweist (z. B. hier die Verwendung einer freien Variablen in einem geschlossenen Begriff). Oft sind diese parametrisierte Typen kommen mit Funktionen höherer Ordnung Operationen an den Parameter - Operationen auf der ganzen Art ( zum Beispiel hier, Hebenfmap
,>>=
,eval
). Sie geben alsoabsurd
als Allzweckoperation weiterVoid
.Stellen Sie sich als weiteres Beispiel vor,
Either e v
wie Sie Berechnungen erfassen möchten, die Ihnen hoffentlichv
eine Ausnahme vom Typ geben, aber möglicherweise auslösene
. Sie können diesen Ansatz verwenden, um das Risiko eines schlechten Verhaltens einheitlich zu dokumentieren. Für perfekt verhaltene Berechnungen in dieser Einstellung nehmen Siee
anVoid
und verwenden Sie siesicher laufen oder
sichere Komponenten in eine unsichere Welt einzubetten.
Oh, und ein letztes Hurra, mit einem "kann nicht passieren" umzugehen. Es zeigt sich in der generischen Reißverschlusskonstruktion überall dort, wo der Cursor nicht sein kann.
Ich habe beschlossen, den Rest nicht zu löschen, obwohl er nicht genau relevant ist.
Eigentlich ist es vielleicht relevant. Wenn Sie sich abenteuerlustig fühlen, zeigt dieser unvollendete Artikel , wie Sie
Void
die Darstellung von Begriffen mit freien Variablen komprimieren könnenin jeder Syntax, die frei von a
Differentiable
undTraversable
functor generiert wirdf
. Wir verwendenTerm f Void
, um Regionen ohne freie Variablen[D f (Term f Void)]
darzustellen , und um Röhren darzustellen , die durch Regionen ohne freie Variablen entweder zu einer isolierten freien Variablen oder zu einem Knotenpunkt in den Pfaden zu zwei oder mehr freien Variablen tunneln. Muss diesen Artikel irgendwann beenden.Für einen Typ ohne Werte (oder zumindest keinen, von dem es sich lohnt, in höflicher Gesellschaft zu sprechen)
Void
ist dies bemerkenswert nützlich. Undabsurd
so benutzt du es.quelle
forall f. vacuous f = unsafeCoerce f
eine gültige GHC-Umschreiberegel?Functor
Instanzen könnten GADTs sein , die nicht wirklich so etwas wie functors sind.Functor
nicht gegen diefmap id = id
Regel verstoßen? Oder meinst du das hier mit "Schwindel"?Das ist genau richtig.
Man könnte sagen, das
absurd
ist nicht nützlicher alsconst (error "Impossible")
. Es ist jedoch typbeschränkt, so dass seine einzige Eingabe ein Typ sein kannVoid
, ein Datentyp, der absichtlich unbewohnt bleibt. Dies bedeutet, dass es keinen tatsächlichen Wert gibt, an den Sie übergeben könnenabsurd
. Wenn Sie jemals in einem Codezweig landen, in dem die Typprüfung denkt, dass Sie Zugriff auf etwas Typisches habenVoid
, befinden Sie sich in einer absurden Situation. Sieabsurd
markieren also grundsätzlich, dass dieser Codezweig niemals erreicht werden sollte."Ex falso quodlibet" bedeutet wörtlich "aus [einem] falschen [Satz] folgt alles". Wenn Sie also feststellen, dass Sie ein Datenelement
Void
in der Hand haben, dessen Typ es ist , wissen Sie, dass Sie falsche Beweise in Ihren Händen haben. Sie können also jedes gewünschte Loch (viaabsurd
) füllen , da aus einem falschen Satz alles folgt.Ich habe einen Blog-Beitrag über die Ideen hinter Conduit geschrieben, der ein Beispiel für die Verwendung enthält
absurd
.http://unknownparallel.wordpress.com/2012/07/30/pipes-to-conduits-part-6-leftovers/#running-a-pipeline
quelle
Im Allgemeinen können Sie es verwenden, um scheinbar teilweise Musterübereinstimmungen zu vermeiden. Beispiel: Eine Annäherung der Datentypdeklarationen aus dieser Antwort :
Dann könnten Sie
absurd
zum Beispiel so verwenden:quelle
Es gibt verschiedene Möglichkeiten, den leeren Datentyp darzustellen . Einer ist ein leerer algebraischer Datentyp. Eine andere Möglichkeit besteht darin, es zu einem Alias für ∀α.α oder zu machen
in Haskell - so können wir es in System F codieren (siehe Kapitel 11 von Beweise und Typen ). Diese beiden Beschreibungen sind natürlich isomorph und der Isomorphismus wird nach
\x -> x :: (forall a.a) -> Void
und nach beobachtetabsurd :: Void -> a
.In einigen Fällen bevorzugen wir die explizite Variante, normalerweise wenn der leere Datentyp in einem Argument einer Funktion oder in einem komplexeren Datentyp wie in Data.Conduit vorkommt :
In einigen Fällen bevorzugen wir die polymorphe Variante, normalerweise ist der leere Datentyp am Rückgabetyp einer Funktion beteiligt.
absurd
entsteht, wenn wir zwischen diesen beiden Darstellungen konvertieren.Zum Beispiel
callcc :: ((a -> m b) -> m a) -> m a
verwendet (implizit)forall b
. Es könnte auch vom Typ sein((a -> m Void) -> m a) -> m a
, da ein Aufruf des Kontingents nicht tatsächlich zurückkehrt, sondern die Kontrolle auf einen anderen Punkt überträgt. Wenn wir mit Fortsetzungen arbeiten wollten, konnten wir definieren(Wir könnten verwenden,
type Continuation' r a = forall b . a -> Cont r b
aber das würde Rang 2-Typen erfordern.) Und dannvacuousM
konvertiert diesCont r Void
inCont r b
.(Beachten Sie auch, dass Sie haskellers.com verwenden können , um nach der Verwendung (umgekehrte Abhängigkeiten) eines bestimmten Pakets zu suchen, um zu sehen, wer und wie das void- Paket verwendet wird.)
quelle
TypeApplications
kann verwendet werden, um Details vonproof :: (forall a. a) -> Void
:proof fls = fls @Void
.In abhängig typisierten Sprachen wie Idris ist es wahrscheinlich nützlicher als in Haskell. Wenn Sie in einer Gesamtfunktion mit einem Wert übereinstimmen, der tatsächlich nicht in die Funktion verschoben werden kann, erstellen Sie normalerweise einen Wert vom Typ "unbewohnt" und verwenden ihn
absurd
zum Abschluss der Falldefinition.Diese Funktion entfernt beispielsweise ein Element aus einer Liste mit der dort vorhandenen Costraint auf Typebene:
Wo der zweite Fall besagt, dass eine leere Liste ein bestimmtes Element enthält, was durchaus absurd ist. Im Allgemeinen weiß der Compiler dies jedoch nicht und wir müssen oft explizit sein. Dann kann der Compiler überprüfen, ob die Funktionsdefinition nicht partiell ist, und wir erhalten stärkere Garantien für die Kompilierungszeit.
Aus Curry-Howard-Sicht, wo Aussagen sind, ist dann
absurd
eine Art QED ein Beweis durch Widerspruch.quelle