views:

2981

answers:

6

I'm going to be teaching a lower-division course in discrete structures. I have selected the text book Discrete Structures, Logic, and Computability in part because it contains examples and concepts that are conducive to implementation with a functional programming language. (I also think it's a good textbook.)

I want an easy-to-understand FP language to illustrate DS concepts and that the students can use. Most students will have had only one or two semesters of programming in Java, at best. After looking at Scheme, Erlang, Haskell, Ocaml, and SML, I've settled on either Haskell or Standard ML. I'm leaning towards Haskell for the reasons outlined below, but I'd like the opinion of those who are active programmers in one or the other.

  • Both Haskell and SML have pattern matching which makes describing a recursive algorithm a cinch.
  • Haskell has nice list comprehensions that match nicely with the way such lists are expressed mathematically.
  • Haskell has lazy evaluation. Great for constructing infinite lists using the list comprehension technique.
  • SML has a truly interactive interpreter in which functions can be both defined and used. In Haskell, functions must be defined in a separate file and compiled before being used in the interactive shell.
  • SML gives explicit confirmation of the function argument and return types in a syntax that's easy to understand. For example: val foo = fn : int * int -> int. Haskell's implicit curry syntax is a bit more obtuse, but not totally alien. For example: foo :: Int -> Int -> Int.
  • Haskell uses arbitrary-precision integers by default. It's an external library in SML/NJ. And SML/NJ truncates output to 70 characters by default.
  • Haskell's lambda syntax is subtle -- it uses a single backslash. SML is more explicit. Not sure if we'll ever need lambda in this class, though.

Essentially, SML and Haskell are roughly equivalent. I lean toward Haskell because I'm loving the list comprehensions and infinite lists in Haskell. But I'm worried that the extensive number of symbols in Haskell's compact syntax might cause students problems. From what I've gathered reading other posts on SO, Haskell is not recommended for beginners starting out with FP. But we're not going to be building full-fledged applications, just trying out simple algorithms.

What do you think?


Edit: Upon reading some of your great responses, I should clarify some of my bullet points.

In SML, there's no syntactic distinction between defining a function in the interpreter and defining it in an external file. Let's say you want to write the factorial function. In Haskell you can put this definition into a file and load it into GHCi:

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

To me, that's clear, succinct, and matches the mathematical definition in the book. But if you want to write the function in GHCi directly, you have to use a different syntax:

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

When working with interactive interpreters, from a teaching perspective it's very, very handy when the student can use the same code in both a file and the command line.

By "explicit confirmation of the function," I meant that upon defining the function, SML right away tells you the name of the function, the types of the arguments, and the return type. In Haskell you have to use the :type command and then you get the somewhat confusing curry notation.

One more cool thing about Haskell--this is a valid function definition:

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

Again, this matches a definition they might find in the textbook. Can't do that in SML!

+2  A: 

Many universities teach Haskell as a first functional language or even a first programming language, so I don't think this will be a problem.

Having done some of the teaching on one such course, I don't agree that the possible confusions you identify are that likely. The most likely sources of early confusion are parsing errors caused by bad layout, and mysterious messages about type classes when numeric literals are used incorrectly.

I'd also disagree with any suggestion that Haskell is not recommended for beginners starting out with FP. It's certainly the big bang approach in ways that strict languages with mutation aren't, but I think that's a very valid approach.

Ganesh Sittampalam
I've seen some universities alternate between Haskell and ML, depending on who was teaching the course.
htw
+12  A: 

We teach Haskell to first years at our university. My feelings about this are a bit mixed. On the one hand teaching Haskell to first years means they don't have to unlearn the imperative style. Haskell can also produce very concise code which people who had some Java before can appreciate.

Some problems I've noticed students often have:

  • Pattern matching can be a bit difficult, at first. Students initially had some problems seeing how value construction and pattern matching are related. They also had some problems distinguishing between abstractions. Our exercises included writing functions that simplify arithmetic expression and some students had difficulty seeing the difference between the abstract representation (e.g., Const 1) and the meta-language representation (1).

    Furthermore, if your students are supposed to write list processing functions themselves, be careful pointing out the difference between the patterns

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

    Depending on how much functional programming you want to teach them on the way, you may just give them a few library functions and let them play around with that.

  • We didn't teach our students about anonymous functions, we simply told them about where clauses. For some tasks this was a bit verbose, but worked well otherwise. We also didn't tell them about partial applications; this is probably quite easy to explain in Haskell (due to its form of writing types) so it might be worth showing to them.

  • They quickly discovered list comprehensions and preferred them over higher-order functions like filter, map, zipWith.

  • I think we missed out a bit on teaching them how to let them guide their thoughts by the types. I'm not quite sure, though, whether this is helpful to beginners or not.

  • Error messages are usually not very helpful to beginners, they might occasionally need some help with these. I haven't tried it myself, but there's a Haskell compiler specifically targeted at newcomers, mainly by means of better error messages: Helium

  • For the small programs, things like possible space leaks weren't an issue.

Overall, Haskell is a good teaching language, but there are a few pitfalls. Given that students feel a lot more comfortable with list comprehensions than higher-order functions, this might be the argument you need. I don't know how long your course is or how much programming you want to teach them, but do plan some time for teaching them basic concepts--they will need it.

nominolo
+6  A: 

BTW,

# SML has a truly interactive interpreter in which functions can be both defined and used. In Haskell, functions must be defined in a separate file and compiled before being used in the interactive shell.

Is inaccurate. Use GHCi:

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

There are also good resources for Haskell in education on the haskell.org edu. page, with experiences from different teachers. http://haskell.org/haskellwiki/Haskell_in_education

Finally, you'll be able to teach them multicore parallelism just for fun, if you use Haskell :-)

Don Stewart
I clarified my statement about the SML vs Haskell interpreter. Norman Ramsey said it better: copy-and-paste from the interpreter to a file.
Barry Brown
+3  A: 
  • SML has a truly interactive interpreter in which functions can be both defined and used. In Haskell, functions must be defined in a separate file and compiled before being used in the interactive shell.

While Hugs may have that limitation, GHCi does not:

$ 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"

There's many reasons I prefer GHC(i) over Hugs, this is just one of them.

  • SML gives explicit confirmation of the function argument and return types in a syntax that's easy to understand. For example: val foo = fn : int * int -> int. Haskell's implicit curry syntax is a bit more obtuse, but not totally alien. For example: foo :: Int -> Int -> Int.

SML has what you call "implicit curry" syntax as well.

$ 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

Essentially, SML and Haskell are roughly equivalent. I lean toward Haskell because I'm loving the list comprehensions and infinite lists in Haskell. But I'm worried that the extensive number of symbols in Haskell's compact syntax might cause students problems. From what I've gathered reading other posts on SO, Haskell is not recommended for beginners starting out with FP. But we're not going to be building full-fledged applications, just trying out simple algorithms.

I like using Haskell much more than SML, but I would still teach SML first.

  • Seconding nominolo's thoughts, list comprehensions do seem to slow students from getting to some higher-order functions.
  • If you want laziness and infinite lists, it's instructive to implement it explicitly.
  • Because SML is eagerly evaluated, the execution model is far easier to comprehend, and "debugging via printf" works a lot better than in Haskell.
  • SML's type system is also simpler. While your class likely wouldn't use them anyways, Haskell's typeclasses are still an extra bump to get over -- getting them to understand the 'a versus ''a distinction in SML is tough enough.
ephemient
+21  A: 

Much as I love Haskell, here are the reasons I would prefer SML for a class in discrete math and data structures (and most other beginners' classes):

  • Time and space costs of Haskell programs can be very hard to predict, even for experts. SML offers much more limited ways to blow the machine.

  • Syntax for function defintion in an interactive interpreter is identical to syntax used in a file, so you can cut and paste.

  • Although operator overloading in SML is totally bogus, it is also simple. It's going to be hard to teach a whole class in Haskell without having to get into type classes.

  • Student can debug using print.

  • Infinite data structures blow people's minds. For beginners, you're better off having them define a stream type complete with ref cells and thunks, so they know how it works:

    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
    

    Now it's not magic any more, and you can go from here to streams (infinite lists).

  • Layout is not as simple as in Python and can be confusing.

There are two places Haskell has an edge:

  • In core Haskell you can write a function's type signature just before its definition. This is hugely helpful for students and other beginners. There just isn't a nice way to deal with type signatures in SML.

  • Haskell has better concrete syntax. The Haskell syntax is a major improvement over ML syntax. I have written a short note about when to use parentheses in an ML program; this helps a little.

Finally, there is a sword that cuts both ways:

  • Haskell code is pure by default, so your students are unlikely to stumble over impure constructs (IO monad, state monad) by accident. But by the same token, they can't print, and if you want to do I/O then at minumum you have to explain do notation, and return is confusing.


On a related topic, here is some advice for your course preparation: don't overlook Purely Functional Data Structures by Chris Okasaki. Even if you don't have your students use it, you will definitely want to have a copy.

Norman Ramsey
Nice answer! I'll take another look at SML. Mind you, my knowledge of both Haskell and SML are limited to a few hours each working through online tutorials and reading books. I have yet to dig into the finer details. Can you help with this question? http://stackoverflow.com/questions/740896
Barry Brown
Agreed on everything apart from "print" debugging. You can use it in Haskell. There are both `Debug.Trace.trace :: String -> a -> a` and `Debug.Trace.traceShow :: (Show a) => a -> b -> b` available which go around IO() limitations and output the trace eagerly.
viraptor
+4  A: 

I am amazed you are not considering OCaml and F# given that they address so many of your concerns. Surely decent and helpful development environments are a high priority for learners? SML is way behind and F# is way ahead of all other FPLs in that respect.

Also, both OCaml and F# have list comprehensions.

Jon Harrop
I have looked at both. One thing, in particular, bothers me about OCaml: the syntax for defining a recursive and a non-recursive function is slightly different. It seems trivial to us, but it can be a big thing to a beginner who's having enough trouble with syntax. I'm looking for tools that let me get right to the heart of the matter as quickly as possible. I haven't seriously considered F# since it's a Microsoft-only language.
Barry Brown
In OCaml, you use "let" for non-recursive value or function definitions and "let rec" for recursive definitions. In SML, you usually use "val" for non-function values and "fun" for functions except when they are non-recursive replacements of previous functions, in which case you must use "val" and a lambda function at the top level or "let val" and a lambda function if it is nested. Suffice to say, the OCaml way seems a lot clearer to me. Also, you do not have things like equality types to explain.
Jon Harrop
Although F# is Microsoft only, you can write in the intersection of OCaml and F# whilst still benefitting from F#'s development environment. That would surely make life far easier for a beginner that having to battle with the comparatively-awful tools available for SML and Haskell.
Jon Harrop