我正在阅读Real World Haskell,作为仿函数/ monad的介绍,它给出了以下示例:
parseByte :: Parse Word8
parseByte =
getState ==> \initState ->
case L.uncons (string initState) of
Nothing ->
bail "no more input"
Just (byte,remainder) ->
putState newState ==> \_ ->
identity byte
where newState = initState { string = remainder,
offset = newOffset }
newOffset = offset initState + 1
Run Code Online (Sandbox Code Playgroud)
(其余部分可以在页面的四分之一处阅读:http://book.realworldhaskell.org/read/code-case-study-parsing-a-binary-data-format.html)
对我没有任何意义的是,为什么这个功能不带任何参数?我希望它接受包含要解析的文本的Parse对象,然后返回解析的文本和新的Parse对象.相反,(我认为)它"神奇地"访问Parser,弹出一个字节,然后返回一个"修改过的"Parser.对象来自哪里?我一直盯着看了一天,仍然不知道这个功能是如何工作的.
这里的任何指导将不胜感激.
该Parse类型定义为
newtype Parse a = Parse
{ runParse :: ParseState -> Either String (a, ParseState)
}
Run Code Online (Sandbox Code Playgroud)
因此,如果您想知道输入来自哪里,那就是类型的定义!每个Parse值都包含一个函数,然后我们==>在这个例子中使用Parse通过组合将两个s链接到一个新的Parse.然后最终使用runParse.此函数需要a ParseState,被定义为
data ParseState = ParseState
{ string :: L.ByteString
, offset :: Int64
} deriving (Show)
Run Code Online (Sandbox Code Playgroud)
这是携带正在解析的字符串的内容.
您可以将Parse类型视为该类型的别名
ParseState -> Either String (a, ParseState)
Run Code Online (Sandbox Code Playgroud)
这是一个你期望的功能.使用==>具有类型的函数(删除newtype包装器)
(==>)
:: (ParseState -> Either String (a, ParseState))
-> (a -> (ParseState -> Either String (b, ParseState)))
-> (ParseState -> Either String (b, ParseState))
Run Code Online (Sandbox Code Playgroud)
然后我们可以拿一个Parse并将它喂给另一个Parse来制作一个新的Parse.所有这些只不过是常规功能组合的花哨包装.它runParse从初始状态调用where-ever得到输入.