Pergunta

Eu tenho este código:

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

E eu quero fazer isso para que eu possa sempre obter um Int da família de tipos Id.Reconheço que será necessária uma conversão.

Achei que talvez criar uma classe funcionasse:

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

instance IdToInt Box where
  idToInt s = s

E isso realmente compila.Mas quando tento usá-lo:

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

Eu recebo um erro:

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

Então, como posso criar uma conversão para um ID de família de tipos para obter um Int?

Com base na resposta de ehird, tentei o seguinte, mas também não funcionou:

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

Dá erro:

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
Foi útil?

Solução

Como outros apontaram, o problema é que o compilador não consegue descobrir qual a usar.As famílias de dados são uma solução, mas uma alternativa que às vezes é mais fácil de trabalhar é usar um tipo testemunha.

Mude sua turma para

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)

A pergunta que você precisa responder é: para qualquer Id, existe apenas um tipo a deve aparecer com?Ou seja, existe uma correspondência um para um entre aareia Id aé?Nesse caso, as famílias de dados são a abordagem correta.Caso contrário, você pode preferir uma testemunha.

Outras dicas

Você não pode.Você precisará testFunc :: (IdToInt a) => Id a -> Int.As famílias do tipo são abertas, então qualquer pessoa pode declarar

type instance Id Blah = ()

a qualquer momento e não oferece função de conversão.A melhor coisa a fazer é colocar a família de tipos na classe:

class HasId a where
  type Id a
  idToInt :: Id a -> Int

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

Você ainda precisará do contexto.

Você não pode usar uma função do tipo IdToInt a => Id a -> Int porque não há como determinar que tipo a é.O exemplo a seguir demonstra isso.

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

Porque Id () = Id Char = Int, o tipo de idToInt no contexto acima é Int -> Int, que é igual a Id () -> Int e Id Char -> Int.Lembre-se de que os métodos sobrecarregados são escolhidos com base no tipo.Ambas as instâncias de classe definem idToInt funções que possuem tipo Int -> Int, portanto, o verificador de tipo não pode decidir qual usar.

Você deve usar uma família de dados em vez de uma família de tipos e declarar instâncias de newtype.

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

Com uma instância newtype, Id () e Id Char são ambos ints, mas têm tipos diferentes.O tipo de um Id informa ao verificador de tipo qual função sobrecarregada usar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top