Monadic型混乱

时间:2011-03-16 02:52:44

标签: haskell types monads parsec

我正在经历Write Yourself a Scheme in Haskell。这是一个很棒的教程,但是我遇到了一个parsing exercises

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

使用以下方法重写parseNumber:

  1. DO-符号
  2. 使用>> =运算符
  3. 进行显式排序

    我没有问号:

    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. 为什么我会遇到类型错误?我误解了monadic bind运算符吗?
    2. 为什么我的口号解决方案没有出现类似的类型错误?
    3. 我觉得我错过了关于类型的基本概念?

2 个答案:

答案 0 :(得分:11)

你正在尝试从do-notation到绑定表示法的非平凡转换,我建议以“琐碎”的方式进行,然后使其成为无点。

回想:

 x <- m    === m >>= \x ->
 let x = e === let x = e in

然后你有:

 parseNumber = many1 digit >>= \x ->
               let y = read x in
               return (Number y)

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

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

 parseNumber = many1 digit >>= \x -> return (Number (read x))
             = many1 digit >>= return . Number . read

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

 parseNumber = liftM (Number . read) (many1 digit)

答案 1 :(得分: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 - 这应该是正确的类型并且可能提供你想要的...