我正在使用Haskell学习函数编程,我试图通过首先理解为什么需要它们来获取概念。
我想知道函数式编程语言中箭头的目标。他们解决了什么问题?我查了http://en.wikibooks.org/wiki/Haskell/Understanding_箭和http://www.cse.chalmers.se/~rjmh/afp-arrows.pdf。我所了解的是,它们被用来描述用于计算的图形,并且它们允许更容易的无点样式编码。
这篇文章假设,无点风格通常更容易理解和写作。我觉得这很主观。在另一篇文章(http://en.wikibooks.org/wiki/Haskell/StephensArrowTutorial#Hangman:_Main_程序)中,实现了一个刽子手游戏,但我看不出箭头如何使这种实现变得自然。
我可以找到很多描述这一概念的论文,但没有任何关于动机的文章。
我错过了什么?
发布于 2011-11-09 22:48:19
我知道我来晚了,但你在这里有两个理论上的答案,我想提供一个实用的选择来仔细考虑。我作为一个相对的哈斯克尔·努布( Haskell noob )来到这里,尽管如此,他最近一直在为我目前正在从事的一个项目在Arrow的主题上进行游行。
首先,您可以有效地解决Haskell中的大多数问题,而不需要使用Arrow。一些著名的Haskellers确实不喜欢也不使用它们(有关这方面的更多信息,请参见这里、这里和这里 )。所以,如果你对自己说“嘿,我不需要这些”,明白你可能真的是对的。
当我第一次学到Arrow时,我发现最令人沮丧的是关于这个主题的教程如何不可避免地达到了电路类比的程度。如果你看一下Arrow代码--至少是含糖的代码--它与硬件防御语言毫无相似之处。你的输入在右边排列,你的输出在左边,如果你不能正确地把它们连接起来,它们就不能启动。我心里想:真的吗?这就是我们最后的归宿吗?我们是否创造了一种如此高层次的语言,它再次由铜线和焊料组成?
据我所能确定的,正确的答案是:事实上,是的。Arrow现在的杀手用例是FRP (想想Yampa,游戏,音乐和反应系统)。玻璃钢面临的问题在很大程度上是所有其他同步消息系统面临的问题:如何将连续的输入流连接到连续的输出流中,而不丢弃相关信息或出现泄漏。您可以将流建模为列表--最近几个玻璃钢系统使用了这种方法--但是当您有了大量的输入时,列表就变得几乎不可能管理了。你得把自己从水流中隔离出来。
Arrows在FRP系统中所允许的是将功能组合成一个网络,同时完全抽象出对这些函数传递的潜在值的任何引用。如果你刚开始接触FP,这可能会让你一开始感到困惑,然后当你意识到它的含义时,你会感到非常兴奋。您最近才吸收了函数可以抽象的概念,以及如何将像[(*), (+), (-)]
这样的列表理解为[(a -> a -> a)]
类型。使用Arrow,您可以进一步推进抽象一层。
这种抽象思维的能力本身也带来了危险。首先,它可以将GHC推向一个角落,在这种情况下,它不知道如何处理您的类型假设。你必须准备好在类型层次上思考--这是一个学习种类、RankNTypes和其他类似主题的极好机会。
也有一些例子,我称之为“愚蠢的箭特技”,程序员够到一些箭组合,仅仅是因为他或她想炫耀一个巧妙的技巧与元组。(这是我自己的对疯狂的微不足道的贡献。)当你在野外遇到这种热狗的时候,你可以随意忽略它。
注意:正如我上面提到的,我是一个相对的菜鸟。如果我已经公布了上述误解,请随时纠正我。
发布于 2011-10-17 21:17:50
这是一种“软”回答,我不确定是否有任何引用以这种方式表示,但这就是我所想到的箭头:
箭头类型的A b c
基本上是一个函数b -> c
,但是具有更多的结构,就像一元值M a
比普通的旧a
具有更多的结构一样。
现在,这个额外的结构恰好取决于您正在讨论的特定箭头实例。就像monads一样,IO a
和Maybe a
都有不同的附加结构。
monads的好处是无法从M a
转到a
。现在,这似乎是一个限制,但实际上它是一个特性:类型系统保护您不将一个单一的值转换为一个普通的旧值。您只能通过通过>>=
或特定monad实例的原始操作参与monad来使用该值。
同样,您从A b c
获得的是无法构造一个新的b消费c生成的“函数”。箭头保护您不使用b
和创建c
,除非参与各种箭头组合器或使用特定箭头实例的基本操作。
例如,Yampa中的信号函数大致是(Time -> a) -> (Time -> b)
,但是它们还必须遵守一定的因果关系限制:时间上的输出t
是由输入信号的过去值决定的:您不能展望未来。所以他们所做的不是用(Time -> a) -> (Time -> b)
编程,而是用SF a b
编程,然后用原语构建信号函数。碰巧的是,由于SF a b
的行为非常类似于一个函数,所以公共结构就是所谓的“箭头”。
发布于 2012-10-09 11:28:30
我喜欢把Arrow,像Monad和函子一样,看作是允许程序员对函数进行奇异的组合。
没有单数或箭头(和函子),函数语言中的函数组合仅限于将一个函数应用于另一个函数的结果。使用monad和函子,您可以定义两个函数,然后编写单独的可重用代码,指定这些函数在特定的monad上下文中如何相互交互,以及如何与传递给它们的数据交互。此代码被放置在Monad的绑定代码中。因此,monad是一个视图,只是一个可重用绑定代码的容器。函数在一个monad的上下文中与另一个monad的上下文中组成不同。
一个简单的例子是可能的monad,其中bind函数中有代码,如果函数A是由函数B组成的,并且B生成一个Nothing,那么绑定代码将确保这两个函数的组合输出一个Nothing,而不需要将A应用于从B输出的no值,如果没有monad,程序员将不得不将代码写进A中以测试没有任何输入。
Monad还意味着程序员不需要在源代码中显式地键入每个函数所需的参数--绑定函数处理参数传递。因此,使用monads,源代码看起来更像是一个静态的函数名链,而不是看起来像函数A“调用”参数C和D的函数B--代码开始感觉更像是电子电路而不是移动机器--功能更强,而不是命令式。
箭头还将函数与绑定函数连接在一起,提供可重用的功能和隐藏参数。但是Arrow本身可以连接在一起并组合在一起,并且可以在运行时将数据路由到其他Arrow。现在,您可以将数据应用到Arrow的两个路径,Arrow对数据“做不同的事情”,并重新组合结果。或者您可以根据数据中的某个值选择要传递数据的Arrow的哪个分支。结果代码甚至更像一个电子线路,有开关,延迟,集成等。程序看起来非常静态,你不应该看到太多的数据操作正在进行。需要考虑的参数越来越少,需要考虑的参数也越来越少。
编写一个箭头化的程序主要包括选择现成的箭头,例如拆分器、开关、延迟和积分器,将功能提升到这些箭头中,并将箭头连接在一起形成更大的箭头。在中,Arrow形成一个循环,将来自世界的输入与程序最后一次迭代的输出结合在一起,从而使输出响应于现实世界的输入。
现实世界的价值观之一是时间。在Yampa中,信号函数Arrow无形地通过计算机程序线程时间参数--您永远不会访问时间值,但是如果将积分器箭头连接到程序中,它将输出随时间变化的积分值,然后您可以使用这些值传递给其他箭头。
https://softwareengineering.stackexchange.com/questions/114681
复制相似问题