A Definition Typ
foo :: C a => a
verspricht einen Wert von jede Typ a
, so lange zu liefern, wie es in Klasse ist C
. Der Benutzer von foo
erhält a
, foo
selbst wählen kann nicht einen bestimmten Typ auswählen.
Mit Ihrer Einstellung, die nur möglich ist (nicht-Fehler) Definition ist
foo = anyC
Wenn Sie D "hello"
zurückkehren wollen, dann ist dies vom Typ D
. Sie können diese Art verwenden, wenn Sie wollen:
bar :: D
bar = D "hello"
dass Beachten Sie auch, wenn Sie möchten, können Sie Ihre Instanz ändern auch:
instance C D where
anyC = D "hello"
In Bezug auf
In Modernste Sprachen ist es einfach.
Dies ist eigentlich nicht der Fall. In Java könnte zum Beispiel ein vages Äquivalent sein:
Java wird das auch nicht akzeptieren. Andere Probleme beiseite, foo()
ist vielversprechend, alle vom Anrufer A
zurückgegeben, kann nicht wählen, um eine D
zurückgeben.
Haskell-Typ-Variablen entsprechen sehr grob den generischen Java-Typparametern. Sie könnten stattdessen über das Subtyping nachdenken, das in Java und anderen OOP-Sprachen vorhanden ist, aber nicht von Haskell verwendet wird.
Wenn Sie ein fromConstrB
Beispiel möchten, können Sie dieses versuchen:
{-# LANGUAGE DeriveDataTypeable, ScopedTypeVariables, TypeOperators, GADTs #-}
module FromConstr where
import Data.Data
data D = D String | AnyD deriving (Show, Data)
foo :: D
foo = fromConstrB field ctor
where
ctor :: Constr
ctor = toConstr (D "aaa")
field :: forall a. Data a => a
field = case eqT :: Maybe (a :~: String) of
Just Refl -> "new string"
Nothing -> error "trying to fill a non-string field"
foo
ausgewertet D "new string"
.Dies kann erweitert werden, so dass field
mehr Typen umfasst, wenn der Konstruktor mehr benötigt, indem Sie case eqT
s verschachteln, so dass wir nach allen benötigten Typen suchen.
Beachten Sie auch, dass fromConstrB
ist begrenzt, da wenn unser Konstruktor zwei Felder mit dem gleichen Typ hat, können wir die Felder nicht mit unterschiedlichen Werten füllen. Dafür müssten wir auf die komplexeren fromConstrM
zurückgreifen, denke ich.
Hier ist ein wenig Komfort Hilfsfunktion. Es dauert eine Constr
, und eine Liste von "untypisierten" Argumente (Dynamic
macht alle Typprüfung zur Laufzeit) und versucht, einen Wert zu erstellen, der den Konstruktor auf die gegebenen Argumente anwendet.
applyConstr :: Data a => Constr -> [Dynamic] -> Maybe a
applyConstr ctor args = let
nextField :: forall d. Data d => StateT [Dynamic] Maybe d
nextField = do
as <- get
case as of
[] -> lift Nothing -- too few arguments
(a:rest) -> do
put rest
case fromDynamic a of
Nothing -> lift Nothing -- runtime type mismatch
Just x -> return x
in case runStateT (fromConstrM nextField ctor) args of
Just (x, []) -> Just x
_ -> Nothing -- runtime type error or too few/too many arguments
Zum Beispiel können Sie es wie folgt:
bar :: D
bar = case applyConstr (toConstr (D "aaa")) [toDyn "hello"] of
Just x -> x
Nothing -> error "runtime type mismatch"
Wenn der Konstruktor mehr Argumente hat, können Sie nur die Liste länger machen müssen, wie in [toDyn "string", toDyn (42::Int), toDyn True]
. Die Funktion toDyn
konvertiert typisierte Werte in "nicht typisierte", so dass sie zusammen in derselben Liste gespeichert und an applyConstr
übergeben werden können. Später wird applyConstr
(zur Laufzeit) testen, ob diese Liste die genaue Länge hat und Werte der richtigen Typen hat.
Meine Frage ist, wie man 'fromConstrB' mit einfachen (eingebauten) Typen, nicht über Typklassen und Instanzen. So kann ich in den meisten modernen Sprachen: a) alle Konstruktoren bekommen b) Konstruktorargumente erhalten c) typen/Standardwerte für sie erhalten d) sogar Aufrufkonstruktor. Ich sehe ein Beispiel, wie man es in diesen Tutorials macht, aber anscheinend liegt das Tutorial darin. Also, Frage ist: Wie konstruiere ich den Wert von Data-like type, wenn ich den benötigten Konstruktor schon gefunden habe? Beispiel zeigt nur das Ziel. Sicher, es hat ein Problem: Ich muss überprüfen, dass der gefundene Konstruktor nur 1 arg hat und es ist String –
@ Paul-AG Siehe meine Bearbeitung, wie man 'fromConstrB' und einige verwandte Funktionen verwendet. – chi
'nextField = StateT uncons >> = heben. fromDynamic' – Gurkenglas