使用StateT进行解析的正确方法

时间:2014-08-11 09:52:48

标签: haskell

考虑以下错误的解析器编写为StateT

type Parser a = StateT String Maybe a -- Maybe is chosen arbitrarily here.

oneDigit :: Parser Int
oneDigit = do
        (x : _) <- get
        return (read x :: Int)

它抛出以下错误:

No instance for (MonadState [String] (StateT String Maybe))

然而,当我改为oneChar :: Parser Char时,这种情况并没有发生。

我的结论是,如果您的州为Parser a,则只能定义m a

现在我的问题是,我如何放宽这种耦合要求?

2 个答案:

答案 0 :(得分:6)

您的州是String,如果您(x : _) <- get,则xChar。您无法在read上使用Char。打印错误是由于类型推断反过来起作用:从read x编译器推断xString,这意味着您的状态为[String]Parser a不适用。具体来说,错误表明(StateT String Maybe)不是MonadState [String]的实例,这是真的,因为它是MonadState String的实例。你可以从GHCI看到它:

> import Control.Monad.State
> :i StateT
...
instance Monad m => MonadState s (StateT s m)

您案件中的s == String

现在,如果您想将字符串中的下一个Char读取为数字(0到9之间的数字),则需要将read替换为digitToInt来自{{ 1}}:

Data.Char

答案 1 :(得分:3)

类型错误试图告诉您,您的状态需要是字符串列表而不是单个字符串,因为您使用

(x : _) <- get

获取列表的第一个元素,然后使用read(期望String)读取它。

由于您只想解析单个数字,因此可以将功能更改为

oneDigit :: Parser Int
oneDigit = do
        (x : _) <- get
        return (read [x] :: Int)

[x]从单个字符创建一个字符串(因为String只是[Char]的别名)。

请注意,您可能不希望在解析器实现中使用read,因为它会因无效输入而崩溃。 reads是一个更好的替代方案,可以让您处理错误案例。例如:

oneDigit :: Parser Int
oneDigit = do
    (x : xs) <- get
    case reads [x] of
        [(n, "")] -> put xs >> return n
        _         -> lift Nothing

这也将put字符串的其余部分返回到状态,以便解析器在成功时实际消耗输入。