初学者使用 Haskell 还是标准 ML?

我将教授一门离散结构的低除法课程。我之所以选择 离散结构、逻辑和可计算性这本教科书,部分原因是它包含有助于使用函数式编程语言实现的示例和概念。(我也认为这是一本很好的教科书。)

我想要一个易于理解的 FP 语言,以说明 DS 的概念,学生可以使用。大多数学生最多只学过一两个学期的 Java 编程。在查看 Scheme、 Erlang、哈斯克尔、 Ocaml 和 SML 之后,我选择了 Haskell 或 Standard ML。由于下面列出的原因,我倾向于 Haskell,但是我想听听那些活跃的程序员的意见。

  • Haskell 和 SML 都有自己的模式匹配,这使得描述一个递归算法变得非常容易。
  • Haskell 有很好的列表理解,与这种列表的数学表达方式非常匹配。
  • Haskell 有惰性计算,非常适合使用列表内涵技术构造无限列表。
  • SML 有一个真正的交互式解释器,在其中可以定义和使用函数。在哈斯克尔,函数必须在单独的文件中定义并编译,然后才能在交互式 shell 中使用。
  • SML 以易于理解的语法明确确认函数参数和返回类型。例如: val foo = fn: int * int-> int。Haskell 的隐式 curry 语法更加迟钝,但并非完全陌生。例如: foo: : Int-> Int-> Int。
  • Haskell 默认使用任意精度的整数。它是 SML/NJ 中的一个外部库。默认情况下,SML/NJ 将输出截断为70个字符。
  • Haskell 的 lambda 语法很微妙——它使用一个反斜杠。SML 更加明确。不知道这门课还需不需要 Lambda。

本质上,SML 和 Haskell 大致相当。我倾向于 Haskell,因为我喜欢哈斯克尔的列表理解和无限列表。但我担心,哈斯克尔紧凑语法中大量的符号可能会给学生带来问题。通过阅读其他关于 SO 的文章,我发现对于初学 FP 的人来说,不推荐使用 Haskell。但是我们不打算构建成熟的应用程序,只是尝试简单的算法。

你觉得怎么样?


编辑: 在阅读你的一些伟大的回应,我应该澄清我的一些要点。

在 SML 中,在解释器中定义函数和在外部文件中定义函数之间没有语法区别。假设您想编写阶乘函数。在哈斯克尔,你可以把这个定义放到一个文件中,然后把它加载到 gHCi 中:

fac 0 = 1
fac n = n * fac (n-1)

对我来说,这是明确的,简洁的,并符合书中的数学定义。但是如果你想直接用 GHCi 写函数,你必须使用不同的语法:

let fac 0 = 1; fac n = n * fac (n-1)

当使用交互式解释器时,从教学的角度来看,当学生可以在文件和命令行中使用相同的代码时,这是非常非常方便的。

通过“显式确认函数”,我的意思是在定义函数时,SML 立即告诉您函数的名称、参数的类型和返回类型。在哈斯克尔,你必须使用 abc0命令,然后你会得到一些令人困惑的咖喱符号。

Haskell 还有一个很酷的地方——这是一个有效的函数定义:

fac 0 = 1
fac (n+1) = (n+1) * fac n

同样,这与他们可能在教科书中找到的定义相匹配。在 SML 中无法做到这一点!

36525 次浏览

许多大学将 Haskell 作为第一种函数式语言甚至第一种编程语言来教授,所以我认为这不会是个问题。

在这样一门课程上做了一些教学工作之后,我不同意你所指出的可能的混淆是那么可能的。最有可能造成早期混淆的原因是错误的布局导致的解析错误,以及数值文字使用不当时关于类型类的神秘消息。

我也不同意任何关于 Haskell 不适合 FP 初学者的建议。这当然是大爆炸的方法,严格的语言与变异不是,但我认为这是一个非常有效的方法。

我们在大学一年级教 Haskell。我对此的感觉有点复杂。一方面,教一年级的 Haskell 意味着他们不必忘记命令式的风格。Haskell 还可以生成非常简洁的代码,以前使用过 Java 的人可以欣赏这些代码。

我注意到一些学生经常遇到的问题:

  • 模式匹配一开始可能会有点困难。最初,学生们对于价值构建与模式匹配之间的关系存在一些问题。他们在区分抽象方面也有一些问题。我们的练习包括简化算术表达式的写作函数,一些学生很难看出抽象表达式(如 Const 1)和元语言表达式(1)之间的区别。

    此外,如果您的学生应该自己编写列表处理函数,请小心指出模式之间的差异

    []
    [x]
    (x:xs)
    [x:xs]
    

    取决于您希望在这个过程中教给他们多少函数式编程,您可以只给他们一些库函数,让他们随意使用。

  • 我们没有教我们的学生关于匿名函数,我们只是告诉他们关于 where子句。对于某些任务,这有点冗长,但在其他方面效果很好。我们也没有告诉他们部分应用程序,这在哈斯克尔可能很容易解释(由于其写作类型的形式) ,所以可能值得向他们展示。

  • 他们很快就发现了列表理解,并且比高阶函数(如 filtermapzipWith)更喜欢列表理解。

  • 我认为我们错过了一些教他们如何让他们以类型来引导他们的思想。不过,我不太确定这对初学者是否有帮助。

  • 错误信息通常对初学者没有多大帮助,他们可能偶尔需要一些帮助。我自己还没有试过,但是有一个 Haskell 编译器专门针对新手,主要是通过更好的错误消息: 西里安

  • 对于小型程序来说,可能的空间泄漏不是问题。

总的来说,Haskell 是一种很好的教学语言,但是也有一些缺陷。考虑到学生们对列表理解比对高阶函数更感到舒服,这可能就是你需要的论据。我不知道你的课程有多长,也不知道你想教给他们多少编程知识,但是一定要计划一些时间来教他们一些基本的概念——他们会需要的。

顺便说一句,

# SML 有一个真正的交互式 函数可以在其中的解释器 定义和使用。在哈斯克尔, 函数必须在 独立的文件,并编译之前 在交互式 shell 中使用。

不准确。请使用 GHCi:

Prelude> let f x = x ^ 2
Prelude> f 7
49
Prelude> f 2
4

在 Haskell.org 的 edu. page 上也有很好的 Haskell 教育资源,有来自不同教师的经验

最后,如果您使用 Haskell: -) ,您将能够仅仅为了好玩而教他们多核并行性

  • SML 有一个真正的交互式解释器,在其中可以定义和使用函数。在哈斯克尔,函数必须在单独的文件中定义并编译,然后才能在交互式 shell 中使用。

虽然 Hugs 可能有这个限制,但 GHCi 没有:

$ ghci
GHCi, version 6.10.1: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer ... linking ... done.
Loading package base ... linking ... done.
Prelude> let hello name = "Hello, " ++ name
Prelude> hello "Barry"
"Hello, Barry"

比起拥抱,我更喜欢 GHC (i)的原因有很多,这只是其中之一。

  • SML 以易于理解的语法明确确认函数参数和返回类型。例如: val foo = fn: int * int-> int。Haskell 的隐式 curry 语法更加迟钝,但并非完全陌生。例如: foo: : Int-> Int-> Int。

SML 也有所谓的“隐式咖喱”语法。

$ sml
Standard ML of New Jersey v110.69 [built: Fri Mar 13 16:02:47 2009]
- fun add x y = x + y;
val add = fn : int -> int -> int

本质上,SML 和 Haskell 大致相当。我倾向于 Haskell,因为我喜欢哈斯克尔的列表理解和无限列表。但我担心,哈斯克尔紧凑语法中大量的符号可能会给学生带来问题。通过阅读其他关于 SO 的文章,我发现对于初学 FP 的人来说,不推荐使用 Haskell。但是我们不打算构建成熟的应用程序,只是尝试简单的算法。

比起 SML,我更喜欢使用 Haskell,但我还是会先教 SML。

  • 与诺米诺洛的想法相同,列表理解 似乎使学生在获得某些高阶函数方面进展缓慢。
  • 如果您想要惰性和无限列表,那么明确地实现它是有益的。
  • 因为 SML 是被热切评估的,所以执行模型更容易理解,而且“通过 printf 进行调试”的效果要比哈斯克尔好得多。
  • SML 的类型系统也更简单。虽然您的类可能不会使用它们,但 Haskell 的类型类仍然需要克服一个额外的障碍——让它们理解 SML 中 'a''a的区别已经够难的了。

虽然我很喜欢 Haskell,但是我更喜欢 SML 作为离散数学和数据结构类(以及大多数其他初学者类)的原因如下:

  • Haskell 程序的时间和空间成本很难预测,即使对专家来说也是如此。SML 提供了更多有限的方法来吹机器。

  • 交互式解释器中函数定义的语法是 一模一样,用于文件中使用的语法,因此可以剪切和粘贴。

  • 尽管 SML 中的运算符重载完全是伪造的,但它也很简单。在哈斯克尔,如果不上打字课,要教一整个班级是很困难的。

  • 学生可以使用 print进行调试。(不过,正如一位评论者指出的那样,在哈斯克尔,使用 ABc1也可能产生几乎相同的效果。)

  • 无穷无尽的数据结构令人震惊。对于初学者来说,最好让他们定义一个包含 ref 单元格和 thunk 的流类型,这样他们就知道它是如何工作的:

    datatype 'a thunk_contents = UNEVALUATED of unit -> 'a
    | VALUE of 'a
    type 'a thunk = 'a thunk_contents ref
    val delay : (unit -> 'a) -> 'a thunk
    val force : 'a thunk -> 'a
    

    现在它不再是魔术,你可以从这里到流(无限列表)。

  • 布局不像 Python 那样简单,可能会让人感到困惑。

Haskell 有两个优势:

  • 在核心 Haskell 中,可以在函数定义之前编写函数的类型签名。这对学生和其他初学者非常有帮助。只是在 SML 中没有一种处理类型签名的好方法。

  • Haskell 有更好的具体语法。Haskell 语法是对 ML 语法的重大改进。我已经写了一个 关于在机器学习程序中何时使用括号的简短说明,这有一点帮助。

最后,还有一把双刃剑:

  • Haskell 代码在默认情况下是纯代码,因此您的学生不太可能偶然碰到不纯结构(IO 单子、状态单子)。但是出于同样的原因,它们不能打印,如果你想做 I/O,那么至少你必须解释 do符号,而 return是令人困惑的。

关于一个相关的话题,这里有一些建议,为您的课程准备: 不要忽视 纯功能性数据结构的克里斯冈崎。即使你没有让你的学生使用它,你也一定会想要一份拷贝。

我很惊讶你没有考虑 OCaml 和 F # ,因为它们解决了你的很多问题。对于学习者来说,体面和有益的发展环境当然是最优先考虑的事情?SML 远远落后,而 F # 在这方面远远领先于所有其他 FPL。

此外,OCaml 和 F # 都有列表理解。

大多数答案都是技术性的,但我认为至少应该考虑一个不是技术性的答案: Haskell (作为 OCaml)目前拥有一个更大的社区,在更广泛的背景下使用它。还有一个大型的数据库库和应用程序编写的利润和乐趣在 黑客入侵。这可能是一个重要的因素,让你的一些学生在你的课程结束后继续使用这种语言,或者在以后尝试其他的函数式语言(比如标准 ML)。

Haskell.我在计算机科学的算法/理论课上名列前茅,这是因为我从 Haskell 中学到的东西。这是一门非常全面的语言,它会教你大量的计算机科学,只要用它

然而,SML 更容易学习。Haskell 具有惰性评估和控制结构等特性,这些特性使其功能更加强大,但代价是学习曲线陡峭。SML 没有这样的曲线。

也就是说,大多数 Haskell 都是从不太科学/数学的语言(比如 Ruby、 ObjecC 或 Python)中删除东西。