打印到控制台并在同一功能中更新Monad状态

vic*_*.ja -1 monads haskell functional-programming

我想定义一个期望的函数,Int根据数字(x)在控制台中输出错误,然后更新Statewith 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)
Run Code Online (Sandbox Code Playgroud)

但我无法弄清楚如何执行IO副作用,然后在同一个函数定义中更新状态

Tho*_*son 6

没有

状态monad中的一个函数,例如a -> State s b,是一个纯函数(没有IO)碰巧有一个额外的函数参数s隐藏了一些方便的管道.

您无法从状态monad打印到控制台.

但是,是的!

然而!您可以使用monad 转换器来获取State和一些底层monad,例如IO.

我将提供一个使用transformers而不是自定义monad 的示例,mtl因为它似乎正在使用.有了mtl你可以使用类喜欢MonadError利用一个throw与使用该MTL类其他库效果很好.另一方面,如果您是这种变压器的最终消费者,那么它就不那么重要了.

首先,我们将导入给我们MonadIO,StateT,MaybeT的模块,并使用newtype派生,这样我们就不必输入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
Run Code Online (Sandbox Code Playgroud)

为了完整,我们将详细说明对抽象有用的类型:

type Variable = String
type Env = [(Variable,Int)]
Run Code Online (Sandbox Code Playgroud)

现在我们可以看到有趣的部分 - Monad定义和管道功能.monad堆栈是StateT MaybeT IO:

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

我们可以先打开newtype,然后运行状态,最后是MaybeT来运行它:

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

通常你会写一大堆函数来提供你的monad抽象.对于这个问题,它只是"更新状态"和"打印到标准输出":

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

emit :: Show a => a -> StateError ()
emit = StateError . liftIO . print . show
Run Code Online (Sandbox Code Playgroud)

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

updateAndPrint :: Variable -> Int -> StateError ()
updateAndPrint v i =
  do emit (v,i)
     modify ((v,i):)
Run Code Online (Sandbox Code Playgroud)

哦,失败很简单 - 只是在我们的MaybeTmonad 失败:

throw :: a -> StateError b
throw _ = fail ""  -- same as 'MaybeT (pure Nothing)'
Run Code Online (Sandbox Code Playgroud)

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

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