Ich denke, das ist die am weitesten in die Haskell-Typ Systemerweiterungen, die ich schon gewesen bin und ich habe einen Fehler, die ich nicht in der Lage war herausfinden. Ich entschuldige mich im Voraus für die Länge, es ist das kürzeste Beispiel, das ich erstellen konnte, das immer noch das Problem veranschaulicht, das ich habe. Ich habe eine rekursive GADT deren Art ist eine Liste gefördert, etwa wie folgt:Wie man eine rekursive GADT mit Art :: '[SomeDataKind] verarbeitet
GADT Definition
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
data DataKind = A | B | C
-- 'parts' should always contain at least 1 element which is enforced by the GADT.
-- Lets call the first piece of data 'val' and the second 'subdata'.
-- All data constructors have these 2 fields, although some may have
-- additional fields which I've omitted for simplicity.
data SomeData (parts :: [DataKind]) where
MkA :: Maybe Char -> Maybe (SomeData subparts) -> SomeData ('A ': subparts)
MkB :: Maybe Char -> Maybe (SomeData subparts) -> SomeData ('B ': subparts)
MkC :: Maybe Char -> Maybe (SomeData subparts) -> SomeData ('C ': subparts)
deriving instance Show (SomeData parts)
Problem
Was ich versuche, die Daten zu tun durchlaufen ist und führe einige Operationen aus, zum Beispiel propagiere die erste Just
Char
, die bis oben gefunden wurde.
Annoying fehlende Features - erforderlich für den nächsten Abschnitt
Jetzt, da es für GADTs offenbar keine Aufzeichnung Syntax-Unterstützung ist (https://ghc.haskell.org/trac/ghc/ticket/2595) Sie müssen sie manuell schreiben, also hier sind sie:
getVal :: SomeData parts -> Maybe Char
getVal (MkA val _) = val
getVal (MkB val _) = val
getVal (MkC val _) = val
-- The lack of record syntax for GADTs is annoying.
updateVal :: Maybe Char -> SomeData parts -> SomeData parts
updateVal val (MkA _val sub) = MkA val sub
updateVal val (MkB _val sub) = MkB val sub
updateVal val (MkC _val sub) = MkC val sub
-- really annoying...
getSubData :: SomeData (p ': rest) -> Maybe (SomeData rest)
getSubData (MkA _ sub) = sub
getSubData (MkB _ sub) = sub
getSubData (MkC _ sub) = sub
Testdaten
Also, die Sache will ich tun. Gehe die Struktur von oben nach unten, bis ich einen Wert gefunden habe, der Just
ist. So gegeben folgende Anfangswerte:
a :: SomeData '[ 'A ]
a = MkA (Just 'A') Nothing
b :: SomeData '[ 'B ]
b = MkB (Just 'B') Nothing
c :: SomeData '[ 'C ]
c = MkC (Just 'C') Nothing
bc :: SomeData '[ 'B, 'C ]
bc = MkB Nothing (Just c)
abc :: SomeData '[ 'A, 'B, 'C ]
abc = MkA Nothing (Just bc)
Erwartetes Ergebnis
Ich mag so etwas wie dieses haben würde:
> abc
MkA Nothing (Just (MkB Nothing (Just (MkC (Just 'C') Nothing))))
> propogate abc
MkA (Just 'C') (Just (MkB (Just 'C') (Just (MkC (Just 'C') Nothing))))
Frühere Versuche
nahm ich ein paar Stichen bei ihm zuerst mit einer regulären Funktion:
propogate sd =
case getVal sd of
Just _val ->
sd
Nothing ->
let
newSubData = fmap propogate (getSubData sd)
newVal = join . fmap getVal $ newSubData
in
updateVal newVal sd
Dies gibt den Fehler:
Broken.hs:(70,1)-(81,35): error: …
• Occurs check: cannot construct the infinite type: rest ~ p : rest
Expected type: SomeData rest -> SomeData (p : rest)
Actual type: SomeData (p : rest) -> SomeData (p : rest)
• Relevant bindings include
propogate :: SomeData rest -> SomeData (p : rest)
(bound at Broken.hs:70:1)
Compilation failed.
Ich habe auch versucht ein typeclass und versucht, auf die Struktur anzupassen:
class Propogate sd where
propogateTypeClass :: sd -> sd
-- Base case: We only have 1 element in the promoted type list.
instance Propogate (SomeData parts) where
propogateTypeClass sd = sd
-- Recursie case: More than 1 element in the promoted type list.
instance Propogate (SomeData (p ': parts)) where
propogateTypeClass sd =
case getVal sd of
Just _val ->
sd
Nothing ->
let
-- newSubData :: Maybe subparts
-- Recurse on the subdata if it exists.
newSubData = fmap propogateTypeClass (getSubData sd)
newVal = join . fmap getVal $ newSubData
in
updateVal newVal sd
Diese im Fehler führt:
Broken.hs:125:5-26: error: …
• Overlapping instances for Propogate (SomeData '['A, 'B, 'C])
arising from a use of ‘propogateTypeClass’
Matching instances:
instance Propogate (SomeData parts)
-- Defined at Broken.hs:91:10
instance Propogate (SomeData (p : parts))
-- Defined at Broken.hs:95:10
• In the expression: propogateTypeClass abc
In an equation for ‘x’: x = propogateTypeClass abc
Compilation failed.
Ich habe auch Kombinationen von Matching aufversuchtund SomeData '[p]
vergeblich.
Ich hoffe, ich vermisse etwas Einfaches, aber ich habe keine Dokumentation nirgendwo gefunden, wie man eine Struktur wie folgt verarbeitet und ich bin an der Grenze meines Verständnisses, wenn das Haskell-System, für jetzt sowieso:).Noch einmal, Entschuldigung für die lange Post und jede Hilfe würde sehr geschätzt werden :)
Wow, danke für die unglaublich gründliche Antwort. Das macht Sinn und ist so viel eleganter, vor allem die 'DataPart'-Portion. Eine Frage, die ich über DataPart habe, ist, wie man Daten für die anderen Typen hinzufügt. Ändern Sie es in 'A_Data :: ((x ==? 'A) ~' True)' mit ähnlichen für die anderen funktioniert nicht. –
Ah, ich habe es herausgefunden. 'A_Data :: ((x ==? 'A) ~' True) => Ganze Zahl -> DataPart x' funktioniert, aber' ... => Ganze Zahl -> DataPart 'A' nicht. Ich bin mir nicht ganz sicher warum. –
Die Typvariable 'x' im Konstruktor' A_Data :: ((x ==? 'A) ~' True) => ... -> DataPart 'A' steht überhaupt nicht mit der Typvariablen im Index in Beziehung vom Typ - es funktioniert nur, weil Typvariablen implizit gebunden sind, aber in diesen beiden Fällen spielen die Typvariablen völlig unterschiedliche Rollen - im ersten Fall enthält der Konstruktor eine Einschränkung für einen Typparameter, im letzteren Fall einen existentiell quantifizierten Typ Variable (mit einer Einschränkung) sowie ein Beweis, dass der Parameter genau "A" ist - aber die existentiell quantifizierte Variable und der Parameter sind völlig unabhängig voneinander. – user2407038