【问题标题】:Distinctive traits of the functional languages函数式语言的显着特征
【发布时间】:2010-10-03 21:07:20
【问题描述】:

众所周知,所有函数式语言都具有一些基本属性,例如使用函数作为程序的基本构建块,其后果是使用递归而不是迭代。但是,也存在一些根本性差异。 Lisp 对 Lisp 代码和数据使用单一表示,而 ML 没有 ML 代码的标准表示。 Erlang 有一个内置的基于actor的并发。 Haskell 有单子。 Haskell 在静态类型系统中区分了纯函数和不纯函数;机器学习没有。

其他函数式语言(Clojure、F#、Arc 等)之间有哪些显着的根本区别?我所说的基础是指影响你使用这种语言开发方式的东西,,例如,它是否与一些广泛使用的运行时集成。

【问题讨论】:

  • 大多数函数式语言确实区分代码和数据。 Lisp 在这方面是非典型的。
  • 我的错,看的不够深入,对 Lisp 和 Erlang 做出了这个错误的结论。
  • 我不清楚问题是什么。你在问吗? “是什么让 F# 与 Clojure 不同”,或者你在问什么?您是否正在寻找有关特定语言或一般 FP 语言的信息?
  • 是的,就像“F# 与 Clojure 的不同之处”。例如。 Haskell 通过拥有 monad 与 Lisp 区分开来。
  • Monad 与众不同,但我认为它们是更基本特征的副作用:monad 在惰性、纯语言中变得有用,并且需要高阶数据类型。

标签: haskell f# functional-programming erlang clojure


【解决方案1】:

在我的头顶:

  • lazy vs. eager(又名 non-strict vs. strictcall-by-need vs. call-by-value em>):函数参数是在函数应用之前、之后还是从不评估?
  • pure vs. impure: 语言允许函数有副作用吗?它有可变引用吗?
  • static vs. dynamic:语言是在编译时还是运行时检查类型?
  • algebraic datatypes:该语言是否支持对变体类型进行模式匹配?
  • metaprogramming:语言是否提供了强大的代码生成系统?
  • concurrencyparallelism:线程/进程是一流的抽象吗?该语言是否可以轻松地同时运行多个计算?
  • “exotic”类型:静态类型系统的表现力如何? GADT?依赖类型?线性类型? F系统?

只有前两项是函数式语言真正独有的(即,几乎所有命令式语言都是急切且不纯的)。

【讨论】:

  • 不要在“静态/动态”类型中加入“强/弱”,让它成为一个重点。
  • 强与弱的定义不明确。我认为静态强而动态弱。 YMMV。
  • 静态类型只意味着类型在编译时确定,动态类型意味着类型在运行时确定。强类型意味着类型不匹配总是错误,弱类型允许隐式转换。这些区别是正交的。例如,C 具有弱静态类型,Common Lisp 具有强动态类型,Perl 具有弱动态类型,Haskell 具有强静态类型。
  • @Svante 我已经修正了答案中的术语——一旦有人批准它就会出现。
【解决方案2】:

我喜欢 Chris Conway 的回答,其中陈述了一些有助于对不同函数式语言进行分类的重要轴。

在特定语言的功能方面,我会选择 F# 来调出许多其他 FPL 中没有的一些功能:

  • 活动模式:许多 FPL 具有代数数据类型和模式匹配,但称为“活动模式”的 F# 功能允许您定义新模式,允许您对任意数据使用模式匹配语法。
  • 计算表达式:F# 为编写单子代码提供了一些漂亮的语法糖;虽然类型系统不能表达更高种类的多态性(没有对类型构造函数的抽象)所以你不能为任意的 monad M 编写代码,你可以为固定的 monad 编写的代码非常酷,人们在seq{} 或 async{} monad。
  • 引文:通常的“元编程的代码作为数据”位,虽然 F# 具有富有表现力的静态类型系统和丰富的语法,但我不确定有多少非 lisps 可以做到这一点。

就一般分类而言,F#是

  • eager(严格,按值调用;但 'lazy' 是一个关键字和库,使用 seq/IEnumerable 来处理一些惰性是一种常见的策略)
  • 不纯的(尽管语法会使您偏向于更纯粹的默认样式)
  • 静态(带有类型推断,所以 F# 经常“感觉像脚本”,只有类型安全)

您的问题的措辞明显偏向于一些额外的语言语用学(例如,它与什么运行时集成),但您还问什么“影响了您的开发方式”,而这些事情确实会影响:

  • Visual Studio 集成意味着出色的编辑体验(例如 Intellisense)
  • Visual Studio 集成意味着出色的调试体验(例如断点/跟踪点、本地变量、即时窗口……)
  • REPL 用于脚本或 UI-on-the-fly 是热门(fsi.exe 命令行,或集成在 VS 中的“F# Interactive”)
  • .NET 集成意味着对于大多数“X”来说,已经有一个库可以做到这一点
  • FsLex/FsYacc 等辅助工具,以及与 MSBuild 的集成,让“构建系统”变得简单

(我认为尝试将一种语言与其运行时和工具区分开来主要是一种学术练习。)

因此,我很喜欢一种特定语言的许多独特特征的描述。我希望其他人可以发布类似的答案,以突出其他语言的独特特征。

【讨论】:

  • Objective Caml 和 Haskell 都有非常有趣的准引用机制。
  • Scala 对自定义模式匹配有很好的支持。 Scala、Clojure 和 Haskell 分别拥有构建系统 sbt、Leiningen 和 Cabal。 MetaOCaml 和 Template Haskell 支持元编程。除了 Visual Studio 集成之外,F# 的功能一点也不独特。
【解决方案3】:
  1. 非严格与严格评估。

  2. 静态与动态类型。

  3. 结构与名义静态类型。 OCaml 是我唯一可以使用的语言 考虑结构类型(在对象和多态变体中), 这与动态缩小了差距 通过消除需要打字 定义许多类型(例如变体 类型)。

  4. Hindley-Milner 导数与其他静态类型推理算法。 SML、OCaml、Haskell 和 F# 使用基于 Hindley-Milner 的类型推断算法,而 Scala 只有本地类型推断(如 C# 3)并且需要更多注释才能编译。 (Haskell 代码在函数级别通常充满了类型注释,但大多数都是不必要的,并且是为了文档和在出现错误时帮助编译器而添加的。

  5. 模式匹配与手动 解构。 SML、OCaml、F#、 Haskell,数学和方案 自动解构 价值。

  6. 封闭总和类型与仅开放总和类型。 SML、OCaml、F# 和 Haskell 允许定义封闭/密封代数类型,以通过隐式传达更具体的约束来加强静态类型。 OCaml 和 F# 也允许开和类型,而 SML 不允许,而 Haskell 需要精心设计的解决方法(由 Oleg Kiselyov 描述)。

  7. 限时模式。模式匹配在 SML 和(普通)OCaml 中非常快,但在 F# 中由于活动模式甚至 Mathematica 中未知的渐近复杂性而具有未知的性能。

  8. 动态编译为本机 代码。 F#、Lisp 和 Scheme 允许代码 生成、编译和 在运行时高效执行。

  9. 宏。 OCaml、Mathematica、Lisp 和 Scheme 是可扩展的语言。

  10. 标准化与专有。 SML、Haskell 2010、Common Lisp 和 Scheme 是标准化语言,而 OCaml、Erlang、F# 和 Mathematica 是专有语言。

【讨论】:

  • 嗯? OCaml 和 Erlang 是专有的?
  • 我认为 Jon 的意思是“没有发布的标准”,在他们的实现中不是专有的。
  • “专有”不等于“未标准化”。这些是正交属性。许多开源语言是非专有的(根据定义),但没有发布的标准。您还可以拥有已发布标准的专有语言(.Net 上的 C# 就是一个突出的例子)
【解决方案4】:

有许多不同之处,但我认为只有两个不同是根本性的,因为它们对您的发展有很大的影响:

  1. 具有代数数据类型和类型推断的动态类型与静态、多态类型系统。静态类型系统在一定程度上限制了代码,但有很多优点:
    • 类型是编译器检查的文档。
    • 类型系统可帮助您选择接下来要编写的代码,当您不确定要编写什么代码时,类型系统可帮助您轻松快速地排除许多替代方案。
    • 强大的现代多态类型系统非常擅长检测小、愚蠢、浪费时间的错误。
  2. 惰性求值随处可见,而惰性求值仅限于仔细控制的结构。
    • Lazy 与 Eager 对您预测和了解程序的时间和空间成本的能力有着巨大的影响。
    • 在完全惰性的语言中,您可以将数据的生产与关于如何处理生产后的数据的决策完全分离。这对于搜索问题尤其重要,因为模块化和重用代码变得更加容易。

【讨论】:

    【解决方案5】:

    函数式编程是一种风格,而不是一种语言结构

    大多数函数式语言都有一些共同的原则:

    • 不可变对象
    • 闭包和匿名函数
    • 通用算法
    • 继续

    但最重要的原则是它们通常会迫使您以实用的风格进行写作。您可以使用几乎任何语言以功能样式进行编程。如果您编写这样的代码,C# 可以被视为“函数式”,任何其他语言也可以。

    【讨论】:

    • 模块和仿函数不是必需的,但也很常见。
    • 我会将函数作为值包含在内。
    • 是的,但我想知道不同函数式语言之间的独特属性是什么,而不是函数式和非函数式语言之间的区别。
    • 嗯,毫无疑问,就像 rampion 提到的那样,发挥着价值。类型系统、模式匹配和列表理解也是其他好的特性。
    • 取决于你问谁。我会说一流的词法闭包使语言功能化。 Haskell 程序员经常使用完全不同的想法,即函数式编程仅意味着纯粹的函数式编程,即本质上只是今天的 Haskell。
    【解决方案6】:

    基本属性?

    • 功能纯度(无副作用)
    • 作为上述的搭档,缺乏状态。
    • 函数中的模式匹配

    第一个是美丽的,第二个是前者的丑陋副作用(双关语)。

    我发现在现实世界中对状态缺失的补偿是函数式语言之间最大的区别。

    这几样东西给了很多免费赠品。大多数时候,语言处理记忆。

    【讨论】:

    • 但大多数 FP 语言都不是纯语言
    • 一种语言可以说促进 FP 的程度与其纯度成正比。
    • 没有缺少状态,只是状态存储在其他地方,通常在堆栈中。
    • 嗯,从纯粹的定义来看,一种语言的“功能性”程度主要与它作为价值能力的高阶功能/功能有关。如果一种语言很难定义和返回函数,那么它的功能性就会降低,不是吗?
    【解决方案7】:

    当您说代码作为数据时,您指的是一种以数据结构表示代码的语言。这被称为Homoiconicity,它通常只适用于 lisp 方言或接近它的语言。 Haskell、Erlang 和 Scala 不是 Homoiconic,Clojure 是。

    Clojure 的基本区别在于:

    1. 它有一个软件事务内存系统,这使得共享状态并发编程更容易

    2. 它是一个 Lisp,与 Haskell 或 Erlang 不同,因此所有代码都是数据,它允许您在运行时通过宏系统对语言本身进行看起来像的更改

    3. 它在 JVM 上运行,这意味着您可以直接访问所有 J​​ava 库

    4. Clojure 数据结构在适当的地方实现了 Java 接口,例如 Collection、List、Map、Runnable 和 Callable。字符串只是 Java 字符串,数字是 Java 整数和双精度数。这意味着 Clojure 数据结构可以直接传递给 Java 库,无需任何桥接或翻译

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-25
    • 2017-06-22
    • 1970-01-01
    相关资源
    最近更新 更多