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.
a
y b
están vinculados, mira la firma de tipo fuera de tu contexto a -> (a -> b) -> a
, no a -> (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? : s