Преобразователи монад: реализация стековой машины с помощью MaybeT (State Stack)

Я пытаюсь реализовать монадный преобразователь Maybe-State и использовать его для реализации простой стековой машины. Определения монады состояния и, возможно, должны быть правильными. Теперь я пытаюсь реализовать поп:

pop :: MaybeT (State Stack) Int

Так что, если стек пуст, он ничего не возвращает, иначе он возвращает Just <popped stack>. Это то, что у меня есть до сих пор:

pop :: MaybeT (State Stack) Int
pop = guard True (do (r:rs) <- get
                     put rs
                     return r)

(Очевидно, что True - это просто фиктивный заполнитель - я реализую условие позже, а сейчас я хочу правильно понять другую часть).

Что не так с моим кодом? Насколько я понимаю, guard принимает условное выражение (True) и функцию f. Если условное выражение истинно, оно дает pure f.

В моем случае,

pure = MaybeT . return . Just

Так не должна ли моя функция f просто возвращать State Stack Int?


Вот полный код с моими реализациями MaybeT и State:

import Control.Applicative (Alternative(..))
import Control.Monad (liftM, ap, guard)
import Control.Monad.Trans.Class (MonadTrans(lift))

main :: IO()
main = return ()


-- State Monad
--------------

newtype State s a = MakeState { runState :: s -> (a, s) }

instance Functor (State s) where
    fmap  = liftM


instance Applicative (State s) where
    pure a = MakeState $ \s -> (a, s)
    (<*>)  = ap

instance Monad (State s) where
    return a = MakeState $ \s -> (a, s)
    m >>= k  = MakeState $ \s -> let (x, s') = runState m s
                              in runState (k x) s'

get :: State s s
get = MakeState $ \s -> (s, s)

put :: s -> State s ()
put s = MakeState $ \_ -> ((), s)

modify :: (s -> s) -> State s ()
modify f = MakeState $ \s -> ((), f s)

-- MaybeT MonadTransformer
---------------------------

newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }

instance Monad m => Functor (MaybeT m) where
    fmap a x = MaybeT $ do e <- runMaybeT x
                           return $ fmap a e



instance Monad m => Applicative (MaybeT m) where
    pure      = MaybeT . return . Just
    (<*>) a b = MaybeT $ do e <- runMaybeT a
                            f <- runMaybeT b
                            return $ e <*> f

instance Monad m => Monad (MaybeT m) where
    return  = pure
    a >>= b = MaybeT $ do aa <- runMaybeT a
                          maybe (return Nothing) (runMaybeT . b) aa


instance Monad m => Alternative (MaybeT m) where
  empty   = MaybeT $ return Nothing
  a <|> b = MaybeT $ do aa <- runMaybeT a
                        bb <- runMaybeT b
                        return $ aa <|> bb


instance MonadTrans MaybeT where
    -- "herwrappen" van het argument
    lift x = MaybeT $ do r <- x
                         return $ Just r


-- Stack Manipulation
---------------------

type Stack = [Int]

-- plaats het argument bovenop de stack
push :: Int -> State Stack ()
push x = do r <- get
            put (x:r)
-- geef de grootte van de stack terug
size :: State Stack Int
size = do r <- get
          return $ length r

-- neem het eerste element van de stack, als het aanwezig is
-- (hint: hoogle naar `guard`)
pop :: MaybeT (State Stack) Int
pop = guard (True) (do (r:rs) <- get
                       put rs
                       return r)

person Gloomy    schedule 05.12.2016    source источник
comment
Какую ошибку вы получаете? guard возвращает MaybeT (State Stack) (), а вам требуется MaybeT (State Stack) Int.   -  person Lee    schedule 05.12.2016
comment
Возможно, это поможет вам stackoverflow.com/questions/27096836/   -  person cibercitizen1    schedule 04.10.2018


Ответы (3)


guard не принимает два аргумента, он принимает только Bool аргумент.

Вам также нужно поднять свои манипуляции с состоянием в MaybeT:

pop :: MaybeT (State Stack) Int
pop = do
  guard True
  (r:rs) <- lift get
  lift $ put rs
  return r
person E4z9    schedule 05.12.2016

Прежде всего, вы должны понимать, что если ваш стек пуст, ваш паттерн r:rs <- get не работает. Но вы пишете это в блоке действий, поэтому будет вызвана функция fail. Это реализовано для Monad m => MaybeT m так: fail _ = MaybeT (return Nothing). Это означает, что если шаблон терпит неудачу, он возвращает Nothing. Это то, что вы хотите.

Итак, вы можете сделать так:

pop :: MaybeT (State Stack) Int
pop = do r:rs <- get
         put rs
         return r
person freestyle    schedule 05.12.2016
comment
Это работает, если используются трансформеры MaybeT и mtl State. В этом упражнении OP использует свои собственные преобразователи, поэтому для его использования им потребуется lift как get, так и put (или реализовать MonadState) и реализовать fail для своих MaybeT. - person duplode; 05.12.2016
comment
Я не обратил на это внимания, но это уместное замечание. - person freestyle; 05.12.2016

Для сравнения, вот более грубая реализация, которая не зависит ни от guard, ни от fail:

pop :: MaybeT (State Stack) Int
pop = do
  stk <- lift get
  case stk of
    [] -> empty
    (r:rs) -> do
      lift (put rs)
      return r

Создание empty, когда стек равен [], равнозначно использованию guard так, как вы намереваетесь, или использованию fail для использования неудачного сопоставления шаблона (как в ответе фристайла).

person duplode    schedule 05.12.2016