Recentemente, publiquei uma pergunta sobre sintático-2.0 com relação à definição de share
. Eu tive isso trabalhando no GHC 7.6 :
{-# LANGUAGE GADTs, TypeOperators, FlexibleContexts #-}
import Data.Syntactic
import Data.Syntactic.Sugar.BindingT
data Let a where
Let :: Let (a :-> (a -> b) :-> Full b)
share :: (Let :<: sup,
sup ~ Domain b, sup ~ Domain a,
Syntactic a, Syntactic b,
Syntactic (a -> b),
SyntacticN (a -> (a -> b) -> b)
fi)
=> a -> (a -> b) -> b
share = sugarSym Let
No entanto, o GHC 7.8 deseja -XAllowAmbiguousTypes
compilar com essa assinatura. Como alternativa, posso substituir o fi
por
(ASTF sup (Internal a) -> AST sup ((Internal a) :-> Full (Internal b)) -> ASTF sup (Internal b))
qual é o tipo implícito no fundep on SyntacticN
. Isso me permite evitar a extensão. Claro que isso é
- um tipo muito longo para adicionar a uma assinatura já grande
- cansativo derivar manualmente
- desnecessário devido ao fundep
Minhas perguntas são:
- Esse é um uso aceitável
-XAllowAmbiguousTypes
? - Em geral, quando essa extensão deve ser usada? Uma resposta aqui sugere "quase nunca é uma boa ideia".
Embora eu tenha lido os documentos , ainda estou tendo problemas para decidir se uma restrição é ambígua ou não. Especificamente, considere esta função do Data.Syntactic.Sugar:
sugarSym :: (sub :<: AST sup, ApplySym sig fi sup, SyntacticN f fi) => sub sig -> f sugarSym = sugarN . appSym
Parece-me que
fi
(e possivelmentesup
) deve ser ambíguo aqui, mas compila sem a extensão. Por que ésugarSym
inequívoco enquantoshare
é? Comoshare
é uma aplicação desugarSym
,share
todas as restrições vêm diretamentesugarSym
.
sugarSym Let
, que é(SyntacticN f (ASTF sup a -> ASTF sup (a -> b) -> ASTF sup b), Let :<: sup) => f
e não envolve variáveis de tipo ambíguas?share
, mas é compilado quando qualquer uma das assinaturas mencionadas na pergunta é usada. Você pergunta também foi questionado nos comentários de um post anteriorf
por si só é suficiente para totalmente disambiguatesig
,fi
esup
.SyntacticN
tornafi
inequívoca a entradasugarSym
, mas então por que o mesmo não é verdadeiro para afi
inshare
?Respostas:
Não vejo nenhuma versão publicada do sintático cuja assinatura
sugarSym
use exatamente esses nomes de tipos, portanto, usarei o ramo de desenvolvimento em commit 8cfd02 ^ , a última versão que ainda usava esses nomes.Então, por que o GHC se queixa da
fi
assinatura do seu tipo, mas não a assinaturasugarSym
? A documentação à qual você vinculou explica que um tipo é ambíguo se não aparecer à direita da restrição, a menos que ela esteja usando dependências funcionais para inferir o tipo ambíguo de outros tipos não ambíguos. Então, vamos comparar os contextos das duas funções e procurar dependências funcionais.Portanto
sugarSym
, os tipos não ambíguos sãosub
,sig
ef
, dentre esses, devemos ser capazes de seguir dependências funcionais para desambiguar todos os outros tipos usados no contexto, a saber,sup
efi
. E, de fato, af -> internal
dependência funcionalSyntacticN
usa nossof
para desambiguar nossafi
e, posteriormente, af -> sig sym
dependência funcionalApplySym
usa nosso recém-desambiguarfi
para desambiguarsup
(esig
, que já era não ambíguo). Então, isso explica porsugarSym
que não requer aAllowAmbiguousTypes
extensão.Vamos agora olhar
sugar
. A primeira coisa que noto é que o compilador não está reclamando de um tipo ambíguo, mas sim de instâncias sobrepostas:Portanto, se estou lendo isso corretamente, não é que o GHC pense que seus tipos são ambíguos, mas sim, ao verificar se seus tipos são ambíguos, o GHC encontrou um problema diferente e separado. É então dizer que se você dissesse ao GHC para não executar a verificação de ambiguidade, ele não teria encontrado esse problema separado. Isso explica por que ativar o AllowAmbiguousTypes permite que seu código seja compilado.
No entanto, o problema com as instâncias sobrepostas permanece. As duas instâncias listadas pelo GHC (
SyntacticN f fi
eSyntacticN (a -> f) ...
) se sobrepõem. Estranhamente, parece que o primeiro deles deve se sobrepor a qualquer outra instância, o que é suspeito. E o que isso[overlap ok]
significa?Suspeito que o Syntactic seja compilado com OverlappingInstances. E olhando o código , de fato ele faz.
Experimentando um pouco, parece que o GHC concorda com instâncias sobrepostas quando fica claro que uma é estritamente mais geral que a outra:
Mas o GHC não concorda com instâncias sobrepostas quando nenhuma delas é claramente mais adequada que a outra:
Sua assinatura de tipo usa
SyntacticN (a -> (a -> b) -> b) fi
, eSyntacticN f fi
nemSyntacticN (a -> f) (AST sym (Full ia) -> fi)
é melhor que a outra. Se eu alterar essa parte da sua assinatura de tipo paraSyntacticN a fi
ouSyntacticN (a -> (a -> b) -> b) (AST sym (Full ia) -> fi)
, o GHC não reclamará mais da sobreposição.Se eu fosse você, examinaria a definição dessas duas instâncias possíveis e determinaria se uma dessas duas implementações é a que você deseja.
fonte
Eu descobri que
AllowAmbiguousTypes
é muito conveniente para usoTypeApplications
. Considere a funçãonatVal :: forall n proxy . KnownNat n => proxy n -> Integer
de GHC.TypeLits .Para usar esta função, eu poderia escrever
natVal (Proxy::Proxy5)
. Um estilo alternativo é usarTypeApplications
:natVal @5 Proxy
. O tipo deProxy
é inferido pelo aplicativo de tipo e é irritante ter que escrevê-lo toda vez que você ligarnatVal
. Assim, podemos ativarAmbiguousTypes
e escrever:No entanto, observe que, uma vez ambíguo, você não pode voltar !
fonte