Haskell或标准ML适合初学者?

时间:2009-05-01 06:31:17

标签: haskell functional-programming sml discrete-mathematics

我将在离散结构中教授低级课程。我部分地选择了教科书Discrete Structures, Logic, and Computability,因为它包含有助于使用函数式编程语言实现的示例和概念。 (我也认为这是一本很好的教科书。)

我想要一个易于理解的FP语言来说明DS概念以及学生可以使用的概念。大多数学生最多只能用一到两个学期的Java编程。在查看Scheme,Erlang,Haskell,Ocaml和SML之后,我已经确定了Haskell或Standard ML。由于下面列出的原因,我倾向于Haskell,但我喜欢那些活跃的程序员在一个或另一个的意见。

  • Haskell和SML都有模式匹配,这使得描述递归算法变得简单。
  • Haskell有很好的列表推导,可以很好地匹配这些列表以数学方式表达的方式。
  • Haskell有懒惰的评价。非常适合使用列表理解技术构建无限列表。
  • SML有一个真正的交互式解释器,可以在其中定义和使用函数。在Haskell中,函数必须在单独的文件中定义,并在交互式shell中使用之前进行编译。
  • SML以易于理解的语法明确确认函数参数和返回类型。例如:val foo = fn:int * int - > INT。 Haskell隐含的咖喱语法有点迟钝,但并非完全陌生。例如:foo :: Int - > Int - >中间体
  • Haskell默认使用任意精度的整数。它是SML / NJ中的外部库。并且SML / NJ默认将输出截断为70个字符。
  • Haskell的lambda语法很微妙 - 它使用一个反斜杠。 SML更明确。但不确定我们是否会在这堂课中需要lambda。

基本上,SML和Haskell大致相同。我倾向于Haskell,因为我喜欢Haskell中的列表理解和无限列表。但我担心Haskell紧凑语法中的大量符号可能会导致学生出现问题。从我收集到的关于SO的其他帖子开始,Haskell不建议初学者从FP开始。但我们不打算构建成熟的应用程序,只是尝试简单的算法。

您怎么看?


编辑:在阅读了一些精彩的回复后,我应该澄清一些要点。

在SML中,在解释器中定义函数和在外部文件中定义函数之间没有语法上的区别。假设您要编写阶乘函数。在Haskell中,您可以将此定义放入文件中并将其加载到GHCi中:

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

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

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

使用交互式口译员时,从教学的角度来看,当学生在文件和命令行中使用相同的代码时非常非常方便。

通过“明确确认函数”,我的意思是在定义函数时,SML立即告诉您函数的名称,参数的类型和返回类型。在Haskell中你必须使用:type命令,然后你会得到一些有点令人困惑的咖喱符号。

关于Haskell的一个更酷的事情 - 这是一个有效的函数定义:

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

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

8 个答案:

答案 0 :(得分:85)

就像我喜欢Haskell一样,以下是我更喜欢SML用于离散数学和数据结构(以及大多数其他初学者类)的原因:

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

  • 交互式解释器中的函数定义语法与文件中使用的语法相同,因此您可以剪切和粘贴。

  • 虽然SML中的运算符重载完全是假的,但它也很简单。在不必进入类型类的情况下,很难在Haskell中教授整个类。

  • 学生可以使用print进行调试。 (虽然,正如评论者指出的那样,使用Debug.Trace.trace可以在Haskell中获得几乎相同的效果。)

  • 无限的数据结构打动了人们的思想。对于初学者来说,最好让他们定义一个带有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语法的重大改进。我写了short note about when to use parentheses in an ML program;这有点帮助。

最后,有一把剑可以削减两种方式:

  • Haskell代码默认是纯粹的,所以你的学生不太可能偶然发现不纯的结构(IO monad,state monad)。但出于同样的原因,他们无法打印,如果你想做I / O,那么你必须解释do符号,return令人困惑。

关于相关主题,以下是您准备课程的一些建议:不要忽视Chris Okasaki的Purely Functional Data Structures。即使你没有让你的学生使用它,你肯定想要一份副本。

答案 1 :(得分:29)

我们在大学的第一年教Haskell。我对此的看法有点混乱。一方面教Haskell到第一年意味着他们不必忘记命令式的风格。 Haskell也可以生成非常简洁的代码,以前有过Java的人可以欣赏它们。

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

  • 首先,模式匹配可能有点困难。学生最初在看到价值构建和模式匹配是如何相关时遇到了一些问题。他们在区分抽象方面也存在一些问题。我们的练习包括编写简化算术表达的函数,一些学生很难看到抽象表示(例如Const 1)和元语言表示(1)之间的差异。

    此外,如果您的学生应该自己编写列表处理功能,请注意指出模式之间的区别

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

    根据您希望在途中教授多少函数式编程,您可以给它们一些库函数,让它们随意使用。

  • 我们没有教我们的学生匿名功能,我们只是告诉他们where条款。对于某些任务,这有​​点冗长,但其他方面效果不错。我们也没有告诉他们部分申请;这可能很容易在Haskell中解释(由于它的写作类型),所以它可能值得向他们展示。

  • 他们很快发现了列表推导,并将其优先于filtermapzipWith等高阶函数。

  • 我认为我们错过了教他们如何让他们按类型指导他们的想法。不过,我不太确定这对初学者是否有帮助。

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

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

总的来说,Haskell是一种很好的教学语言,但有一些陷阱。鉴于学生对列表推导感觉比高阶函数更舒服,这可能是您需要的论据。我不知道你的课程有多长,或者你想教他们多少编程,但是要花些时间教他们基本概念 - 他们需要它。

答案 2 :(得分:13)

BTW,

  

#SML具有真正的互动性   功能可以的解释器   定义和使用。在哈斯克尔,   函数必须在a中定义   单独的文件和之前编译   在交互式shell中使用。

不准确。使用GHCi:

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

在haskell.org edu上,Haskell在教育方面也有很好的资源。页面,有不同老师的经验。 http://haskell.org/haskellwiki/Haskell_in_education

最后,如果你使用Haskell,你将能够教他们多核并行,只是为了好玩: - )

答案 3 :(得分:10)

许多大学将Haskell教授为第一种功能语言甚至是第一种编程语言,所以我认为这不会成为一个问题。

在一门这样的课程上完成了一些教学,我不同意你认识到的可能的混淆。早期混淆的最可能的来源是解析错误布局导致的错误,以及在错误地使用数字文字时关于类型类的神秘消息。

我也不同意任何关于Haskell不建议初学者开始使用FP的建议。当然,突变的严格语言并不是一种大爆炸方式,但我认为这是一种非常有效的方法。

答案 4 :(得分:9)

  
      
  • SML有一个真正的交互式解释器,可以在其中定义和使用函数。在Haskell中,函数必须在单独的文件中定义,并在交互式shell中使用之前进行编译。
  •   

虽然拥抱可能有这种限制,但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)而不是Hugs的原因有很多,这只是其中之一。

  
      
  • SML以易于理解的语法明确确认函数参数和返回类型。例如:val foo = fn:int * int - > INT。 Haskell隐含的咖喱语法有点迟钝,但并非完全陌生。例如:foo :: 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,因为我喜欢Haskell中的列表理解和无限列表。但我担心Haskell紧凑语法中的大量符号可能会导致学生出现问题。从我收集到的关于SO的其他帖子开始,Haskell不建议初学者从FP开始。但我们不打算构建成熟的应用程序,只是尝试简单的算法。

我喜欢使用Haskell而不是SML,但我仍然会首先教授SML。

  • 借调nominolo的想法,列表理解似乎会减慢学生进入某些高阶函数的速度。
  • 如果你想要懒惰和无限列表,明确地实现它是有益的。
  • 因为热切评估SML,所以执行模型更容易理解,“通过printf调试”比Haskell更好。
  • SML的类型系统也更简单。虽然你的课程可能不会使用它们,但是Haskell的类型类仍然是一个额外的结果 - 让他们理解SML中'a''a的区别是非常困难的。

答案 5 :(得分:7)

大多数答案都是技术性的,但我认为你应该考虑至少一个不是:Haskell(作为OCaml),在这个时候,有一个更大的社区在更广泛的背景下使用它。还有一个大型的图书馆和应用程序数据库,为Hackage的利润和乐趣而编写。这可能是让您的学生在课程结束后使用该语言的一个重要因素,也可能是稍后尝试其他功能语言(如标准ML)。

答案 6 :(得分:4)

我很惊讶你不考虑OCaml和F#,因为他们解决了你的许多问题。当然,体面和有益的开发环境是学习者的首要任务吗?在这方面,SML远远落后于所有其他FPL。

此外,OCaml和F#都有列表推导。

答案 7 :(得分:2)

Haskell中。我在CS的算法/理论课中领先于我从使用Haskell学到的东西。它是一种如此全面的语言,它会教你大量的CS,只是使用它

然而,SML更容易学习。 Haskell具有惰性评估和控制结构等功能,使其功能更强大,但成本却是陡峭(ish)的学习曲线。 SML没有这样的曲线。

也就是说,Haskell的大部分内容都来自于较少科学/数学语言,如Ruby,ObjC或Python。