Ecco lo scenario: ho scritto del codice con una firma del tipo e i reclami di GHC non sono stati in grado di dedurre x ~ y per alcuni x
e y
. Di solito puoi lanciare GHC in un osso e semplicemente aggiungere l'isomorfismo ai vincoli di funzione, ma questa è una cattiva idea per diversi motivi:
- Non enfatizza la comprensione del codice.
- Puoi finire con 5 vincoli in cui uno sarebbe stato sufficiente (ad esempio, se i 5 sono implicati da un altro vincolo specifico)
- Puoi finire con vincoli fasulli se hai fatto qualcosa di sbagliato o se GHC non aiuta
Ho appena trascorso diverse ore a combattere il caso 3. Sto giocando syntactic-2.0
, e stavo cercando di definire una versione indipendente dal dominio share
, simile alla versione definita in NanoFeldspar.hs
.
Ho avuto questo:
{-# 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)
, che non è certo quello che stavo cercando. Quindi o avevo scritto un codice che non intendevo (che richiedeva il vincolo), oppure GHC voleva quel vincolo a causa di altri vincoli che avevo scritto.
Si scopre che dovevo aggiungere (Syntactic a, Syntactic b, Syntactic (a->b))
all'elenco dei vincoli, nessuno dei quali implica (Internal a) ~ (Internal b)
. Fondamentalmente mi sono imbattuto nei vincoli corretti; Non ho ancora un modo sistematico per trovarli.
Le mie domande sono:
- Perché GHC ha proposto questo vincolo? Da nessuna parte in sintattica c'è un vincolo
Internal a ~ Internal b
, quindi da dove GHC l'ha tirato fuori? - In generale, quali tecniche possono essere utilizzate per rintracciare l'origine di un vincolo di cui GHC ritiene di aver bisogno? Anche per i vincoli che posso scoprire da solo, il mio approccio è essenzialmente brutale forzando il percorso offensivo scrivendo fisicamente vincoli ricorsivi. Questo approccio sta fondamentalmente scendendo in una buca di coniglio infinita di vincoli e riguarda il metodo meno efficiente che posso immaginare.
a
e b
sono vincolati - guarda la firma del tipo al di fuori del tuo contesto - a -> (a -> b) -> a
, no a -> (a -> b) -> b
. Forse è tutto? Con i risolutori di vincoli, possono influenzare l'uguaglianza transitiva ovunque , ma gli errori di solito mostrano una posizione "vicina" a dove è stato indotto il vincolo. Sarebbe bello però @jozefg - forse annotare vincoli con tag o qualcosa del genere, per mostrare da dove provengono? : s