我刚开始从this wikibook开始学习Haskell,我在其中一个练习中遇到了一些麻烦。
具体而言,以下内容无法正常工作
parseNumber :: Parser LispVal
parseNumber = (many1 digit) >>= (return $ Number . read)
除非我稍微改变
parseNumber :: Parser LispVal
parseNumber = (many1 digit) >>= (\n -> return $ Number . read $ n)
我希望有人可以解释为什么return $ Number . read
不会评估我在第二个定义中明确创建的同一个lambda函数,因为我认为这正是部分函数评估在点使用时的作用自由风格代码(显然不是!)
感谢您的帮助,希望这不是另一个初学者的monad问题...
答案 0 :(得分:4)
这只是$
如何关联的问题。从根本上说,$
只是编写较少括号的算子;它与将括号括起来添加到表达式的末尾相同。
使用这个想法,我们可以重写你的第二个例子:
parseNumber = (many1 digit) >>= (\n -> return (Number . read ( n)))
作为参考,带圆括号的原始表达式如下所示:
parseNumber = (many1 digit) >>= (return (Number . read))
因此,部分应用程序的等价物实际上是:
parseNumber = (many1 digit) >>= (\n -> (return (Number . read)) n)
基本上,将多个$
个关联组合起来的方式与预期不同。
答案 1 :(得分:3)
转到定义 -
($) :: (a -> b) -> a -> b
($) = id
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(.) f g x = f (g x)
现在你有了
return $ Number . read = ($) return (Number . read) -- (.) has higher precedence
= return (Number . read)
你所在的monad是Parser
monad,所以这是试图将解析后的值绑定到一个函数,该函数返回另一个函数的解析器(多层抽象!)
相反,你想要的是
return . Number . read
这相当于你所写的内容,你可以通过
看到\n -> return $ Number . read $ n = \n -> return . Number . read $ n -- definition of (.)
= return . Number . read -- eta reduction
最后,请注意当您看到模式时
x >>= return . f
这总是可以替换为
fmap f x -- or liftM f x
即。它表明您根本没有真正使用Monad
实例,而是使用较弱(且更通用)Functor
实例。
答案 2 :(得分:1)
看起来你想要:
parseNumber = (many1 digit) >>= (return . Number . read)
或替代地
parseNumber = (many1 digit) `fmap` (Number . read)
Number . read
是一个函数String -> LispVal
,因此return $ Number . read
的类型为Parser (String -> LispVal)
,而您需要函数类型String -> Parser LispVal