函数编程中“无点”样式的优缺点是什么?

| 我知道在某些语言中(Haskell?),努力是为了实现无点样式,或者永远不要通过名称显式地引用函数参数。对于我来说,这是一个很难理解的概念,但它可能有助于我理解该样式的优点(甚至是缺点)。谁能解释?     
已邀请:
我相信这样做的目的是简洁明了,并将流水线计算表达为功能的组合,而不是考虑通过线程进行参数传递。简单示例(在F#中)-给出:
let sum = List.sum
let sqr = List.map (fun x -> x * x)
像这样使用:
> sum [3;4;5]
12
> sqr [3;4;5]
[9;16;25]
我们可以将“平方和”函数表示为:
let sumsqr x = sum (sqr x)
并使用像:
> sumsqr [3;4;5]
50
或者我们可以通过以下方式定义x:
let sumsqr x = x |> sqr |> sum
这样写,很明显x只是通过一系列函数传递给“线程化”。直接合成看起来更好:
let sumsqr = sqr >> sum
这更加简洁,这是我们正在做的事情的另一种思考方式。组成函数,而不是想象经过的论证过程。我们没有描述ѭ6的工作原理。我们正在描述它是什么。 PS:了解合成的一种有趣方法是尝试使用诸如Forth,Joy,Factor等连接语言进行编程。这些可以被认为只是合成(Forth
: sumsqr sqr sum ;
),其中单词之间的间隔是合成运算符。 PPS:也许其他人可以对性能差异发表评论。在我看来,通过使编译器更清楚地知道,无需像流水线一样产生中间值,合成可以降低GC压力。帮助解决所谓的“森林砍伐”问题。     
一些作者认为无点风格是最终的功能编程风格。简而言之,类型为“ 8”的函数描述了从类型为“ 9”的一个元素到另一类型为“ 10”的元素的转换。这个想法是\“ pointpoint \”函数(使用显式变量编写)强调元素(当您编写
\\x -> ... x ...
时,您正在描述元素
x
发生了什么),而\“ point-free \”函数(在不使用变量的情况下表示),强调转换本身,是由更简单的转换组成的。无点风格的拥护者认为,转换确实应该是中心概念,并且有指向性的符号虽然易于使用,但使我们偏离了这一崇高的理想。 无点功能编程已经有很长时间了。自1924年MosesSchönfinkel进行开创性工作以来,逻辑学家就已经知道了组合逻辑,这已经成为罗伯特·费斯和哈斯克尔·库里在1950年代首次进行ML类型推理的基础。 从一组表达能力强的基本组合器构建函数的想法非常吸引人,并且已应用于各个领域,例如从APL派生的数组操作语言或解析器组合器库(例如Haskell的Parsec)。 John Backus是无点编程的著名提倡者。他在1978年的演讲“可以从冯·诺伊曼风格中解放编程吗?”中写道:   Lambda表达式(及其替换规则)能够   定义所有可能类型的所有可能的可计算函数   以及任何数量的参数。这种自由和力量有其   缺点及其明显的优点。类似   常规的无限制控制语句的功能   语言:无限自由带来混乱。如果一个   不断发明新的组合形式以适应场合,例如   一个人可以在lambda演算中,不会熟悉   几种组合形式的样式或有用属性   足以满足所有目的。就像结构化编程一样   避开许多控制语句以获得更简单的程序   结构,更好的性能以及统一的方法   了解他们的行为,因此避免使用函数式编程   Lambda表达式,替换和多功能   类型。从而实现熟悉的程序   具有已知有用特性的功能形式。这些程序是   如此结构化,经常可以理解他们的行为,   通过机械使用与那些相似的代数技术得到证明   用于解决高中代数问题。 所以他们在这里。无点编程的主要优点是,它们强制使用结构化的组合器样式,从而使方程式推理变得自然。方程式推理在“ Squiggol”运动的拥护者中得到了特别的宣传(参见[1] [2]),的确使用了相当多的无点组合器和计算/重写/推理规则。 [1]“鸟食主义形式主义导论”,杰里米·吉本斯,1994年 [2]“使用香蕉,透镜,信封和铁丝网进行功能编程”,Erik Meijer,Maarten Fokkinga和Ross Paterson,1991年 最后,无点编程在Haskellites中流行的一个原因是它与类别理论的关系。在范畴论中,态射(可以看作“对象之间的变换”)是研究和计算的基本对象。尽管部分结果可以使特定类别的推理以点式进行,但是构建,检查和操作箭头的常用方法仍然是无点式,其他语法(例如字符串图)也表现出这种“无点式”。提倡“编程代数”方法的人们与编程中的类别用户之间存在相当紧密的联系(例如,香蕉论文[2]的作者是/是顽固的分类家)。 您可能对Haskell Wiki的Pointfree页面感兴趣。 无点样式的缺点非常明显:阅读起来可能真是痛苦。尽管存在许多阴影,alpha等效等恐怖现象,我们仍然喜欢使用变量的原因是,它是一种阅读和思考非常自然的表示法。一般的想法是,复杂的函数(使用透明引用语言)就像复杂的管道系统:输入是参数,它们进入一些管道,应用于内部函数,重复(
\\x -> (x,x)
)或被遗忘(
\\x -> ()
,管道等等。而且变量符号在所有机器上都很好地隐含:您为输入指定一个名称,并在输出上指定一个名称(或辅助计算),但不必描述所有的管道设计方案,小管子就不会成为大管子的障碍,等等。短至ѭ15something的东西里的管道量真是惊人。您可以单独跟踪每个变量,也可以读取每个中间管道节点,但是您不必一起查看整个机器。当您使用无点样式时,它是非常明确的,您必须将所有内容记下来,然后再进行查看,有时这很丑陋。 PS:这种深入的愿景与堆栈编程语言密切相关,而堆栈编程语言可能是使用程度最低(很少)的编程语言。我建议尝试在其中进行一些编程,以便获得它的感觉(就像我建议逻辑编程一样)。参见因素,猫或尊贵的Forth。     
虽然我被无点概念所吸引,并在某些事情上使用了它,并同意前面提到的所有积极观点,但我发现这些观点都是消极的(上面有一些详细介绍): 较短的表示法减少了冗余;在高度结构化的组合中(ramda.js样式,或者在Haskell中是无点的,或任何连接语言),代码读取比线性扫描一堆
const
绑定并使用符号突出显示以查看哪个绑定进入什么要复杂得多。其他下游计算。除了树形结构与线性结构之外,描述性符号名称的丢失使该函数难以直观地掌握。当然,树形结构和命名绑定的丢失也具有很多优点,例如,函数会感觉更通用-不会通过选择的符号名称绑定到某些应用程序域-而且树形结构甚至在语义上都存在如果绑定已布置,并且可以顺序理解(lisp let / let *样式)。 仅通过管道传递或组合一系列功能时,无点是最简单的,因为这还会导致我们人类易于遵循的线性结构。但是,通过多个接收者进行一些临时计算很繁琐。元组有各种各样的包装,镜头和其他艰苦的机制只是使一些计算变得可访问,否则将只是多次使用某些值绑定。当然,可以将重复部分作为单独的函数提取出来,也许这还是个好主意,但是对于某些非短函数也有参数,即使将其提取出来,其参数也必须是某种方式在两个应用程序中进行线程连接,然后可能需要记住该函数以不实际重复计算。人们会大量使用
converge
lens
memoize
useWidth
等。 特定于JavaScript:更难随便进行调试。借助
let
绑定的线性流,可以在任何地方轻松添加断点。对于无点样式,即使以某种方式添加了断点,也很难读取值流,例如。您不仅可以在开发控制台中查询或悬停在某个变量上。另外,由于无点不是JS的本机,所以ramda.js或类似的库函数会使堆栈变得相当模糊,尤其是在专心致意的情况下。 代码易碎性,特别是在非平凡的大小系统和生产中。如果提出了新的要求,那么上述弊端就会发挥作用(例如,很难为下一个维护者阅读代码,而维护者可能自己就是几周之后,也很难跟踪数据流以进行检查)。但是最重​​要的是,即使是看起来很小且无害的新要求,也可能需要完全不同的代码结构。可以说这是一件好事,因为它可以清晰地表示新事物,但是重写大量的无点代码非常耗时,因此我们没有提到测试。因此,可以感觉到,基于松散,结构化,基于词汇分配的编码可以更快地重新利用。特别是如果编码是探索性的,并且在具有奇怪约定(时间等)的人类数据领域中,很少能准确地100%捕获,并且可能始终存在要求更准确或更准确地处理某些内容的需求客户,无论哪种方法都能更快地解决问题。     

要回复问题请先登录注册