Frage

Ich habe diesen Code:

type family Id obj :: *
type instance Id Box = Int

Und ich möchte es so gestalten, dass ich immer ein Int aus der Id-Typfamilie erhalten kann.Mir ist bewusst, dass eine Umstellung erforderlich sein wird.

Ich dachte, vielleicht würde es funktionieren, eine Klasse zu erstellen:

class IdToInt a where
  idToInt :: Id a -> Int

instance IdToInt Box where
  idToInt s = s

Und das wird tatsächlich kompiliert.Aber wenn ich versuche, es zu verwenden:

testFunc :: Id a -> Int
testFunc x = idToInt x

Ich erhalte die Fehlermeldung:

src/Snowfall/Spatial.hs:29:22:
Couldn't match type `Id a0' with `Id a'
NB: `Id' is a type function, and may not be injective
In the first argument of `idToInt', namely `x'
In the expression: idToInt x
In an equation for `testFunc': testFunc x = idToInt x

Wie kann ich also eine Konvertierung für eine Typfamilien-ID erstellen, um einen Int zu erhalten?

Basierend auf der Antwort von ehird habe ich Folgendes versucht, aber es funktioniert auch nicht:

class IdStuff a where
  type Id a :: *
  idToInt :: Id a -> Int

instance IdStuff Box where
  type Id Box = Int
  idToInt s = s

testFunc :: (IdStuff a) => Id a -> Int
testFunc x = idToInt x

Es gibt einen Fehler:

src/Snowfall/Spatial.hs:45:22:
Could not deduce (Id a0 ~ Id a)
from the context (IdStuff a)
  bound by the type signature for
             testFunc :: IdStuff a => Id a -> Int
  at src/Snowfall/Spatial.hs:45:1-22
NB: `Id' is a type function, and may not be injective
In the first argument of `idToInt', namely `x'
In the expression: idToInt x
In an equation for `testFunc': testFunc x = idToInt x
War es hilfreich?

Lösung

Wie andere bereits betont haben, besteht das Problem darin, dass der Compiler nicht herausfinden kann, welche a benutzen.Datenfamilien sind eine Lösung, aber eine Alternative, mit der man manchmal einfacher arbeiten kann, ist die Verwendung eines Typzeugen.

Ändern Sie Ihre Klasse in

class IdToInt a where
  idToInt :: a -> Id a -> Int

instance IdToInt Box where
  idToInt _ s = s

-- if you use this a lot, it's sometimes useful to create type witnesses to use
box = undefined :: Box

-- you can use it like
idToInt box someId

-- or
idToInt someBox (getId someBox)

Die Frage, die Sie beantworten müssen, lautet: Für jeden Fall Id, gibt es nur einen Typ a es sollte mit erscheinen?Das heißt, gibt es eine Eins-zu-eins-Entsprechung zwischen? as und Id aS?Wenn ja, sind Datenfamilien der richtige Ansatz.Wenn nicht, bevorzugen Sie möglicherweise einen Zeugen.

Andere Tipps

Sie können nicht.Sie benötigen testFunc :: (IdToInt a) => Id a -> Int.Typfamilien sind offen, also kann jeder deklarieren

generasacodicetagpre.

jederzeit und geben Sie keine Konvertierungsfunktion an.Das Beste, was man tun soll, ist, die Typfamilie in die Klasse zu bringen:

generasacodicetagpre.

Sie benötigen jedoch noch den Kontext.

Sie können eine Funktion vom Typ nicht verwenden IdToInt a => Id a -> Int weil es keine Möglichkeit gibt, den Typ zu bestimmen a Ist.Das folgende Beispiel zeigt dies.

type family Id a :: *
type instance Id () = Int
type instance Id Char = Int

class IdToInt a where idToInt :: Id a -> Int

instance IdToInt () where idToInt x = x + 1
instance IdToInt Char where idToInt x = x - 1

main = print $ idToInt 1

Weil Id () = Id Char = Int, die Art von idToInt im obigen Kontext ist Int -> Int, was gleich ist Id () -> Int Und Id Char -> Int.Denken Sie daran, dass überladene Methoden basierend auf ihrem Typ ausgewählt werden.Beide Klasseninstanzen definieren idToInt Funktionen, die einen Typ haben Int -> Int, daher kann der Typprüfer nicht entscheiden, welches verwendet werden soll.

Sie sollten eine Datenfamilie anstelle einer Typfamilie verwenden und neue Typinstanzen deklarieren.

data family Id a :: *
newtype instance Id () = IdUnit Int
newtype instance Id Char = IdChar Int

Mit einer Newtype-Instanz, Id () Und Id Char sind beide Ints, haben aber unterschiedliche Typen.Der Typ eines Id informiert den Typprüfer darüber, welche überladene Funktion verwendet werden soll.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top