Создание монад в haskell

Я хочу создать свою собственную монаду. Вот что я написал:--3-->

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)

но это не работает. Ghc говорит:

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'

что в этом плохого?


Я хочу делать вычисления, пока i меньше n. n должно быть константами, я еще не знаю, как это сделать правильно. Это должна быть какая-то смесь государства и возможно. Если у вас есть некоторые советы, не стесняйтесь поделиться им со мной:P

3 ответов


о return:

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

так return принимает аргумент типа a, и возвращает что-то типа m a. В этом случае m и LeafConType, так что LeafConType a возвращается.

теперь предположим, что мы проходим True. Тогда a = Bool, поэтому тип возврата должен быть LeafConType Bool. Однако вы определяете:

return = LeafCon

и return True становится LeafCon True. Но это не допускается, потому что определение типа LeafConType государств это

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

и LeafConType Bool аргумент LeafCon должно иметь тип (Bool, Int, Int) не только Bool. И это то, что означает ошибка компиляции:a не может быть таким же, как (a, Int, Int). Вы заявляете:

я хочу делать вычисления в то время как i меньше n.

это означает, что вам понадобятся некоторые значения по умолчанию для i и n, ибо иначе невозможно будет определить return. Если они оба равны нулю по умолчанию, тогда вы можете определить:

return a = LeafCon (a, 0, 0)

о (>>=):

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

теперь посмотри на свою реализацию (немного другая нотация, та же идея):

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

что мы видим здесь, это lc возвращается, когда i >= n. Но!--31--> типа LeafConType a, а f - это функция, которая может возвращать значение типа LeafConType b, for любой b. В результате могло получиться, что b не равно a и поэтому эти типы не совпадают. В заключение вы должны серьезно задать себе один вопрос:--43-->

может ли этот тип вычислений быть выражен как монада в любом случае?


функции, которые вы указали для >>= и return не удовлетворяют типы, требуемые Monad:

return :: a -> LeafConType a

данной декларации

return = LeafCon

вы даете функции несовместимый тип

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

заявления return 42 поэтому было бы невозможно в вашей монаде.

Я вообще не понимаю, что должна делать ваша монада. Сначала взгляните на простые, работающие монады!

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

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

судя по вашему описанию того, что вы хотите, чтобы ваша монада делать, я думаю, что вы хотите, что-то вроде этого:

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)

примеры:

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

Я бросил это вместе довольно быстро, это довольно уродливо, и я не проверял, подчиняется ли он каким-либо законам монады, так что используйте на свой страх и риск! :)