Вывести на консоль и обновить состояние Monad в той же функции - PullRequest
0 голосов
/ 15 декабря 2018

Я хочу определить функцию, которая ожидает Int, печатает ошибку в консоли в зависимости от номера ( x ) и затем обновляет State с помощью Nothing.

Как мне объединить эти команды в одной функции?

Вот что я получил:

  type Env = [(Variable,Int)]
  newtype StateError a = StateError { runStateError :: Env -> Maybe (a, Env) }
  class Monad m => MonadError m where
    throw :: Monad m => a -> m a

  instance MonadError StateError where
    throw x = StateError (\s -> Nothing)

Но я не могу понять, как выполнить побочный эффект IOи затем Состояние обновления в той же функции определения

1 Ответ

0 голосов
/ 15 декабря 2018

Нет

Функция в монаде состояния, такая как a -> State s b, - это чистая функция (без ввода-вывода), в которой есть дополнительный аргумент функции s, хотя она скрытанемного удобной сантехники.

Вы не можете печатать на консоли из монады состояния.

Однако, да!

Однако!Вы можете использовать монаду преобразователь , чтобы получить как State, так и некоторую базовую монаду, такую ​​как IO.

Я приведу пример использования transformers вместо пользовательской монады и mtl в качествеПохоже, вы использовали.С mtl вы можете использовать классы типа MonadError для использования throw, который хорошо работает с другими библиотеками, которые используют классы mtl.С другой стороны, если вы являетесь конечным потребителем этого преобразователя, это менее важно.

Сначала мы импортируем модули, которые дают нам MonadIO, StateT, MaybeT, и используем вывод новых типов, поэтому мы не будемнужно набрать шаблон экземпляра монады:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import qualified Control.Monad.Trans.State as S
import Control.Monad.IO.Class
import Control.Monad.Trans.Maybe
import Control.Monad.Trans

Просто чтобы завершить, мы изложим типы, полезные для вашей абстракции:

type Variable = String
type Env = [(Variable,Int)]

Теперь мы можем перейти к интересномучасть - определение монады и функции для сантехники.Стек монады - StateT MaybeT IO:

newtype StateError a = StateError { unStateError :: S.StateT Env (MaybeT IO) a }
    deriving (Monad, Applicative, Functor)

И мы можем запустить его, сначала развернув новый тип, затем запустив состояние, и, наконец, MaybeT:

run :: StateError a -> IO (Maybe (a, Env))
run = runMaybeT . flip S.runStateT [] . unStateError

Обычно вы 'Я напишу армию функций, которые обеспечат вашу абстракцию монады.Для этого вопроса это просто «обновить состояние» и «напечатать на стандартный вывод»:

modify :: (Env -> Env) -> StateError ()
modify = StateError . S.modify

emit :: Show a => a -> StateError ()
emit = StateError . liftIO . print . show

Вооружившись нашей Монадой Силы, мы можем сделать такие причудливые вещи, как состояние обновления и emit IOсообщения и отслеживают сбой или успех:

updateAndPrint :: Variable -> Int -> StateError ()
updateAndPrint v i =
  do emit (v,i)
     modify ((v,i):)

О, и сбой довольно прост - просто потерпите неудачу в нашей MaybeT монаде:

throw :: a -> StateError b
throw _ = fail ""  -- same as 'MaybeT (pure Nothing)'

Мы можемиспользуйте эту монаду как положено:

> run $ updateAndPrint "var" 1
"(\"var\",1)"
Just (()             -- ^ return value of `updateAndPrint`
     ,[("var",1)])   -- ^ resulting state
...