Eu tenho um tipo de termo que, em essência, é:
data Term a = Term a Bool
deriving Show
O Bool
é um tipo de ajudante, e é necessário apenas ocasionalmente. Gostaria que o usuário pudesse ignorá-lo se não for necessário. Ou seja, o seguinte deve funcionar:
main :: IO ()
main = print v
where v = "a"
=: Term "b" True
=: []
onde:
class Join a b where
(=:) :: a -> [b] -> [b]
infixr 1 =:
instance Join a (Term a) where
x =: y = Term x False : y
instance Join (Term a) (Term a) where
x =: y = x : y
Infelizmente, o GHC reclama:
a.hs:15:8-12: error: [GHC-39999]
• Ambiguous type variable ‘b0’ arising from a use of ‘print’
prevents the constraint ‘(Show b0)’ from being solved.
Relevant bindings include v :: [b0] (bound at a.hs:17:8)
Mas se eu escrever main
assim:
main :: IO ()
main = print v
where v :: [Term String]
v = "a"
=: Term "b" True
=: []
Então está tudo bem. (Observe que a única diferença é que dei v
uma assinatura de tipo.)
Estou curioso para saber se existe algum truque moderno do GHC (famílias de tipos, famílias de tipos injetivos, famílias de tipos fechados, GADTs, etc., ou algum sinalizador mágico) que tornaria a assinatura de tipo desnecessária e permitiria que o GHC deduzisse o tipo sozinho, sem qualquer ajuda.
Aqui está o programa na íntegra:
data Term a = Term a Bool
deriving Show
class Join a b where
(=:) :: a -> [b] -> [b]
infixr 1 =:
instance Join a (Term a) where
x =: y = Term x False : y
instance Join (Term a) (Term a) where
x =: y = x : y
main :: IO ()
main = print v
where v :: [Term String] -- Want to avoid this signature and let GHC deduce it.
v = "a"
=: Term "b" True
=: []
E a questão é se há algum recurso ou truque do GHC que eu possa aplicar para que a assinatura de tipo em v
não seja necessária. Também estou feliz em mudar a Join
classe ou adicionar novos parâmetros/dependências etc., desde que a definição local de v
possa ser escrita como está sem a assinatura de tipo.
Eu também apreciaria uma explicação de por que isso não seria possível, se houver uma boa razão legítima para que o que estou pedindo seja irracional. Parece-me que há informações de tipo suficientes para que o GHC deduza o tipo em si, mas suspeito que algum tipo de suposição de "mundo aberto" esteja acontecendo e nenhum dos truques de "família de tipos fechados" funcionará; se esse for realmente o caso. Para ser claro, estou perfeitamente feliz que a Join
classe nunca seja instanciada em nenhum outro tipo.
De longe, a coisa mais simples a fazer é simplesmente usar
(:)
em vez de(=:)
quando você inclui oBool
campo. Então você nem precisa de nenhuma classe, você apenas escreve:Você pode precisar de uma declaração de fixidez para
=:
.Reconheço que esta não é uma resposta completa, pois agora há dois nomes de funções diferentes, então pode não ser generalizável para a versão não minimizada do seu problema, mas considere isso de qualquer maneira...
Aqui está uma abordagem. Você pode usar uma família de tipos fechados que seleciona o tipo correto do lado direito com base no tipo do lado esquerdo:
e definir o
=:
operador como uma função polimórfica simples usando esta família:Agora, sua
main
compilação ocorre porque o lado esquerdo de cada=:
chamada força um tipo inequívoco do lado direito:Claro,
(=:) = undefined
é uma implementação decididamente subótima. Para implementar=:
corretamente, você precisará usar uma restrição para despachar para a definição correta:Com definições de classe e instância apropriadas:
o programa completo parece funcionar como pretendido: