The Little Haskeller
====================

by Curt Sampson <cjs@cynic.net>

$Revision$ $Date$

This is a translation of the code from _The Little MLer_ into Haskell.
It's divided into sections by chapter, and numbered according to the Q&A
numbers in the text of each chapter.

It's currently in quite an early state, and rather rough and ready. I
intend to continue adding translations as I go through the book myself,
clean up things as I learn more and receive comments, and perhaps do a
big final cleanup at the end. Please feel free to mail me any comments
you have, preferably including the revision number of the document above.
I would appreciate feedback from both Haskell experts and novices who
are using this.

This is "literate Haskell," which means that the actual code is
surrounded by blank lines and preceeded by a ">" symbol on each line. If
this file is saved as a ".lhs" file, it can be read directly into the
ghci or Hugs interpreters (using the ":l" command), and you can play
with the definitions.


1. Building Blocks
------------------

16. The ML "datatype" command in Haskell is just "data".

We add "deriving show" after the data definition so that the interpreter
or compiler can print out the value of this; try typing just "Salt" at
your interpreter prompt both with and without the "deriving Show" added
to the type definition.

> data Seasoning = Salt | Pepper                        deriving Show

21. Here we use "Number" instead of "Num", because "Num" is already
defined in the Prelude as a Haskell type class. (Don't worry yet about
what a type class is; it's similar to a type.) And we don't need the
"of" between a constructor (OneMoreThan) and the type of the data it can
take as a parameter (Number).

> data Number = Zero | OneMoreThan Number               deriving Show

23. The Haskell syntax doesn't use parentheses for arguments to
functions or constructors (which are really just a kind of function). In
this example, we assign this to a name so that it parses as a Haskell
file, but when typing at the interpreter prompt, you can leave off
everything up to the "=" sign.

> ch1sec23 = OneMoreThan Zero

25. Haskell, not having parentheses to figure out where the list of
arguments to a function ends, but it does know how many arguments a
function takes, so it tries to use that many. In this case, it knows
that OneMoreThan takes one argument, so if we tried to type

  OneMoreThan OneMoreThan Zero   -- Invalid!

it would assume that the argument to the first OneMoreThan is the thing
after it, which is "OneMoreThan". But OneMoreThan is not a number, it's
a function that makes a Number, so this will fail. We really want to
apply the second OneMoreThan to the Zero, first, getting a Number, and
then pass the Number resulting from that to the first OneMoreThan. We
uses parentheses to force this order of evaluation.

> ch1sec25 = OneMoreThan (OneMoreThan Zero)

26. Invalid code examples, like this one and the one above, will not
have a ">" in front of them, because of course they can't be parsed by
the interpreter. And we'll put a little comment after invalid code, just
to make it clear.

  OneMoreThan 0    -- Invalid!

27. From this point on, we may leave out textual explanation and give
you just the code samples in the format below. You can tell from the
name that the example has been assigned to which chapter and section
it's from.

> ch1sec27 = OneMoreThan (OneMoreThan (OneMoreThan (OneMoreThan Zero)))

32. We use just the letter "a" in Haskell as a type variable, rather
than "'a", and the type variable goes after the type name, rather than
before.

> data OpenFacedSandwich a = Bread a | Slice (OpenFacedSandwich a)
>      deriving Show

> ch1sec33 = Bread 0

34. A slight difference in booleans; in Haskell we have no automatic
definition "true"; instead we construct a new True value of type boolean
using the no-argument constructor for it which comes from the definition
of Bool in the prologue:

  data Bool = False | True

> ch1sec34 = Bread True

35. The types can be found with the ":t" command in your interpreter;
e.g., ":t Bread True" should produce something like:

  Bread True :: OpenFacedSandwich Bool

which is a proper Haskell definition.

37. The definitions are also ungramatatical in Haskell:

  data OpenFacedSandwich Int    -- Invalid!
      = Bread Int
      | Slice (OpenFacedSandwich_I Int)

43. In Haskell, we can declare the type of an expression along with the
expression.

> ch1sec43 = Bread 0 :: OpenFacedSandwich Int

Of course, if we got it wrong, the compiler would tell us:

    Bread True :: OpenFacedSandwich Int    -- Invalid

> ch1sec45 = Bread (OneMoreThan Zero) :: OpenFacedSandwich Number

46. We declare the type within OpenFacedSandwich below to be Int,

> ch1sec46 = Bread (Bread 0) :: OpenFacedSandwich (OpenFacedSandwich Int)

but really, "0" in Haskell might be a float, or other kind of number.
The type system tries to figure out what it should use based where it's
used and what's wanted in that context. The general type is actually the
type class "Num", so if you just ask the interpreter

  :t Bread 0

You will get this:

  Bread 0 :: (Num t) => OpenFacedSandwich t

That just says that "t" is one of many types that is of class "Num"; you
don't have to worry about this at this point in the book. If ever it
does confuse you, you can always declare a type for the literal number
you type in:

  :t Bread (0 :: Int)

will give you the more familiar

  Bread (0 :: Int) :: OpenFacedSandwich Int

47. And the type of:

> ch1sec47 = Bread (Bread (OneMoreThan Zero))

is

  OpenFacedSandwich (OpenFacedSandwich Number)

XXX Editor's note: we should insert a "moral" here about how one
translates function application from ML to Haskell. Something about the
parens....


2. Matchmaker, Matchmaker
-------------------------

1.

> data ShishKebab = Skewer
>                 | Onion ShishKebab
>                 | Lamb ShishKebab
>                 | Tomato ShishKebab
>                 deriving Show

> ch2sec3 = Onion Skewer
> ch2sec4 = Onion (Lamb (Onion Skewer))

From here, with the aid of the examples above, you should be
able to fairly easily convert from the ML constructor form

    Onion(Lamb(Onion(Skewer))

to the Haskell form given in ch2sec4 above. So we'll leave out the
simpler ones from this point on.

15. Note that in Haskell the type declaration of a definition must come
before the definition itself. So the two boxes in the book are reversed
below.

> onlyOnions :: ShishKebab -> Bool
> onlyOnions Skewer     = True
> onlyOnions (Onion x)  = onlyOnions x
> onlyOnions (Lamb x)   = False
> onlyOnions (Tomato x) = False

Note that pattern matching expressions need to be parenthesized just
as regular expressions do; we want to say that "Onion applied to x" is
the single argument to onlyOnions in the pattern "onlyOnions (Onion
x)". Leaving out the parenthesis would say that onlyOnions takes two
arguments, the first of which is a function that takes something and
returns a ShishKebab, and the second of which is something. (Normally
the type declaration would make clear what these are.) In this case,
since that doesn't match the type declaration, you'd get an error,
in ghc's case, "Equations for `onlyOnions' have different numbers of
arguments."

> ch2sec24 = onlyOnions (Onion (Onion Skewer))

42. The successive applications of the function:

> ch2sec42a = onlyOnions (Onion (Onion Skewer)) -- is the same as:
> ch2sec42b = onlyOnions (Onion Skewer)         -- which is the same as:
> ch2sec42c = onlyOnions Skewer                 -- which is:
> ch2sec42d = True

> ch2sec43 = onlyOnions (Onion (Lamb (Skewer)))

63.

> isVegetarian :: ShishKebab -> Bool
> isVegetarian Skewer     = True
> isVegetarian (Onion x)  = isVegetarian x
> isVegetarian (Lamb x)   = False
> isVegetarian (Tomato x) = isVegetarian x

64. Well, we're definiting all of this in one module (file), and we
can't have multiple constructors with the same name. Otherwise, the
interpreter can't know, when you say "Onion foo", whether you're trying
to make a ShishKebab or a Shish. So we prepend an 'S' to the Shish
constructors to resolve that problem. If you get odd type errors later
on in this chapter, check that you're using the right constructor! Trust
me, you will make this mistake.

> data Shish a = Bottom a
>              | SOnion (Shish a)
>              | SLamb (Shish a)
>              | STomato (Shish a)
>              deriving Show

65.

> data Rod = Dagger | Fork | Sword                      deriving Show
> data Plate = GoldPlate | SilverPlate | BrassPlate     deriving Show

> ch2sec69 = SOnion (STomato (Bottom Dagger))

You'll note that in Haskell, we say that this belongs to "Shish Rod",
the opposite of ML's "rod shish". Type ":t ch2sec69" at your interpreter
prompt to see:

    ch2sec69 :: Shish Rod

73.

> isVeggie :: Shish a -> Bool
> isVeggie (Bottom x)  = True
> isVeggie (SOnion x)  = isVeggie x
> isVeggie (SLamb x)   = False
> isVeggie (STomato x) = isVeggie x

74.

    isVeggie (Onion Fork)       -- Invalid!

> ch2sec76 = isVeggie (SOnion (STomato (Bottom Dagger)))


XXX Editor's note: The rest of chapter two is coming soon.
