2011-08-03 22 views
9

He escrito una función similar a Data.Enumerator.List.map que hace que un Iteratee sea compatible con un Enumerator que alimenta un tipo diferente Stream.Escriba la firma en una cláusula where

import Data.Enumerator

test :: Monad m => (ao -> ai) -> Iteratee ai m b -> Iteratee ao m b 
test f iter = go $$ iter 
    where go (Continue k) = continue $ 
      \stream -> go $$ k (fmap f stream) 
     go (Yield res _) = yield res EOF 

Si Omito la firma tipo para go, esto va a funcionar muy bien. Sin embargo, me gustaría incluirlo pero no puedo determinar cuál debería ser la firma correcta. Aquí es lo que creo que debe ser:

go :: Monad m => Step ai m b -> Iteratee ao m b

pero eso no funciona.
Necesito un consejo para encontrar la firma de tipo correcta para go.

Respuesta

12

Probablemente no le pueda dar a go una firma de tipo como está.

La razón de esto es que hace uso de argumentos polimórficos vinculados por test. Esto significa que, dentro de go, el identificador f tiene el tipo (ao -> ai) para algunos tipos específicosao y ai.

variables de tipo son por lo general sólo en el alcance de la firma de tipo único en el que están introducidos, por lo que cuando usted da su tipo go propia firma, el ao y ai de nuevas, tipos polimórficos, que por supuesto provoca un error de tipo cuando intente combinarlos con los tipos nombrados de manera similar, pero fijos (e incognoscibles) de la firma test.

El resultado final es que no se puede escribir el tipo de go explícitamente, lo cual no es muy satisfactorio. Para resolver esto, GHC ofrece the ScopedTypeVariables extension, que permite traer variables introducidas en una firma de tipo en el alcance dentro de la cláusula where de la función, entre otras cosas.

Tenga en cuenta que si sólo utiliza la cláusula where para crear un ámbito interno de las definiciones, y no hacer uso de identificadores vinculados por argumentos a la función externa, puede escribir las firmas de tipos en la cláusula where igual que usted puede para enlaces de nivel superior. Si no desea usar las extensiones de GHC, simplemente puede pasar los parámetros de forma redundante. Algo como esto debería funcionar en ese caso:

test :: Monad m => (ao -> ai) -> Iteratee ai m b -> Iteratee ao m b 
test f iter = go f $$ iter 
    where go :: Monad m => (ao -> ai) -> Step ai m b -> Iteratee ao m b 
     go f (Continue k) = continue $ 
      \stream -> go f $$ k (fmap f stream) 
     go _ (Yield res _) = yield res EOF 
+0

@hammar: Gracias. Juro que hago un error tonto cada vez que coloco más de cuatro líneas de código en una respuesta sin cargarlas primero en GHCi. Suspiro... –

6

es probable que desee ese tipo, pero con la extensión ScopedTypeVariables habilitado y con las variables de tipo de firma test 's en el alcance:

{-# LANGUAGE ScopedTypeVariables #-} 
import Data.Enumerator 
test :: forall m ai ao b. Monad m => (ao -> ai) -> Iteratee ai m b -> Iteratee ao m b 
test f iter = go $$ iter 
    where go :: Step ai m b -> Iteratee ao m b 
     go (Continue k) = continue $ 
      \stream -> go $$ k (fmap f stream) 
     go (Yield res _) = yield res EOF 

Véase el GHC documentation para más información.

+1

N.B. - Observe que no existe ninguna restricción 'Monad m' aquí en el tipo' go', a diferencia del tipo propuesto en la pregunta. Esto es importante, porque no solo es el mismo 'm' en alcance, la * restricción de clase * también está todavía dentro del alcance. –

+0

... aunque probablemente funcionaría bien si añadieras la restricción. Sería especialmente fácil para GHC encontrar una instancia. –

+0

Lo hace.También puede agregar 'Monad Maybe',' m ~ m' o cualquier otra restricción trivial al contexto, y GHC lo aceptará sin quejarse. Sin embargo, advierte sobre '(Monad m, Monad m, Monad m) =>'. –