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副作用,然后在同一个函数定义中更新状态
没有
状态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)