我一直在努力理解monad的概念,并尝试使用以下示例进行实验:
我有一个Editor
数据类型,它代表文本文档的状态以及一些用于处理其的函数。
data Editor = Editor {
lines :: [Line], -- editor contents are kept line by line
lineCount :: Int, -- holds length lines at all times
caret :: Caret -- the current caret position
-- ... some more definitions
} deriving (Show)
-- get the line at the given position (first line is at 0)
lineAt :: Editor -> Int -> Line
lineAt ed n = ls !! n
where
ls = lines ed
-- get the line that the caret is currently on
currentLine :: Editor -> Line
currentLine ed = lineAt ed $ currentY ed
-- move the caret horizontally by the specified amount of characters (can not
-- go beyond the current line)
moveHorizontally :: Editor -> Int -> Editor
moveHorizontally ed n = ed { caret = newPos }
where
Caret x y = caret ed
l = currentLine ed
mx = fromIntegral (L.length l - 1)
newX = clamp 0 mx (x+n)
newPos = Caret newX y
-- ... and lots more functions to work with an Editor
所有这些功能都作用于一个 Editor
,其中许多返回一个新的 Editor
(其中光标已移动或某些文本已更改),因此我认为这可能是使用 State
monad 的一个好应用,并且我已经重新编写了大多数 Editor
函数,现在看起来像这样:
lineAt' :: Int -> State Editor Line
lineAt' n = state $ \ed -> (lines ed !! n, ed)
currentLine' :: State Editor Line
currentLine' = do
y <- currentY'
lineAt' y
moveHorizontally' :: Int -> State Editor ()
moveHorizontally' n = do
(Caret x y) <- gets caret
l <- currentLine'
let mx = fromIntegral (L.length l - 1)
let newX = clamp 0 mx (x+n)
modify (\ed -> ed { caret = Caret newX y })
moveHorizontally' :: Int -> State Editor ()
moveHorizontally' n = do
(Caret x y) <- gets caret
l <- currentLine'
let mx = fromIntegral (L.length l - 1)
let newX = clamp 0 mx (x+n)
modify (\ed -> ed { caret = Caret newX y })
这样做很棒,因为它让我可以在
do
-notation中轻松地组合编辑操作。然而,现在我正在努力将其应用到实际应用程序中。假设我想在执行某些IO的应用程序中使用此
Editor
。假设我想每次用户按下键盘上的l
键时操纵一个Editor
实例。我需要另一个代表整个应用程序状态的
State
monad,它包含一个Editor
实例和一种类似事件循环的方法,使用IO
monad读取键盘并调用moveHorizontally'
通过修改其Editor
来修改当前AppState。我已经阅读了有关这个主题的一些内容,并且似乎需要使用Monad Transformers来构建具有IO底部的monad堆栈。我以前从未使用过Monad Transformers,也不知道接下来该怎么做?我还发现
State
monad已经实现了一些功能(它似乎是Monad Transformer的特殊情况?),但我对如何使用它感到困惑?