【问题标题】:Partial Function Application in HaskellHaskell 中的偏函数应用
【发布时间】:2013-12-21 13:14:32
【问题描述】:

我刚开始从 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 函数,因为我认为这正是部分函数计算在无点样式中使用时所做的事情代码(显然不是!)

感谢您的帮助,希望这不是另一个初学者的单子问题...

【问题讨论】:

  • return . Number . read 应该可以工作。

标签: haskell


【解决方案1】:

这只是$ 关联的问题。从根本上说,$ 只是一个减少括号的运算符;这与在表达式末尾添加括号包装相同。

使用这个想法,我们可以重写你的第二个例子:

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

作为参考,带括号的原始表达式如下所示:

parseNumber = (many1 digit) >>= (return (Number . read))

所以等价的部分应用其实是:

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

基本上,组合多个$ 关联的方式与您的预期不同。

【讨论】:

  • 感谢您的回答,虽然我有点理解 $ 的关联方式 - 我只是想象它在最后留下了某种孔/占位符,现在看起来很愚蠢。当我在方案中想象这个(如果它有部分应用)我会考虑((+) 1 2) 好的,因为+ 应用于没有参数本身所以你得到(+ 1 2) 但当然括号真的意味着+(1, 2) 所以那就是可能是我哪里出错了
【解决方案2】:

转到定义--

($) :: (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 实例。

【讨论】:

  • 一个彻底的答案,谢谢,但你并没有最快地到达那里。就像我在接受的答案中解释的那样,问题是我想象在参数上留下了一个空白(但在函数式语言中,这只是愚蠢的,因为返回的唯一参数可能是函数)所以接受的答案更有帮助
【解决方案3】:

看起来像你想要的:

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

【讨论】:

  • 谢谢,这解决了我的问题,但我认为它没有最好地回答我的问题(它仍然很有帮助,因为我根本没有看到 Parser (a -> b)a-> Parser b 之间的区别)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-01
  • 1970-01-01
  • 2010-09-25
  • 2018-04-21
  • 1970-01-01
  • 1970-01-01
  • 2014-04-29
相关资源
最近更新 更多