Recientemente publiqué una pregunta sobre syntactic-2.0 con respecto a la definición de share
. He tenido esto trabajando en 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
Sin embargo, GHC 7.8 quiere -XAllowAmbiguousTypes
compilar con esa firma. Alternativamente, puedo reemplazar el fi
con
(ASTF sup (Internal a) -> AST sup ((Internal a) :-> Full (Internal b)) -> ASTF sup (Internal b))
cual es el tipo implicado por el fundep en SyntacticN
. Esto me permite evitar la extensión. Por supuesto esto es
- un tipo muy largo para agregar a una firma ya grande
- cansado de derivar manualmente
- innecesario debido al fundep
Mis preguntas son:
- ¿Es este un uso aceptable de
-XAllowAmbiguousTypes
? - En general, ¿cuándo se debe usar esta extensión? Una respuesta aquí sugiere que "casi nunca es una buena idea".
Aunque he leído los documentos , todavía tengo problemas para decidir si una restricción es ambigua o no. Específicamente, considere esta función de Data.Syntactic.Sugar:
sugarSym :: (sub :<: AST sup, ApplySym sig fi sup, SyntacticN f fi) => sub sig -> f sugarSym = sugarN . appSym
Me parece que
fi
(y posiblementesup
) debería ser ambiguo aquí, pero se compila sin la extensión. ¿Por qué essugarSym
inequívoco mientrasshare
es? Comoshare
es una aplicación desugarSym
,share
todas las restricciones provienen directamentesugarSym
.
sugarSym Let
, que es(SyntacticN f (ASTF sup a -> ASTF sup (a -> b) -> ASTF sup b), Let :<: sup) => f
y no involucra variables de tipo ambiguas?share
, pero se compila cuando se usa cualquiera de las firmas mencionadas en la pregunta.f
por sí sola es suficiente para disambiguate totalmentesig
,fi
ysup
.SyntacticN
esfi
ambiguo ensugarSym
, pero ¿por qué no es lo mismo enfi
inshare
?Respuestas:
No veo ninguna versión publicada de Syntactic cuya firma
sugarSym
use esos nombres de tipo exactos, por lo que usaré la rama de desarrollo en commit 8cfd02 ^ , la última versión que todavía usa esos nombres.Entonces, ¿por qué GHC se queja de la
fi
firma de su tipo pero no de la firmasugarSym
? La documentación a la que se ha vinculado explica que un tipo es ambiguo si no aparece a la derecha de la restricción, a menos que la restricción esté utilizando dependencias funcionales para inferir el tipo ambiguo de otros tipos no ambiguos. Entonces, comparemos los contextos de las dos funciones y busquemos dependencias funcionales.Entonces
sugarSym
, los tipos no ambiguos sonsub
,sig
yf
, y de ellos deberíamos poder seguir las dependencias funcionales para desambiguar todos los otros tipos utilizados en el contexto, a saber,sup
yfi
. Y, de hecho, laf -> internal
dependencia funcional enSyntacticN
utiliza nuestrof
para desambiguar nuestrofi
, y luego laf -> sig sym
dependencia funcional enApplySym
usa nuestro recién desambiguadofi
para desambiguarsup
(ysig
, que ya no era ambiguo). Eso explica por quésugarSym
no requiere laAllowAmbiguousTypes
extensión.Veamos ahora
sugar
. Lo primero que noto es que el compilador no se queja de un tipo ambiguo, sino de instancias superpuestas:Entonces, si estoy leyendo esto correctamente, no es que GHC piense que sus tipos son ambiguos, sino que mientras verifica si sus tipos son ambiguos, GHC encontró un problema diferente y diferente. Luego le dice que si le dijera a GHC que no realizara la verificación de ambigüedad, no habría encontrado ese problema por separado. Esto explica por qué habilitar AllowAmbiguousTypes permite que su código se compile.
Sin embargo, el problema con las instancias superpuestas permanece. Las dos instancias enumeradas por GHC (
SyntacticN f fi
ySyntacticN (a -> f) ...
) se superponen entre sí. Por extraño que parezca, parece que el primero de estos debería superponerse con cualquier otra instancia, lo que es sospechoso. Y que[overlap ok]
significaSospecho que Syntactic está compilado con OverlappingInstances. Y mirando el código , de hecho lo hace.
Experimentando un poco, parece que GHC está de acuerdo con instancias superpuestas cuando está claro que una es estrictamente más general que la otra:
Pero GHC no está de acuerdo con las instancias superpuestas cuando ninguna de ellas es claramente mejor que la otra:
Sus usos tipo de firma
SyntacticN (a -> (a -> b) -> b) fi
, y niSyntacticN f fi
tampocoSyntacticN (a -> f) (AST sym (Full ia) -> fi)
es un ajuste mejor que el otro. Si cambio esa parte de su firma tipo aSyntacticN a fi
oSyntacticN (a -> (a -> b) -> b) (AST sym (Full ia) -> fi)
, GHC ya no se queja de la superposición.Si yo fuera usted, miraría la definición de esas dos posibles instancias y determinaría si una de esas dos implementaciones es la que desea.
fuente
He descubierto que
AllowAmbiguousTypes
es muy conveniente para usarTypeApplications
. Considere la funciónnatVal :: forall n proxy . KnownNat n => proxy n -> Integer
de GHC.TypeLits .Para usar esta función, podría escribir
natVal (Proxy::Proxy5)
. Un estilo alternativo es usarTypeApplications
:natVal @5 Proxy
. El tipo deProxy
es inferido por la aplicación de tipo, y es molesto tener que escribirlo cada vez que llamanatVal
. Así podemos habilitarAmbiguousTypes
y escribir:Sin embargo, tenga en cuenta que una vez que se vuelve ambiguo, ¡no puede regresar !
fuente