我是haskell的新手,我必须编写一个程序上下文感知,所以我认为我可以使用Reader Monad来保持从文件中读取上下文,我知道如何读取将内容放在列表中的文件像[([Char],[Char])]这样的tuplessomething,但我不知道如何实现Reader Monad,使环境可用于我的程序的所有组件而不使用命令式样式,特别是我不知道如何设置和使用环境,据我所知,我应该将它作为参数提供给需要环境的所有函数使用runReader函数env,但我很困惑,有人可以给我一些指示或一个很好的教程吗?提前谢谢
答案 0 :(得分:8)
使用任何“普通”monad [0] 的基本方案完全相同。基本上:
do
表示法编写返回monadic类型值的函数,就像您编写IO
函数main
一样。<-
绑定相同 monad中的值以获取值“inside”,从而导致其他值“运行”。let
绑定任何其他值,使其独立于monadic结构。这样做,monad描述的额外功能的所有杂乱细节(在这种情况下,传递额外的环境参数)都会自动处理。
现在,通常的Reader操作是ask
和local
:
ask
是保持环境的一元价值;在do
区块中,您使用它的方式与getLine
monad中使用IO
的方式相同。local
接受一个在Reader monad中提供新环境和计算的函数,在前者修改的环境中运行后者,然后获取结果并将其放入当前函数。换句话说,它使用本地修改的environemnt运行子计算。“run”函数是创造性命名的runReader
,它只是在Reader monad和环境值中进行计算,使用后者运行前者,并在monad之外返回最终结果。
作为一个例子,这里有一些函数在Reader monad中进行一些无意义的计算,其中环境是一个“最大值”,表示何时停止:
import Control.Monad.Reader
computeUpToMax :: (Int -> Int) -> Int -> Reader Int [Maybe Int]
computeUpToMax f x = do
maxVal <- ask
let y = f x
if y > maxVal
then return []
else do zs <- local (subtract y) (computeUpToMax f y)
z <- frob y
return (z:zs)
frob :: Int -> Reader Int (Maybe Int)
frob y = do
maxVal <- ask
let z = maxVal - y
if z == y
then return Nothing
else return $ Just z
要运行它,你可以使用这样的东西:
> runReader (computeUpToMax (+ 1) 0) 9
[Just 8, Just 6, Nothing]
...其中9
是初始环境。
几乎完全相同的结构可以与其他monad一起使用,例如State
,Maybe
或[]
,但在后两种情况下,您通常只使用最终结果monadic结果值而不是使用“run”函数。
[0] :正常意味着不涉及编译器魔法,最明显的“异常”monad当然是IO
。
答案 1 :(得分:5)
我认为如果你看看如何在不使用Reader的情况下解决这个问题,然后比较翻译版本,这是最简单的。这是我正在处理的程序的精简示例,其中环境是一组用于更新显示的回调函数。它稍微复杂一点,因为它使用ReaderT而不是Reader,但一切都以基本相同的方式工作。
runProcess :: Env -> State -> Action -> IO State
runProcess env state action = do
newstate <- processAction state action
let ufunc = mainUFunc env -- get the callback to update the display
ufunc newstate -- update the display
return newstate
现在我将更改它以使用Reader monad传递环境。由于代码已经在IO中,因此必须使用monad转换器版本ReaderT
。
runProcessR :: State -> Action -> ReaderT Env IO State
runProcessR state action = do
newstate <- lift $ processAction state action
env <- ask -- get the environment from the reader
liftIO $ (mainUFunc env) newstate -- updating is in IO; it needs to be lifted
return newstate
此时,程序的主循环基本上是:
loop :: State -> ReaderT Env IO ()
loop = do
action <- liftIO getAction
if action == EndLoop
then return ()
else do
st' <- processActionR st action
loop st'
mainLoop :: IO ()
mainLoop = do
env <- setUpCallbacks
let st = initState
runReaderT $ loop st
这就是你如何使用Reader。以前用于获取环境参数的每个函数都不再需要。不采取环境的功能可以直接使用,如果它们是monadic,则可以解除。
答案 2 :(得分:1)
这是恕我直言最佳monad资源 - All About Monads,这里是Reader monad的一部分。