Can Haskell programs be represented as Lisp S-expressions? - haskell

This would be useful for genetic programming, which usually use a Lisp subset as representation for programs.
I've found something called Liskell (Lisp syntax, Haskell inside) on the web, but the links are broken and I can't find the paper on it...

Check out Lisk, which was designed to fix the author's gripes with Liskell.
In my spare time I’m working on a project called Lisk. Using the -pgmF option for GHC, you can provide GHC a program name that is called to preprocess the file before GHC compiles it. It also works in GHCi and imports. You use it like this:
{-# OPTIONS -F -pgmF lisk #-}
(module fibs
(import system.environment)
(:: main (io ()))
(= main (>>= get-args (. print fib read head)))
(:: test (-> :string (, :int :string)))
(= test (, 1))
(:: fib (-> :int :int))
(= fib 0 0)
(= fib 1 1)
(= fib n (+ (fib (- n 1))
(fib (- n 2)))))
The source is here.
Also, if you don't actually care about Haskell and just want some of its features, you might want to check out Qi (or its successor, Shen), which has s-expression syntax with many modern functional-programming features similar to Haskell.

You might be interested in a project I have been working on, husk scheme.
Basically it will let you call into Scheme code (S-expressions) from Haskell, and vice-versa. So you can intermingle that code within your program, and then process the s-expressions as native Haskell data types when you want to do something on the Haskell side.
Anyway, it may be useful to you, or not - have a look and decide for yourself.

In most genetic programming software, programs are represented as abstract syntax trees (AST) which are evaluated directly in that form. The Lisp S-expression syntax is only apparent when the programs are output as source code. Have you considered just modifying the output module in your chosen software to produce Haskell source code from the ASTs instead?

The obvious answer is "yes" -- unsurprising given that S-expressions were intended as a simple & uniform representation of parsed code. The thing is that languages like Haskell or ML tend to have some problems with that. I once did something similar to OCaml (abused CamlP4 and wrote some function that translates the P4 AST into some sexpr-like representation), and the fun begins when you run into similar kinds of AST nodes that have different types because they're not really the same... For example, there's function application, and there's a similar form that is used in patterns, and yet another form that is used in type expressions.
My guess is that trying to do genetic programming this way is likely to suffer from too much junk programs that don't have any meaning. But that's unsurprising too for any statically typed language -- a dynamically typed language will allow more junk in. Comparing the two worlds WRT to genetic programming might be interesting for reasons beyond the AI...

The Liskell paper is at http://clemens.endorphin.org/ILC07-Liskell-draft.pdf and the liskell.org site seems to still be up in general.

Related

Why was function application chosen as default Haskell operator, not composition?

Haskell syntax requires relatively noisy f . g $ 3 compared to 3 g f as in stack-oriented languages. What were main design arguments for this choice?
That could also be written f (g 3).
Why is Haskell not a concatenative language?
Based on A History of Haskell, it was influenced by a variety of functional programming and lazy language experiments, including ML. As section 4, Syntax describes:
Currying
Following a tradition going back to Frege, a function of two arguments may be represented as a function of one argument that itself returns a function of one argument. This tradition was honed by Moses Sch ̈onfinkel and Haskell Curry and came to be called currying. Function application is denoted by juxtaposition and associates to the left. Thus, f x y is parsed (f x) y. This leads to concise and powerful code. For example, to square each number in a list we write map square [1,2,3], while to square each number in a list of lists we write map (map square) [[1,2],[3]]. Haskell, like many other languages based on lambda calculus, supports both curried and uncurried definitions,
The concept of currying is so central to Haskell's semantics and the lambda calculus at its core that any other method of arrangement would interact poorly with the language.
The stack-oriented style doesn't so much compose as sequence functions; 3 g f is such a language is rather f $ g $ 3 in Haskell. Of course, that's equivalent to f . g $ 3, but it only works as long as you immediately apply the composition to some value. In Haskell, you very often compose functions just to hand them to some higher-order combinator, or to make a point-free definition. In a stack-oriented language that requires some sort of explicit block, in Haskell it requires just the . operator.
Usually, you don't just chain "atomic" functions. Certainly you don't deal with globally-named single-letter functions, so the tiny . or $ doesn't really make a dramatic difference verbosity-wise. And very often, as rmmh said, you chain partially applied functions, e.g.
main = interact $ unlines . take 10 . filter ((>20) . length) . lines
That's much more cumbersome without cheap tight-binding application. Also, it's very natural to have the seperating . to mark what's not immediately applied but just composed.
If you're interested in the history of Haskell, Hudak, Hughes, Peyton Jones & Wadler's "A History of Haskell: Being Lazy with Class" is the best-known paper on this topic, and well-worth reading.
It doesn't address your question directly, but it does point out one very relevant fact: Haskell was created as a unifying compromise between a bunch of existing languages from small teams. Quoting section 2.2 ("A tower of Babel"):
As a result of all this activity, by the mid-1980s there were a number of researchers, including the authors, who were keenly interested in both design and implementation techniques for pure, lazy languages. In fact, many of us had independently designed our own lazy languages and were busily building our own implementations for them. We were each writing papers about our efforts, in which we first had to describe our languages before we could describe our implementation techniques. Languages that contributed to this lazy Tower of Babel include:
Miranda […]
Lazy ML (LML) […]
Orwell […]
Alfl […]
Id […]
Clean […]
Ponder […]
Daisy […]
So the answer may simply be that Haskell copied this from its predecessor languages. And since a bunch of these languages were in turn based or inspired by Lisp and ML, they may analogously have copied it from them. So back to quote your question:
What were main design arguments for this choice?
Chances are that there was never a sustained argument for the choice. Very few high-level languages have gone for the stack-based design, in any case, and few people know them.
My guess would be lambda calculus and usefulness (in real world scenarios).
In lambda calculus, space is application, and thus it feels more similar to people who know it.
In most commonly used languages, the usual thing to do with a function is to apply it. Haskell is not a stack-based language, so the choice was made there.

turn off lazy evaluation in haskell

Is it possible to turn off lazy evaluation in Haskell?
Is there a specific compiler flag of library to facilitate this?
I wanted to try something new with a old program that I had written a while back, to see if I can improve performance.
There are a number of ways to turn a lazy thing strict. You can:
Explicitly insert a spurious pattern match.
Use seq or its close relative ($!).
Use BangPatterns.
Use strictness annotations on your types.
More information here.
You can't turn off laziness, because the I/O system of Haskell depends on it. Without lazy evaluation this program would run into a busy loop without ever outputting anything:
main = forever (putStrLn "Hello!")
This is because forever c is an infinite program. With lazy evaluation the program is calculated only as far as necessary to run the next instruction. If you turn off laziness, every function becomes strict, including (>>), which basically makes the forever function diverge:
forever c = let cs = c >> cs in cs
However, you can add strictness annotations to constructors and patterns. When a function is strict, its argument is forced as part of the evaluation of the result independent of whether the argument is needed or not. This resembles eager evaluation.
In addition to what Daniel Wagner listed you may want to take a look at a similar question Is there a Haskell compiler or preprocessor that uses strict evaluation?.
Answers include the DDC compiler which attempts to make an strict version of haskell and only lazy explicitly
ghc plugin described in monad.reader 12
"Using nfdata and rnf everywhere" - solrize
and more
The predominate suggestion is to use profiling tools and learn how to optimize Haskell as it is however, since most would consider it a different language with non-strict evaluation turned off.
There's a variant of Haskell called pH (http://csg.csail.mit.edu/projects/languages/ph.shtml)
which uses eager evaluation while still providing non-strict semantics. The Haskell Report is careful to say that it's a non-strict language. Laziness is the obvious way to describe and, apparently, to implement non-strictness.
So, if your question is "Can we use a different evaluation system while maintaining non-strict semantics", you could look at pH. If your question is "Is there a version of Haskell which shares the surface syntax but is strict by default", I think it's covered by other answers.
You can enable the Strict pragma in a module, which will cause everything to be strict by default.
https://ghc.haskell.org/trac/ghc/wiki/StrictPragma
The simple answer is no. The more complex answer is that the computational model upon which Haskell builds up and evaluates functions works in a lazy manner. As you will read in other answer there are ways to force evaluation of some functions earlier then normal, and it is occasionally adventitious to do so. But there is a large portion of valid Haskell which has no normal form. This includes the IO functions and a large amount of the standard prelude.
Conclusion: there is no more a way to turn of lazy evaluation in Haskell then there is a way to turn off pointer arithmetic in C or to turn off OO in Ruby of Java. I suspect that this is much farther then you though this question would take you. (There's no --strict mode), but if you really want to see just how deep the rabit hole goes, "Implementing Lazy Functional Languages on Stock Hardware: The Spineless Tagless G-machine" by Simon Peyton Jones is an adventure worth having.
The strict-identity package has a strict version of the Identity monad.
You can find it here:
https://hackage.haskell.org/package/strict-identity
The usage would look something like this:
foo = runStrictIdentity $! do
x <- f a b
y <- g x y
return $! x + y
Each time return or bind >>= is used, the two parts are evaluated using seq, giving a reasonable guarantee of strictness provided your data structure isn't too deep. This works, for i.e. numbers and basic structures.

Are there any purely functional Schemes or Lisps?

I've played around with a few functional programming languages and really enjoy the s-expr syntax used by Lisps (Scheme in particular).
I also see the advantages of working in a purely functional language. Therefore:
Are there any purely functional Schemes (or Lisps in general)?
The new Racket language (formerly PLT Scheme) allows you to implement any semantics you like with s-expressions (really any syntax). The base language is an eagerly evaluated, dynamically typed scheme variant but some notable languages built on top are a lazy scheme and a functional reactive system called Father Time.
An easy way to make a purely functional language in Racket is to take the base language and not provide any procedures that mutate state. For example:
#lang racket/base
(provide (except-out (all-from-out racket/base) set! ...more here...))
makes up a language that has no set!.
I don't believe there are any purely functional Lisps, but Clojure is probably the closest.
Rich Hickey, the creator of Clojure:
Why did I write yet another programming language?
Basically because I wanted a Lisp for Functional Programming
designed for Concurrency and couldn't find one.
http://clojure.org/rationale
Clojure is functional, with immutable data types and variables, but you can get mutable behavior in some special cases or by dropping down to Java (Clojure runs on the JVM).
This is by design - another quote by Rich is
A purely functional programming
language is only good for heating your
computer.
See the presentation of Clojure for Lisp programmers.
Are there any purely functional Schemes (or Lisps in general)?
The ACL2 theorem prover is a pure Lisp. It is, however, intended for theorem proving rather than programming, and in particular it is limited to first-order programs. It has, however, been extremely successful in its niche.
Among other things, it won the 2005 ACM Software System Award.
Probably not, at least not as anything other than toys/proofs of concept. Note that even Haskell isn't 100% purely functional--it has secret escape hatches, and anything in IO is only "pure" in some torturous, hand-waving sense of the word.
So, that said, do you really need a purely functional language? You can write purely functional code in almost any language, with varying degrees of inconvenience and inefficiency.
Of course, languages that assume universal state-modification make it painful to keep things pure, so perhaps what you really want is a language that encourages immutability? In that case, you might find it worthwhile to take a look at Clojure's philosophy. And it's a Lisp, to boot!
As a final note, do realize that most of Haskell's "syntax" is thick layers of sugar. The underlying language is not much more than a typed lambda calculus, and nothing stops you from writing all your code that way. You may get funny looks from other Haskell programmers, though. There's also Liskell but I'm not sure what state it's in these days.
On a final, practical note: If you want to actually write code you intend to use, not just tinker with stuff for fun, you'll really want a clever compiler that knows how to work with pure code/immutable data structures.
inconsistent and non-extendable syntax
What is "inconsistency" here?
It is odd to base a language choice soley on syntax. After all, learning syntax will take a few hours -- it is a tiny fraction of the investment required.
In comparison, important considerations like speed, typing discipline, portability, breadth of libraries, documentation and community, have far greater impact on whether you can be productive.
Ignoring all the flame bait, a quick google for immutable Scheme yields some results:
http://blog.plt-scheme.org/2007/11/getting-rid-of-set-car-and-set-cdr.html
30 years ago there was lispkit lisp
Not sure how accesible it is today.
[Thats one of the places where I learnt functional programming]
there is owl lisp, a dialect of scheme R5RS with all data structures made immutable and some additional pure data structures. It is not a large project, but seems to be actively developed and used by a small group of people (from what I can see on the website & git repository). There are also plans to include R7RS support and some sort of type inference. So while probably not ready for production use, this might be a fun thing to play with.
If you like lisp's syntax then you can actually do similar things in Haskell
let fibs = ((++) [1, 1] (zipWith (+) fibs (tail fibs)))
The let fibs = aside. You can always use s-expr syntax in Haskell expressions. This is because you can always add parentheses on the outside and it won't matter. This is the same code without redundant parentheses:
let fibs = (++) [1, 1] (zipWith (+) fibs (tail fibs))
And here it is in "typical" Haskell style:
let fibs = [1, 1] ++ zipWith (+) fibs (tail fibs)
There are a couple of projects that aim to use haskell underneath a lispy syntax. The older, deader, and more ponderous one is "Liskell". The newer, more alive, and lighter weight one is hasp. I think you might find it worth a look.

Write a Haskell interpreter in Haskell

A classic programming exercise is to write a Lisp/Scheme interpreter in Lisp/Scheme. The power of the full language can be leveraged to produce an interpreter for a subset of the language.
Is there a similar exercise for Haskell? I'd like to implement a subset of Haskell using Haskell as the engine. Of course it can be done, but are there any online resources available to look at?
Here's the backstory.
I am exploring the idea of using Haskell as a language to explore some of the concepts in a Discrete Structures course I am teaching. For this semester I have settled on Miranda, a smaller language that inspired Haskell. Miranda does about 90% of what I'd like it to do, but Haskell does about 2000%. :)
So my idea is to create a language that has exactly the features of Haskell that I'd like and disallows everything else. As the students progress, I can selectively "turn on" various features once they've mastered the basics.
Pedagogical "language levels" have been used successfully to teach Java and Scheme. By limiting what they can do, you can prevent them from shooting themselves in the foot while they are still mastering the syntax and concepts you are trying to teach. And you can offer better error messages.
I love your goal, but it's a big job. A couple of hints:
I've worked on GHC, and you don't want any part of the sources. Hugs is a much simpler, cleaner implementation but unfortunately it's in C.
It's a small piece of the puzzle, but Mark Jones wrote a beautiful paper called Typing Haskell in Haskell which would be a great starting point for your front end.
Good luck! Identifying language levels for Haskell, with supporting evidence from the classroom, would be of great benefit to the community and definitely a publishable result!
There is a complete Haskell parser: http://hackage.haskell.org/package/haskell-src-exts
Once you've parsed it, stripping out or disallowing certain things is easy. I did this for tryhaskell.org to disallow import statements, to support top-level definitions, etc.
Just parse the module:
parseModule :: String -> ParseResult Module
Then you have an AST for a module:
Module SrcLoc ModuleName [ModulePragma] (Maybe WarningText) (Maybe [ExportSpec]) [ImportDecl] [Decl]
The Decl type is extensive: http://hackage.haskell.org/packages/archive/haskell-src-exts/1.9.0/doc/html/Language-Haskell-Exts-Syntax.html#t%3ADecl
All you need to do is define a white-list -- of what declarations, imports, symbols, syntax is available, then walk the AST and throw a "parse error" on anything you don't want them to be aware of yet. You can use the SrcLoc value attached to every node in the AST:
data SrcLoc = SrcLoc
{ srcFilename :: String
, srcLine :: Int
, srcColumn :: Int
}
There's no need to re-implement Haskell. If you want to provide more friendly compile errors, just parse the code, filter it, send it to the compiler, and parse the compiler output. If it's a "couldn't match expected type a against inferred a -> b" then you know it's probably too few arguments to a function.
Unless you really really want to spend time implementing Haskell from scratch or messing with the internals of Hugs, or some dumb implementation, I think you should just filter what gets passed to GHC. That way, if your students want to take their code-base and take it to the next step and write some real fully fledged Haskell code, the transition is transparent.
Do you want to build your interpreter from scratch? Begin with implementing an easier functional language like the lambda calculus or a lisp variant. For the latter there is a quite nice wikibook called Write yourself a Scheme in 48 hours giving a cool and pragmatic introduction into parsing and interpretation techniques.
Interpreting Haskell by hand will be much more complex since you'll have to deal with highly complex features like typeclasses, an extremely powerful type system (type-inference!) and lazy-evaluation (reduction techniques).
So you should define a quite little subset of Haskell to work with and then maybe start by extending the Scheme-example step by step.
Addition:
Note that in Haskell, you have full access to the interpreters API (at least under GHC) including parsers, compilers and of course interpreters.
The package to use is hint (Language.Haskell.*). I have unfortunately neither found online tutorials on this nor tried it out by myself but it looks quite promising.
create a language that has exactly the features of Haskell that I'd like and disallows everything else. As the students progress, I can selectively "turn on" various features once they've mastered the basics.
I suggest a simpler (as in less work involved) solution to this problem. Instead of creating a Haskell implementation where you can turn features off, wrap a Haskell compiler with a program that first checks that the code doesn't use any feature you disallow, and then uses the ready-made compiler to compile it.
That would be similar to HLint (and also kind of its opposite):
HLint (formerly Dr. Haskell) reads Haskell programs and suggests changes that hopefully make them easier to read. HLint also makes it easy to disable unwanted suggestions, and to add your own custom suggestions.
Implement your own HLint "suggestions" to not use the features you don't allow
Disable all the standard HLint suggestions.
Make your wrapper run your modified HLint as a first step
Treat HLint suggestions as errors. That is, if HLint "complained" then the program doesn't proceed to compilation stage
Baskell is a teaching implementation, http://hackage.haskell.org/package/baskell
You might start by picking just, say, the type system to implement. That's about as complicated as an interpreter for Scheme, http://hackage.haskell.org/package/thih
The EHC series of compilers is probably the best bet: it's actively developed and seems to be exactly what you want - a series of small lambda calculi compilers/interpreters culminating in Haskell '98.
But you could also look at the various languages developed in Pierce's Types and Programming Languages, or the Helium interpreter (a crippled Haskell intended for students http://en.wikipedia.org/wiki/Helium_(Haskell)).
If you're looking for a subset of Haskell that's easy to implement, you can do away with type classes and type checking. Without type classes, you don't need type inference to evaluate Haskell code.
I wrote a self-compiling Haskell subset compiler for a Code Golf challenge. It takes Haskell subset code on input and produces C code on output. I'm sorry there isn't a more readable version available; I lifted nested definitions by hand in the process of making it self-compiling.
For a student interested in implementing an interpreter for a subset of Haskell, I would recommend starting with the following features:
Lazy evaluation. If the interpreter is in Haskell, you might not have to do anything for this.
Function definitions with pattern-matched arguments and guards. Only worry about variable, cons, nil, and _ patterns.
Simple expression syntax:
Integer literals
Character literals
[] (nil)
Function application (left associative)
Infix : (cons, right associative)
Parenthesis
Variable names
Function names
More concretely, write an interpreter that can run this:
-- tail :: [a] -> [a]
tail (_:xs) = xs
-- append :: [a] -> [a] -> [a]
append [] ys = ys
append (x:xs) ys = x : append xs ys
-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith f (a:as) (b:bs) = f a b : zipWith f as bs
zipWith _ _ _ = []
-- showList :: (a -> String) -> [a] -> String
showList _ [] = '[' : ']' : []
showList show (x:xs) = '[' : append (show x) (showItems show xs)
-- showItems :: (a -> String) -> [a] -> String
showItems show [] = ']' : []
showItems show (x:xs) = ',' : append (show x) (showItems show xs)
-- fibs :: [Int]
fibs = 0 : 1 : zipWith add fibs (tail fibs)
-- main :: String
main = showList showInt (take 40 fibs)
Type checking is a crucial feature of Haskell. However, going from nothing to a type-checking Haskell compiler is very difficult. If you start by writing an interpreter for the above, adding type checking to it should be less daunting.
You might look at Happy (a yacc-like parser in Haskell) which has a Haskell parser.
This might be a good idea - make a tiny version of NetLogo in Haskell. Here is the tiny interpreter.
see if helium would make a better base to build upon than standard haskell.
Uhc/Ehc is a series of compilers enabling/disabling various Haskell features.
http://www.cs.uu.nl/wiki/Ehc/WebHome#What_is_UHC_And_EHC
I've been told that Idris has a fairly compact parser, not sure if it's really suitable for alteration, but it's written in Haskell.
Andrej Bauer's Programming Language Zoo has a small implementation of a purely functional programming language somewhat cheekily named "minihaskell". It is about 700 lines of OCaml, so very easy to digest.
The site also contains toy versions of ML-style, Prolog-style and OO programming languages.
Don't you think it would be easier to take the GHC sources and strip out what you don't want, than it would be to write your own Haskell interpreter from scratch? Generally speaking, there should be a lot less effort involved in removing features as opposed to creating/adding features.
GHC is written in Haskell anyway, so technically that stays with your question of a Haskell interpreter written in Haskell.
It probably wouldn't be too hard to make the whole thing statically linked and then only distribute your customized GHCi, so that the students can't load other Haskell source modules. As to how much work it would take to prevent them from loading other Haskell object files, I have no idea. You might want to disable FFI too, if you have a bunch of cheaters in your classes :)
The reason why there are so many LISP interpreters is that LISP is basically a predecessor of JSON: a simple format to encode data. This makes the frontend part quite easy to handle. Compared to that, Haskell, especially with Language Extensions, is not the easiest language to parse.
These are some syntactical constructs that sound tricky to get right:
operators with configurable precedence, associativity, and fixity,
nested comments
layout rule
pattern syntax
do- blocks and desugaring to monadic code
Each of these, except maybe the operators, could be tackled by students after their Compiler Construction Course, but it would take the focus away from how Haskell actually works. In addition to that, you might not want to implement all syntactical constructs of Haskell directly, but instead implement passes to get rid of them. Which brings us to the literal core of the issue, pun fully intended.
My suggestion is to implement typechecking and an interpreter for Core instead of full Haskell. Both of these tasks are quite intricate by themselves already.
This language, while still a strongly typed functional language, is way less complicated to deal with in terms of optimization and code generation.
However, it is still independent from the underlying machine.
Therefore, GHC uses it as an intermediary language and translates most syntaxical constructs of Haskell into it.
Additionally, you should not shy away from using GHC's (or another compiler's) frontend.
I'd not consider that as cheating since custom LISPs use the host LISP system's parser (at least during bootstrapping). Cleaning up Core snippets and presenting them to students, along with the original code, should allow you to give an overview of what the frontend does, and why it is preferable to not reimplement it.
Here are a few links to the documentation of Core as used in GHC:
System FC: equality constraints and coercions
GHC/As a library
The Core type

Haskell or Standard ML for beginners? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
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!
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. (Although, as a commenter points out, it is possible to get almost the same effect in Haskell using Debug.Trace.trace.)
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.
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.
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 :-)
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.
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.
Most answers were technical, but I think you should consider at least one that is not: Haskell (as OCaml), at this time, has a bigger community using it in a wider range of contexts. There's also a big database of libraries and applications written for profit and fun at Hackage. That may be an important factor in keeping some of your students using the language after your course is finished, and maybe trying other functional languages (like Standard ML) later.
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.
Haskell. I'm ahead in my algos/theory class in CS because of the stuff I learned from using Haskell. It's such a comprehensive language, and it will teach you a ton of CS, just by using it.
However, SML is much easier to learn. Haskell has features such as lazy evaluation and control structures that make it much more powerful, but with the cost of a steep(ish) learning curve. SML has no such curve.
That said, most of Haskell was unlearning stuff from less scientific/mathematic languages such as Ruby, ObjC, or Python.

Resources