Pergunta

Eu quero criar minha própria mônada. Isso é o que escrevi:

data LeafConType a = LeafCon (a,Int,Int)

instance Monad (LeafConType ) where
return = LeafCon 
lc@(LeafCon (t,i,n)) >>= f = if i>=n
                                then lc
                                else f (t,i,n)

Mas isso não funciona. GHC diz:

leafcon.hs:26:1:
    Occurs check: cannot construct the infinite type: a = (a, Int, Int)
    When generalising the type(s) for `return'
    In the instance declaration for `Monad LeafConType'

leafcon.hs:27:1:
    Occurs check: cannot construct the infinite type: a = (a, Int, Int)
    When generalising the type(s) for `>>='
    In the instance declaration for `Monad LeafConType'

O que há de errado nisso?


Eu quero fazer cálculos enquanto estiver menor que n. n deve ser constantes por ainda não sei como fazer isso correto. Deve ser uma mistura de estado e talvez. Se você tem alguns conselhos, sinta -se à vontade para compartilhar comigo: P

Foi útil?

Solução

Sobre return:

Prelude> :t return
return :: (Monad m) => a -> m a 

Então return leva um argumento do tipo a, e retorna algo do tipo m a. Nesse caso m é LeafConType, assim LeafConType a é devolvido.

Agora suponha que passamos True. Então a = Bool, então o tipo de retorno deve ser LeafConType Bool. No entanto, você define:

return = LeafCon

Então, return True torna-se LeafCon True. Mas isso não é permitido, porque a definição de tipo de LeafConType afirma isso

data LeafConType a = LeafCon (a, Int, Int)

Então para LeafConType Bool o argumento para LeafCon deve ter tipo (Bool, Int, Int), não apenas Bool. E é isso que o erro de compilação significa: a não pode ser o mesmo que (a, Int, Int). Você afirma:

Eu quero fazer cálculos enquanto i é menor que n.

Isso significa que você precisará de alguns valores padrão para i e n, por caso contrário, será impossível definir return. Se os dois forem zero por padrão, você poderá definir:

return a = LeafCon (a, 0, 0)

Sobre (>>=):

Prelude> :t (>>=)
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b

Agora veja sua implementação (notação ligeiramente diferente, a mesma ideia):

lc@(LeafCon (t, i, n)) >>= f | i >= n    = lc 
                             | otherwise = f t

O que vemos aqui é que lc é devolvido quando i >= n. Mas lc é do tipo LeafConType a, enquanto f é uma função que pode retornar um valor do tipo LeafConType b, por algum b. Como resultado, pode ser isso b não é igual a a E, portanto, esses tipos não correspondem. Em conclusão, você precisa seriamente fazer uma pergunta:

  Esse tipo de computação pode ser expresso como uma mônada?

Outras dicas

As funções que você especificou para >>= e return Não satisfaça os tipos necessários por Monad:

return :: a -> LeafConType a

Dada a declaração

return = LeafCon

você dá à função o tipo incompatível

return :: (a, Int, Int) -> LeafConType a

Uma declaração como return 42 Portanto, seria impossível em sua mônada.

Não entendo o que sua mônada deve fazer. Primeiro dê uma olhada em Mônadas simples e trabalhando!

instance Monad [] where
    (>>=) = concatMap
    return a = [a]

instance Monad Maybe where
    return = Just
    (Just x) >>= f = f x
    Nothing >>= f = Nothing

A julgar pela sua descrição do que você deseja que sua mônada faça, acho que você quer algo um pouco assim:

data LeafConType a = LeafCon { runLeafCon' :: Int -> Int -> (Maybe a, Int, Int) }

runLeafCon :: Int -> Int -> LeafConType a -> Maybe a
runLeafCon i n lc = let (t, _, _) = runLeafCon' lc i n in t

getI :: LeafConType Int
getI = LeafCon $ \i n -> (Just i, i, n)

getN :: LeafConType Int
getN = LeafCon $ \i n -> (Just n, i, n)

setI :: Int -> LeafConType ()
setI i = LeafCon $ \_ n -> (Just (), i, n)

setN :: Int -> LeafConType ()
setN n = LeafCon $ \i _ -> (Just (), i, n)

instance Monad LeafConType where
    return t = LeafCon $ \i n -> if (i < n) 
                                 then (Just t, i, n) 
                                 else (Nothing, i, n)

    (LeafCon k) >>= f = 
        LeafCon $ \i n -> 
            let (t, i', n') = k i n
            in case t of
                 Nothing -> (Nothing, i', n')
                 (Just t') -> if (i' < n')
                              then runLeafCon' (f t') i' n'
                              else (Nothing, i, n)


example :: Int -> LeafConType ((), Int)
example x = do 
  i <- getI
  m <- setI (i + x)
  return (m, i + x)

Alguns exemplos:

*Main> runLeafCon 2 10 $ example 4
Just ((),6)
*Main> runLeafCon 2 10 $ example 8
Nothing
*Main> runLeafCon 2 10 $ example 7
Just ((),9)

Juntei isso rapidamente, é bastante feio e não verifiquei se isso obedece a alguma das leis da Mônada, então use por sua conta e risco! :)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top