Este es el escenario: he escrito un código con una firma de tipo y las quejas de GHC no pudieron deducir x ~ y para algunos x
y y
. Por lo general, puede arrojar un hueso a GHC y simplemente agregar el isomorfismo a las restricciones de la función, pero esta es una mala idea por varias razones:
- No enfatiza la comprensión del código.
- Puede terminar con 5 restricciones donde una hubiera sido suficiente (por ejemplo, si las 5 están implicadas por una restricción específica más)
- Puede terminar con restricciones falsas si ha hecho algo mal o si GHC no ayuda
Acabo de pasar varias horas luchando contra el caso 3. Estoy jugando con syntactic-2.0
, y estaba tratando de definir una versión independiente del dominio share
, similar a la versión definida en NanoFeldspar.hs
.
Tuve esto:
{-# 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
y GHC could not deduce (Internal a) ~ (Internal b)
, que ciertamente no es lo que estaba buscando. Entonces, o bien había escrito un código que no tenía la intención (que requería la restricción), o GHC quería esa restricción debido a algunas otras restricciones que había escrito.
Resulta que necesitaba agregar (Syntactic a, Syntactic b, Syntactic (a->b))
a la lista de restricciones, ninguna de las cuales implica (Internal a) ~ (Internal b)
. Básicamente me topé con las restricciones correctas; Todavía no tengo una forma sistemática de encontrarlos.
Mis preguntas son:
- ¿Por qué GHC propuso esa restricción? En ninguna parte sintáctica hay una restricción
Internal a ~ Internal b
, entonces, ¿de dónde sacó GHC eso? - En general, ¿qué técnicas se pueden utilizar para rastrear el origen de una restricción que GHC cree que necesita? Incluso para las limitaciones que puedo descubrir por mí mismo, mi enfoque es esencialmente bruto forzando el camino ofensivo al escribir físicamente las restricciones recursivas. Este enfoque está básicamente bajando por un agujero infinito de restricciones y es el método menos eficiente que pueda imaginar.
fuente
a
yb
están vinculados, mira la firma de tipo fuera de tu contextoa -> (a -> b) -> a
, noa -> (a -> b) -> b
. Tal vez eso es todo? Con los solucionadores de restricciones, pueden influir en la igualdad transitiva en cualquier lugar , pero los errores generalmente muestran una ubicación "cercana" al lugar donde se indujo la restricción. Sin embargo, eso sería genial @jozefg: ¿quizás anotar restricciones con etiquetas o algo así, para mostrar de dónde vinieron? : sRespuestas:
En primer lugar, su función tiene el tipo incorrecto; Estoy bastante seguro de que debería ser (sin el contexto)
a -> (a -> b) -> b
. GHC 7.10 es algo más útil para señalar eso, porque con su código original, se queja de una falta de restricciónInternal (a -> b) ~ (Internal a -> Internal a)
. Después de corregirshare
el tipo, GHC 7.10 sigue siendo útil para guiarnos:Could not deduce (Internal (a -> b) ~ (Internal a -> Internal b))
Después de agregar lo anterior, obtenemos
Could not deduce (sup ~ Domain (a -> b))
Después de añadir que, obtenemos
Could not deduce (Syntactic a)
,Could not deduce (Syntactic b)
yCould not deduce (Syntactic (a -> b))
Después de agregar estos tres, finalmente escribe; así que terminamos con
Entonces diría que GHC no ha sido inútil para guiarnos.
En cuanto a su pregunta sobre el seguimiento de dónde GHC obtiene sus requisitos de restricción, puede probar los indicadores de depuración de GHC , en particular
-ddump-tc-trace
, y luego leer el registro resultante para ver dóndeInternal (a -> b) ~ t
y(Internal a -> Internal a) ~ t
se agregan alWanted
conjunto, pero eso será una lectura bastante larga .fuente
¿Intentaste esto en GHC 8.8+?
La clave es usar el tipo de agujero entre las restricciones:
_ => your difficult type
fuente