Haskell中的部分函数应用

时间:2013-12-21 13:14:33

标签: haskell

我刚开始从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问题...

3 个答案:

答案 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