Aqui está o cenário: escrevi algum código com uma assinatura de tipo e as queixas do GHC não puderam deduzir x ~ y para alguns x
e y
. Geralmente, você pode jogar um osso no GHC e simplesmente adicionar o isomorfismo às restrições da função, mas essa é uma má idéia por vários motivos:
- Não enfatiza a compreensão do código.
- Você pode terminar com 5 restrições nas quais uma seria suficiente (por exemplo, se as 5 estiverem implícitas em mais uma restrição específica)
- Você pode acabar com restrições falsas se tiver feito algo errado ou se o GHC estiver sendo inútil
Passei várias horas lutando contra o caso 3. Estou brincando syntactic-2.0
e tentando definir uma versão independente do domínio share
, semelhante à versão definida em NanoFeldspar.hs
.
Eu tive isso:
{-# LANGUAGE GADTs, FlexibleContexts, TypeOperators #-}
import Data.Syntactic
-- Based on NanoFeldspar.hs
data Let a where
Let :: Let (a :-> (a -> b) :-> Full b)
share :: (Let :<: sup,
Domain a ~ sup,
Domain b ~ sup,
SyntacticN (a -> (a -> b) -> b) fi)
=> a -> (a -> b) -> a
share = sugarSym Let
e GHC could not deduce (Internal a) ~ (Internal b)
, que certamente não é o que eu estava procurando. Então, ou eu tinha escrito algum código que não pretendia (que exigia a restrição) ou o GHC queria essa restrição devido a outras restrições que eu havia escrito.
Acontece que eu precisava adicionar (Syntactic a, Syntactic b, Syntactic (a->b))
à lista de restrições, nenhuma das quais implica (Internal a) ~ (Internal b)
. Eu basicamente me deparei com as restrições corretas; Ainda não tenho uma maneira sistemática de encontrá-los.
Minhas perguntas são:
- Por que o GHC propôs essa restrição? Em nenhum lugar da sintática há uma restrição
Internal a ~ Internal b
, então de onde o GHC conseguiu isso? - Em geral, que técnicas podem ser usadas para rastrear a origem de uma restrição que o GHC acredita que precisa? Mesmo para as restrições que posso descobrir, minha abordagem é essencialmente bruta, forçando o caminho ofensivo, anotando fisicamente as restrições recursivas. Essa abordagem está basicamente diminuindo um buraco infinito de restrições e é o método menos eficiente que posso imaginar.
a
e b
são vinculados - observe a assinatura de tipo fora do seu contexto - a -> (a -> b) -> a
, não a -> (a -> b) -> b
. Talvez seja isso? Com os solucionadores de restrições, eles podem influenciar a igualdade transitiva em qualquer lugar , mas os erros geralmente mostram um local "próximo" de onde a restrição foi induzida. Isso seria legal, embora @jozefg - talvez anotando restrições com tags ou algo assim, para mostrar de onde elas vieram? : s