Ja, Sie können GHC davon überzeugen, dass Sie sich nicht für den genauen Typ interessieren, mit dem parametriert ist. Nur, es ist eine schreckliche Idee.
{-# LANGUAGE GADTs #-}
import Unsafe.Coerce
data D a = D a deriving (Show)
data Wrap where -- this GADT is equivalent to your `ExistentialQuantification` version
Wrap :: D a -> Wrap
unwrap :: Wrap -> D a
unwrap (Wrap (D a)) = D (unsafeCoerce a)
main = print (unwrap (Wrap $ D "bla") :: D Integer)
Dies ist, was passiert, wenn ich das einfache Programm ausführen:
![enter image description here](https://i.stack.imgur.com/t5fzJ.png)
und so weiter, bis das System senkt den Speicherverbrauch.
Typen sind wichtig! Wenn Sie das Typsystem umgehen, umgehen Sie die Vorhersagbarkeit Ihres Programms (d. H. Es kann alles passieren, einschließlich thermonuclear war oder das berühmte demons flying out of your nose).
Nun, offenbar dachten Sie, dass Typen irgendwie anders arbeiten. In dynamischen Sprachen wie Python und auch in OO-Sprachen wie Java ist ein Typ in gewisser Weise eine Eigenschaft, die ein Wert haben kann. Die (Referenz-) Werte enthalten also nicht nur die Informationen, die zur Unterscheidung verschiedener Werte eines einzelnen Typs benötigt werden, sondern auch Informationen zur Unterscheidung verschiedener (Teil-) Typen. Das ist in vieler Hinsicht ziemlich ineffizient – es ist ein Hauptgrund, warum Python so langsam ist und Java solch eine riesige VM braucht.
In Haskell existieren die Typen nicht zur Laufzeit. Eine Funktion weiß nie, mit welchem Typ die Werte arbeiten. Nur weil der Compiler alles über die Typen weiß, die es haben wird, hat die Funktion irgendein solches Wissen – nicht, den der Compiler bereits hart-codiert hat! (Das heißt, wenn Sie es mit unsafeCoerce
, umgehen, die, wie ich gezeigt, wie unsicher, wie es klingt.)
Wenn Sie nicht möchten, die Art als “ Eigenschaft befestigen ” auf einen Wert, Sie müssen es tun ausdrücklich und darum sind diese existentiellen Wrapper da. Es gibt jedoch normalerweise bessere Möglichkeiten, dies in einer funktionalen Sprache zu tun. Für was ist die Anwendung, für die Sie das haben wollten?
Vielleicht ist es auch hilfreich, sich daran zu erinnern, was eine Signatur mit polymorphem Ergebnis bedeutet. unwrap :: Wrap -> D a
bedeutet nicht “ das Ergebnis ist einige D a
... und der Anrufer besser kümmern sich nicht um die a
verwendet ”. Das wäre in Java der Fall, aber in Haskell wäre es ziemlich nutzlos, weil man mit einem Wert unbekannten Typs nichts machen kann.
Stattdessen heißt es: für was auch immer Typen a
Des Anrufer dieser Funktion der Lage ist, einen geeigneten D a
Wert zu liefern. Natürlich ist dies schwer zu liefern – ohne zusätzliche Informationen ist es genauso unmöglich wie etwas mit einem Wert von unbekannten Typ zu tun. Aber wenn es bereits a
Werte in den Argumenten oder a
ist irgendwie auf eine Typklasse beschränkt (zB fromInteger :: Num a => Integer -> a
, dann ist es durchaus möglich, und sehr nützlich.
Um ein String
Feld – unabhängig von der zu erhalten a
Parameter – Sie können direkt auf dem umwickelten Wert arbeiten:
data D a = D
{ dLabel :: String
, dValue :: a
}
data Wrap where Wrap :: D a -> Wrap
labelFromWrap :: Wrap -> String
labelFromWrap (Wrap (D l _)) = l
Um solche Funktionen auf Wrap
zu schreiben mehr Allgemein (mit jedem “ Label-Accessor, der a
” nicht interessiert), verwende Rank2-Polymorphismus, wie in der Antwort von N.M.
Ich denke, der Grund, warum 'existiert a. D a' ist kein Teil von Haskell ist ein einfacher Wunsch, Redundanz zu vermeiden. Dieser Typ entspricht "Forall r". (forall a. D a -> r) -> r ', also kann man das auch nutzen, um existenzielle Werte darzustellen. –
@ n.m. streng genommen ist es nicht gleichwertig, ich stimme zu, dass das einzig Nützliche, was einem solchen existenziellen Wert angetan werden kann, die Anwendung einer Funktion "Forall a" ist. D a -> r ', so macht die Unfähigkeit, diese Ad-hoc-Existenz auszudrücken, einen Sinn - danke. –