6

我正在通过在 Haskell 中为自己编写一个计划。它是一个很棒的教程,但我遇到了一个解析练习

parseNumber :: Parser LispVal
parseNumber = liftM (Number . read) $ many1 digit

使用以下方法重写 parseNumber:

  1. 做记号
  2. 使用 >>= 运算符的显式排序

我对 do-notation 没有任何问题:

parseNumber :: Parser LispVal
parseNumber = do x <- many1 digit 
                 let y = read x
                 return $ Number y

对于#2,我尝试了很多变体,例如:

parseNumber :: Parser LispVal
parseNumber = (many1 digit) >>= (liftM (Number . read))

但我一直遇到类型错误。我有两个问题。

  1. 为什么我会收到类型错误?我误解了单子绑定运算符吗?
  2. 为什么我的 do-notation 解决方案没有出现类似的类型错误?

我觉得我缺少关于类型的基本概念?

4

2 回答 2

11

您正在尝试从 do-notation 到 bind notation 的非平凡转换,我建议以“平凡”的方式进行,然后使其无积分。

记起:

x <- m === m >>= \x ->
 让 x = e === 让 x = e 在

然后你有:

parseNumber = many1 digit >>= \x ->
               让 y = 读入 x
               返回(数字 y)

(我已删除$以避免优先级问题。)

然后我们可以将其转换为:

parseNumber = many1 digit >>= \x -> return (Number (read x))
             = many1 digit >>= return . 数字 。读

现在,如果你想使用liftM,你需要停止使用 bind,因为提升的函数需要一个单子值作为它的参数。

parseNumber = liftM (Number . read) (many1 digit)
于 2011-03-16T03:03:40.043 回答
2

在您的情况下, bind 具有类型:

(>>=) :: Parser a -> (a -> Parser b) -> Parser b

(因为您使用Parser的是 Monad)

你给 bind 两个参数:第一个,many1 digit, 应该没问题(关于类型);但是第二个参数的类型是 的结果类型liftM,即Parser a -> Parser b这不符合第二个参数的预期类型(a -> Parser b)

没有测试它:而不是使用liftM (Number.read)作为绑定的第二个参数,尝试使用return . Number . read- 这应该有正确的类型并且可能给出你想要的......

于 2011-03-16T08:31:33.360 回答