Basic Syntax

Values, Functions & Types

Haskell is a purely functional programming language, which means that every variable is immutable (value) and every function is a first-class citizen.

-- values
x :: Int  -- declare x as an Int
x = 1     -- bind 1 to x
y = 2     -- bind 2 to y (type inferred)
z = x + y -- bind (x + y) to z

-- functions
add :: Int -> Int -> Int -- declare `add` as a function from Int to Int to Int
                         -- (can be seen as "take 2 Ints and return an Int")
add x y = x + y          -- declare `add x + y` as a `x + y`
add 1 2                  -- 3

-- types
Int      -- integer, fixed-precision integer
Integer  -- integer, arbitrary-precision integer
Float    -- floating-point number, single-precision
Double   -- floating-point number, double-precision
Bool     -- boolean, either True or False
Char     -- character, single unicode character
[a]      -- list, a sequence of elements of type `a`
(a, b)   -- tuple, a pair of elements of type `a` and `b`

Operators

-- arithmetic 
+ - *  -- (binary, infix)
div    -- (binary, prefix)

-- comparison (binary, infix)
== /= < <= > >=

-- logical
&& ||  -- (binary, infix)
not    -- (unary, prefix)
-- infix marker
4 `add` 2  -- add 4 2
4 `div` 2  -- div 4 2

-- function application
($) :: (a -> b) -> a -> b
($) f x = f x
$ add 1 2  -- 3

-- function composition
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g x = f (g x)
descSort = reverse . sort

List

Unlike imperative programming languages, List in Haskell is considered to be a primary data structure, and one of the most important and widely used types.

-- list of Ints
list = [1, 2, 3]
-- `:` put an element at the beginning of a list
list' = 4 : 5 : 6 : [] 
-- `++` concatenate two lists
list'' = list ++ list' ++ [7, 8, 9]

To work with lists, Haskell provides a set of useful functions:

head [1, 2, 3, 4]   -- 1
tail [1, 2, 3, 4]   -- [2, 3, 4]
init [1, 2, 3, 4]   -- [1, 2, 3]
last [1, 2, 3, 4]   -- 4
length [1, 2, 3, 4] -- 4
null []             -- True

Flow Control

Branching

-- multiple bindings
isZero :: Int -> Bool
isZero 0 = True
isZero _ = False  -- `_` wildcard, matches anything

-- if-then-else
max :: Int -> Int -> Int
max x y =
  if x > y then
    x
  else
    y

Recursion

In a pure functional language like Haskell, there is no way to change the value, making loops impossible. Instead, recursion is used to replace its functionality.

-- factorial
factorial :: Int -> Int
factorial 0 = 1                     -- base case, also termination condition
factorial n = n * factorial (n - 1) -- recursive case

-- fibonacci
fib :: Int -> Int
fib 0 = 0
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)

Custom Types

In Haskell, we can define our own types using type and data keywords.

-- type synonym
type Name = String
type Age = Int

-- data type
data Color = Red | Green | Blue  -- different constructors
data Calculation =
  Add Int Int    -- constructor with parameters
  | Sub Int Int
  | Mul Int Int
  | Div Int Int
calc :: Calculation -> Int
calc (Add x y) = x + y
calc (Sub x y) = x - y
calc (Mul x y) = x * y
calc (Div x y) = x `div` y

-- recursive data type
data PeaNum = Succ PeaNum | Zero
four :: PeaNum
four = Succ $ Succ $ Succ $ Zero

data Tree a = Leaf | Node (Tree a) a (Tree a)
tree :: Tree Int
tree = 
  Node (Node Leaf 1 Leaf) 2 (Node (Node Leaf 3 Leaf) 4 Leaf)