September 7, 2026

Haskell for Hobos-- Types

There are types and there are typeclasses. Don't get them confused (like I did). Go read my section on Typeclasses for a discussion of this.

Haskell goes in for types in a big way. Haskell will absolutely throw a fit if you play fast and loose with types. This strictness is good, because it allows many problems to be detected at compile time rather than having them bite you later.

Haskell performs what they call "type inference" and does a good job of it. This means that it is possible (though perhaps not good) to write a lot of Haskell code without explicitly declaring types.

Consider the following function which returns a magic number:

magic = 12345
Haskell sees the primitive type constant 12345 of type "int" so it can deduce that the function returns a type of "int". That is type inference. If we want to be explicit (and it is a good habit to form) we would write:
magic :: Int
magic = 12345
Here the :: operator could be read as "has type of"

Primitive Types

Haskell has a handful of primitive types (like Int and Char). There is a "String" type, but it turns out that this is just a convenience. String is just a shorthand for [Char] -- i.e. a list of Char types.

Note that Haskell has a bewildering variety of integer types.

Int - platform dependent
Integer - unlimited
Int64, Word64 -- and more!
These days "Int" is probably 64 bits on most machines. I also read that is can be only 28 bits on some 32 bit machines with certain Haskell implementations. This is much like "int" in the C language -- you never know for sure and you need to be careful.

The Integer type an unbounded arbitrary size and precision integer. It seems well behaved and has been in Haskell forever. Just the thing for huge factorials and such.

There is Int64 (as well as Word64, which is unsigned). Int64 pops up unexpectedly and gives me headaches, which can generally be tamed using "fromIntegral". I have a page elsewhere dedicated to these troubles.

There is a thing called "Integral", but it is not a type at all, rather a typeclass (used to indicate that a given type supports integer division).

Functions with arguments

Here things get interesting. The notation is surprising and is related to currying. I talk about currying elsewhere, but it is unavoidable to talk about it here also.
Here is a function with one argument:
to_str :: Int -> String
to_str x = show x
Nothing all that tricky yet. This is a function that takes an Int as an argument and returns it as a string. Exactly what "show" does, but "show" is not as fussy and will take almost anything as an argument. But notice the syntax with the two character -> operator.

The two character -> operator is also used to form what are called "lambda expressions", but we will simply note that for now. There is no reason to talk about lambda expressions here, but you have been warned.

Things get interesting when we move on to a function with 2 arguments. Suppose we get tired of writing 2 + 2 and want to say "sadd 2 2" Not only that, suppose we want to return the result as a string.

sadd :: Int -> Int -> String
sadd x y = show $ x + y
It is a silly example, but the point is to demonstrate the type signature. This function takes two arguments, each of them an Int and it returns a String.

The -> operator is sometimes called "the function arrow". It sits between the type of an argument on the left and the result on the right. This won't entirely make sense until you also know that it is right associative. This means that the type we showed above actually works like this:

sadd :: Int -> ( Int -> String )
So the first arrow says that we have a function that takes a single Int as an argument and returns a function! The function it returns (with the type as shown inside the parenthesis) takes another Int as an argument and returns a String.

This takes some getting used to and is what Haskell calls "currying". I have a separate section on that elsewhere -- but we are really describing it here, so you can get the explanation twice from two different points of view.

Every function in Haskell actually (in all truth) only ever takes one argument. We get the illusion of two argument functions from statements like this:

sadd x y = show $ x + y
Nonetheless, "sadd" actually takes one argument (namely "x") and produces a nameless hidden function that in turn takes one argument (namely "y") and produces a string.

Partial application

We can tap into the nameless hidden function that currying produces by doing what is called "partial application".
We do this:
half_add a = sadd a
This yields a function that tucks away the "a" argument and is waiting for another Int -- and will produce a String when it gets one (at which time it will perform the addition and then make a string out of the result).

This definitely takes some getting used to. Here is an example of two ways to write the same function:

double_it x = 2 * x
double_it = 2 *
A "double_it" function might possibly actually be useful. The first form above should give no surprises. It takes an argument x, multiplies it by 2, and returns the result.

The second form illustrates partial application and is exactly the same! We get a function that is waiting for a single argument and when it gets one, will multiply it by 2 and return the result.

Sections

Haskell has some interesting syntax and behavior with regard to infix functions and partial application. Consider the infix operator (actually a function) that we use every day -- namely "+". What if we partially apply good old "+"? Do we write +2 or do we write 2+ and does it matter?

It doesn't matter, at least if we write it as a section, and as it turns out that is the only way to write such a thing, so there!

To use a partially applied infix operator like +2 as a function, we need to enclose it in parenthesis, i.e as (+2). Voila! We have a "section". This may well clue Haskell in that this is not the constant "+2". Subtraction is more troublesome and you should write (subtract 2).

As an example, suppose you had a list of numbers and wanted to add 13 to all the numbers. You could do this:

newlist = map (+13) [ 100, 200, 500 ]
This yields: [113,213,513]. You could equally well write the following and you would get the same result.
newlist = map (13+) [ 100, 200, 500 ]
Since we have written the partially applied infix as a section, Haskell doesn't fret about whether the first or second argument is missing. At least not for a commutative operator.

At this point you are probably wishing for a list of all the fibbonacci numbers that are less that 99999. Here is how to get it:

fibs = [0,1] ++ zipWith (+) fibs (drop 1 fibs)
main = putStrLn . show $ takeWhile (< 99999) fibs

[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025]
Here we have introduced a new Haskell utility function, takeWhile. It runs through the list, testing elements using the function given, and inserting them into a new list. When it hits an element where the test fails, it stops.

Here the function (which returns a Bool) is a section, namely (< 99999). Here the less than operator, while infix, is not commutative so we are not free to move it after the constant. Well actually we are, but it changes the sense of the test, so to obtain something equivalent to the above we need to write:

main = putStrLn . show $ takeWhile (99999 >) fibs


Have any comments? Questions? Drop me a line!

Tom's software pages / tom@mmto.org