Digite o erro ao tentar implementar a função (>> =) para criar um transformador de Monad personalizado
-
27-09-2019 - |
Pergunta
Estou tentando criar um transformador de Monad para um projeto futuro, mas, infelizmente, minha implementação da função da Monad Typeclasse (>> = =) não funciona.
Primeiro de tudo, aqui está a implementação da Mônada subjacente:
newtype Runtime a = R {
unR :: State EInfo a
} deriving (Monad)
Aqui, a implementação do TypeClasse da Mônada é feita automaticamente pelo GHC (usando o GeneralizedNewtypeDeriving
idioma Pragma). O transformador da Mônada é definido como assim:
newtype RuntimeT m a = RuntimeT {
runRuntimeT :: m (Runtime a)
}
O problema vem da maneira como eu instanculo a função (>> =) do Monad TypeClasse:
instance (Monad m) => Monad (RuntimeT m) where
return a = RuntimeT $ (return . return) a
x >>= f = runRuntimeT x >>= id >>= f
Do jeito que eu vejo, o primeiro >>=
corre no subjacente m
Mônada. Desta forma, runRuntimeT x >>=
Retorna um valor do tipo Runtime a
(certo ?). Então, o seguinte código, id >>=
, deve retornar um valor do tipo a
. Este valor é transmitido para a função f do tipo f :: (Monad m) => a -> RuntimeT m b
.
E aqui vem o problema do tipo: o f
O tipo da função não corresponde ao tipo exigido pela função (>> =). Jow posso fazer disso coerente? Eu posso ver por que isso não funciona, mas não consigo transformá -lo em algo funcional.
Editar: a mensagem de erro:
Core.hs:34:4:
Occurs check: cannot construct the infinite type: m = RuntimeT m
When generalising the type(s) for `>>='
In the instance declaration for `Monad (RuntimeT m)'
Failed, modules loaded: none.
Obrigado pela ajuda e não hesite em corrigir nenhuma falha na minha mensagem,
Charlie P.
Solução
Para seguir o que Reid Barton disse sobre StateT
- Veja como você definiria RuntimeT
usando StateT
. o Runtime
Monad pode então ser definido trivialmente usando a Mônada de Identidade.
newtype RuntimeT m a = R {
unR :: StateT EInfo m a
}
type Runtime = RuntimeT Identity
instance Monad m => Monad (RuntimeT m) where
return = R . return
(R m) >>= f = R (m >>= unR . f)
Outras dicas
O habitual StateT s m
Monad envia a
para s -> m (a, s)
Mas você está trabalhando com m (s -> (a, s))
em vez de. Eu não acho que o último forma uma mônada para o general s
. Tem certeza que não quer apenas usar StateT
?
Eis porque eu não acho a
→ m (s -> (a, s))
é uma mônada: escrever >>=
Eu preciso de uma função que tome argumentos de tipos
m (s -> (a, s))
a -> m (s -> (b, s))
e retorna um valor do tipo
m (s -> (b, s))
Os "efeitos" (ou seja, fmap (const ())
) do resultado deve ser o do primeiro argumento, já que não temos como obter um a
passar para o segundo argumento. Desde m
aparece apenas na parte externa do tipo de resultado, não podemos usar o segundo argumento para nada - não haverá como nos livrar do m
Introduz.