[OT] Teaching Haskell in High School

Arjan van IJzendoorn afie@cs.uu.nl
Fri, 7 Feb 2003 14:41:03 +0100


Hello all,

Michael Sperber wrote:

> - With the programming environment, it isn't just a question of being
>   "easier to use": in my experience, environments like Hugs (or any
>   Scheme environment other than DrScheme) work for some, but frustrate
>   many beginners because they don't enable them to fix trivial
>   mistakes which they're bound to make in the first couple of weeks.

I completely agree. I've downloaded and installed DrScheme and I must say
the environment is really nice: it looks good, helps you to layout your
program, matches parentheses and so on. The quality of the messages,
however, is not as good as I would have expected. I am a beginning Scheme
programmer and here's something I encountered:

> (define (length2 xs)
    (cond [(empty? xs) 0]
          [else (+1 (length2 (rest xs)))]))
> (length2 (cons 3 (cons 7 empty)))
procedure application: expected procedure, given: 1; arguments were: 1

The definition is accepted but when use it I get an error I don't
understand. I should note that the environment neatly indicates that (+1
(length2 (rest xs))) is the problem. I've browsed the documentation, found a
definition of length and saw that the only difference was a space character
between + and 1. That couldn't be it, could it? But it was. Apparently
Scheme supports a unary plus, but still the message was wrong and should
have read:

procedure application: expected procedure, given: +1; arguments were: 1

I think it is important that error messages show exactly what was entered by
the user (+1 and not 1) and that's why in Helium we remember each little
detail in the abstract syntax tree. I still don't know what "arguments were:
1" means. Does it refer to the number of arguments?

>   The TeachScheme! folks had to work a long time on getting DrScheme
>   to provide the feedback needed to alleviate this problem.

That's a really good point. We should look at the errors students actually
make and not at the errors we make or just make an educated guess. For that
reason, Helium has a logging facility built in which sends a server the
programs containing errors. This wealth of information can now be used to
give hints for type errors that occur often and to improve other kinds of
errors. [Don't worry, the version distributed via the web does not contain
the logging facility; only the version in our labs has that piece of code
compiled in].

> - In theory, static typing is good because it helps spot bugs in your
>   program early.  However, the current state of the art is such that
>   the type error messages in Haskell systems are not sufficiently
>   helpful (in fact, often misleading) when it comes to finding the
>   actual source of the problem.

True. I've also seen people being disappointed in the language Haskell
because of the message of the compiler/interpreter. But then I wondered: how
can dynamic typing improve on this? So I made a type error in my length2
example:

> (define (length2 xs)
    (cond [(empty? xs) 0]
          [else (+ 1 (rest xs))]))
> (length2 (cons 2 empty))
+: expects type <number> as 2nd argument, given: empty; other arguments
were: 1

Perfect. I know immediately what the problem is and I can fix it. [The fact
that a type error only shows up when you actually reach to code is a pity,
because you may submit a lab assignment that still somewhere tries to add a
number to a list.] But how is this different from the Helium message:

(2,20): Type error in infix application
*** Expression     : 1 + xs
*** Term           : xs
*** Type           : [a]
*** Does not match : Int

It contains exactly the same information; invisible to the user for now is
that we also now where the expresion ends so that we can do highlighting in
the future. Maybe Michael is referring to what Hugs says about this program:

ERROR "C:\docs\Bla.hs":1 - Illegal Haskell 98 class constraint in inferred
type
*** Expression : mylength2
*** Type       : Num [a] => [a] -> [a]

Wrong line number and a very confusing message. We must keep in mind though
that no Haskell compiler so far was designed primarily with helpful messages
in mind.

>   Again, Helium may be an improvement, but I suspect that the
>   underlying problem is deeply rooted.

The underlying problem may be the inference algorithm used in most
compilers. We use a radically different approach which allows us to
determine much more information and point at different origins of the error
when compared to Hindley Milner based inferencing. If there are three uses
of a variable as a Bool and one as an Int we can say that probably the Int
usage is wrong.

>   - The syntax for ordinary variables and type variables are the same.

That's just how you teach it. You can 'raise' hords of students believing
that type variables are always called a, b and c and normal variables use
longer, more informative names. As Larry Wall put it: "you can write
assembly in any language".

>   - The same holds generally for the type syntax which is analogous to
>     the value syntax.

I use different colours in my lectures and a really cool, parsing
programming environment could do that, too.

>  I wish the Haskell (and ML
> and ...) community would just reap the benefits rather than doing the
> usual NIH thing.

Good point, but there are fundamental differences between Scheme and Haskell
and our whole teaching here in Utrecht is heavily geared towards Haskell: we
really need laziness to write our parsers and our attribute grammars, we
believe that static typing is the way to go for more robust software, we
think that algebraic data types are a great way to model data and so on. The
whole curriculum is based on those needs and beliefs and switching to
DrScheme because there is a wonderful environment out there is no option.
The Scheme people cannot convince me to use Scheme and I can't convince the
Scheme people to use Haskell. And I honestly think that that is *no
problem*. I let myself be inspired by DrScheme and maybe DrScheme can learn
some tricks from Helium in the future. And maybe by doing that we can make
this world a little better :-)

Cheers, Arjan