2012-06-14 17 views
21

Estaba siguiendo el documento "Kleisli arrows of outrageous fortune" de Conor McBride y he publicado mi implementación de su código here. Brevemente, define los siguientes tipos y clases:Reingresando notación para mónadas indexadas

type a :-> b = forall i . a i -> b i 

class IFunctor f where imap :: (a :-> b) -> (f a :-> f b) 

class (IFunctor m) => IMonad m where 
    skip :: a :-> m a 
    bind :: (a :-> m b) -> (m a :-> m b) 

data (a := i) j where 
    V :: a -> (a := i) i 

Luego se define dos tipos de liga, el último de los cuales utiliza (:=) para restringir el índice inicial:

-- Conor McBride's "demonic bind" 
(?>=) :: (IMonad m) => m a i -> (a :-> m b) -> m b i 
(?>=) = flip bind 

-- Conor McBride's "angelic bind" 
(>>=) :: (IMonad m) => m (a := j) i -> (a -> m b j) -> m b i 
m >>= f = bind (\(V a) -> f a) m 

Este último se unen funciona perfectamente bien para reconsolidación do notación de usar mónadas indexadas con la extensión RebindableSyntax, usando las siguientes definiciones correspondientes para return y fail:

return :: (IMonad m) => a -> m (a := i) i 
return = skip . V 

fail :: String -> m a i 
fail = error 

... pero el problema es que no puedo obtener el enlace anterior (es decir (?>=)) para trabajar. He intentado en lugar definiendo (>>=) y return ser:

(>>=) :: (IMonad m) => m a i -> (a :-> m b) -> m b i 
(>>=) = (?>=) 

return :: (IMonad m) => a :-> m a 
return = skip 

Entonces creó un tipo de datos garantizada habitar un índice específico:

data Unit a where 
    Unit :: Unit() 

Pero cuando intento volver a enlazar do notación utilizando las nuevas definiciones para (>>=) y return, que no funciona, como se demuestra en el siguiente ejemplo:

-- Without do notation 
test1 = skip Unit >>= \Unit -> skip Unit 

-- With do notation 
test2 = do 
    Unit <- skip Unit 
    skip Unit 

test1 controles de tipo, pero no test2, lo cual es raro, ya que pensamos que todo lo que hizo fue dejar que RebindableSyntaxdo notación desugar test2 a test1, así que si test1 tipo comprobaciones, entonces ¿por qué no lo hace test2? El error que consigo es:

Couldn't match expected type `t0 -> t1' 
      with actual type `a0 :-> m0 b0' 
Expected type: m0 a0 i0 -> (t0 -> t1) -> m Unit() 
    Actual type: m0 a0 i0 -> (a0 :-> m0 b0) -> m0 b0 i0 
In a stmt of a 'do' block: Unit <- skip Unit 
In the expression: 
    do { Unit <- skip Unit; 
     skip Unit } 

el error se mantiene incluso cuando se utiliza la sintaxis explícita forall en lugar del operador :-> tipo.

Ahora, sé que hay otro problema con el uso del "enlace demoníaco", que es que no puede definir (>>), pero todavía quería ver qué tan lejos podía ir con él. ¿Alguien puede explicar por qué no puedo hacer que GHC deshaga el "lazo demoníaco", incluso cuando normalmente lo haría?

+0

Dado que esto surgió en una [nueva pregunta duplicada] (http://stackoverflow.com/questions/33488322/ranknpolymorphism-and-kleisli-arrows-of-outrageous-fortune), señalaré que hoy en día GHC (actualmente 7.10.2) apenas admite 'ImpredicativeTypes' en absoluto, por lo tanto, mucho más que la notación' do 'se rompe para este código ahora. –

Respuesta

9

IIUC, el gestor de GHC en realidad ejecuta después de typechecker (source). Eso explica por qué la situación que observas es teóricamente posible. Probablemente, el contador de tipos tiene algunas reglas de tipado especiales para la notación do, y esas pueden ser inconsistentes con lo que haría el contador de letras con el código desugarred.

Por supuesto, es razonable esperar que sean consistentes, por lo que recomendaría presentar un error GHC.

+2

Gracias por el enlace. Voy a verificar esto. Si aceptan que esa es la razón del error de tipo, aceptaré su respuesta. –

+2

También estoy interesado en saber qué está pasando. Enfrenté el mismo problema, pero estaba menos agitado. Supongo que el polimorfismo demoníaco fue inesperado: sorprendió a mucha gente. – pigworker