One thing I missed before was comments. A single line comment begins with two or more consecutive -'s. Nested comments use "{-" and "-}" to end blocks.

Names of types are capitalized, such as Num and Bool. The names of values are not typed. True and False are types, not values. The

`::`

notation should be read as "has type". Haskell uses type inference to infer types which is why we didn't need to give type definitions before. Haskell function definitions are curried so a function of N arguments is treated as N functions of one argument, hence f x y z = x+y+z would have type `f :: (Num a) => a -> a -> a -> a`

.For example, consider the function

`add5`

which is a poor man's map, permanently specialized to add 5 to each element in the list:

add5 [] = []

add5 (x:xs) = (x+5):add5(xs)

Without any type definitions added the compiler infers the types as

`add5 :: (Num a) => [a] -> [a]`

. This reads as a function where a is of type Num returning a list of a. If I wanted to write the type definition explicitly I could add `add5 :: [Int] -> [Int]`

which will now restrict it to only mapping from Int to Int. Attempting to use this on any other type will be an error. The type signature itself is also checked by the compiler, so putting something nonsensical here will be caught at compile time.Putting this all together we can write map as:

mymap :: (x -> y) -> [x] -> [y]

mymap f [] = []

mymap f (x:xs) = f x:mymap f xs

Function application has high precedence so this minimizes the amount of parenthesis you have to use (that's a difficult habit to break from Lispy languages).

On a side note, one of the neat features is pattern matching with a guard. The guard is specified after the pattern with the | (pipe) symbol (I read this as "where"). In the example below, the guard clause means we can choose whether to include things in the filtered list or not.

myfilter :: (x -> Bool) -> [x] -> [x]

myfilter f [] = []

myfilter f (x:xs) | f x = x:myfilter f xs

| otherwise = myfilter f xs