以相同的功能打印到控制台并更新 Monad 状态

Print to console and update Monad state in same function

我想定义一个需要 Int 的函数,根据数字 (x) 在控制台中打印错误,然后更新 StateNothing.

如何将这些命令加入一个函数中?

这是我得到的:

  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 副作用和 然后 状态更新

状态 monad 中的函数,例如 a -> State s b,是一个纯函数(无 IO),它恰好有一个额外的函数参数 s 通过一些方便的管道隐藏。

您不能从状态 monad 打印到控制台。

不过,是的!

但是!您可以使用 monad transformer 来获取 State 和一些底层 monad,例如 IO.

我将提供一个使用 transformers 而不是自定义 monad 和 mtl 的示例,因为它看起来像您正在使用的那样。使用 mtl,您可以像 MonadError 一样使用 类 来利用 throw,它可以与使用 mtl 类 的其他库一起很好地工作。另一方面,如果你是这个变压器的最终消费者,那么它就不那么重要了。

首先,我们将导入为我们提供 MonadIO、StateT、MaybeT 的模块,并使用新类型派生,这样我们就不必输入 monad 实例样板:

{-# 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)]

现在我们可以进入有趣的部分 - 用于管道的 Monad 定义和函数。 monad 栈是 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

通常您会编写一大堆函数来提供您的 monad 抽象。对于这个问题,它只是 "update the state" 和 "print to stdout":

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

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

有了我们的 Monad of Power,我们可以做一些奇特的事情,比如更新状态 and 发出 IO 消息 and 跟踪失败或成功:

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

哦,失败非常简单 - 只需在我们的 MaybeT monad 中失败:

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

我们可以按预期使用这个 monad:

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