𝕴 𝖉𝖔 𝖒𝖆𝖌𝖎𝖈

Haskell Notes 2



Strong typing


Can be automatically inferred

Basic types#

Int Common Fixed-width integers, usually 32/64 bits wide
Integer Uncommon Arbitrary-precision integers
Double Common Floating-point numbers
Float Uncommon Slow in computation
Prelude> :type 'a'
'a' :: Char

Prelude> 'a'            -- Automatically inferred

:: symbol
Prelude> 'a' :: Char    -- Explicit signature

Function calls#

Prelude> odd 3

Prelude> compare 2 3
e.g. LT EQ GT

Prelude> (compare 2 3) == LT

Prelude> compare (sqrt 3) (sqrt 6)  -- Parentheses cannot be removed here

Composite data types: Lists and Tuples#


Lists can have any length and can only contain values of the same type.

Tuples have a fixed length but can contain values of different types.


[[Int]] is a list containing values of type [Int], and [Int] is a list containing values of type Int, replacing the type variable a.

Lists [] and head, tail, last; take, drop#

head takes the first element, tail takes all except the first
last takes the last element

Prelude> head [1, 2, 3, 4]

Prelude> head []
*** Exception: Prelude.head: empty list

Prelude> tail "list"
ghci> last [1,2,3]
Prelude> take 2 [1, 2, 3, 4, 5]
Prelude> drop 2 [1, 2, 3, 4, 5]
Prelude> drop 4 [1, 2]
Prelude> drop (-1) "foo"    -- When the first argument is less than or equal to 0, the drop function returns the entire input list

Tuples () and fst, snd#

Size is at least 2

The type of a tuple is determined by the number, position, and type of its elements

Prelude> :type (True, "hello")
(True, "hello") :: (Bool, [Char])
Prelude> (4, ['a', 'm'], (16, True))
Prelude> :t ()
() :: ()
Prelude> fst (1, 'a')

Prelude> snd (1, 'a')

Use cases for tuples e.g.#

  • If a function needs to return multiple values, they can be wrapped in a tuple and returned as the function's value.

  • When a fixed-length container is needed but there is no need for a custom type, tuples can be used to wrap values.

Passing expressions to functions using parentheses#

a b c d is equivalent to (((a b) c) d)


Functions with side effects are called impure functions, while functions without side effects are called pure functions.

Side effects refer to the ability of a function to modify global variables, depending on the value at a certain moment. Essentially, side effects are an invisible input or output of a function. By default, Haskell functions are pure: the result of a function depends only on the explicitly passed parameters.

The type signature of impure functions starts with IO, as shown in the following example:

Prelude> :type readFile
readFile :: FilePath -> IO String
-- The type signature of impure functions starts with IO

Loading .hs files#

Use :load in ghci


Once a variable is bound (i.e., associated) to an expression, its value does not change. This is not possible in .hs files, but it is possible in the ghci command line.

e.g. [Assign.hs]

Conditional evaluation#

The types of different branches must be the same.


-- file: ch02/myDrop.hs

myDrop :: Int -> [a] -> [a]
myDrop n xs = if n <= 0 || null xs    -- ## Usage of null
              then xs   -- Indentation must not be omitted
              else myDrop (n - 1) (tail xs) -- Indentation must not be omitted

-- Substitution and rewriting
-- Variables n and xs are substituted
-- myDrop function is rewritten

-- ## Lazy evaluation
-- It can also be written in one line e.g. [myDropX.hs](ch02/myDrop.hs)

-- ## Conditional evaluation
-- null xs not equals to xs == null
-- The expressions after then and else are called "branches",
-- and the types of different branches must be the same.
-- if is not a branch.

-- e.g.
-- Prelude> if True then 1 else "foo"

-- <interactive>:2:14:
--     No instance for (Num [Char])
--         arising from the literal `1'
--     Possible fix: add an instance declaration for (Num [Char])
--     In the expression: 1
--     In the expression: if True then 1 else "foo"
--     In an equation for `it': it = if True then 1 else "foo"

Can be simplified to one line [myDropX.hs]

-- file: ch02/myDropX.hs
myDropX n xs = if n <= 0 || null xs then xs else myDropX (n - 1) (tail xs)

Lazy evaluation#

e.g. [isOdd.hs]

-- file: ch02/isOdd.hs
isOdd n = mod n 2 == 1

-- ghci> isOdd (1+2)
-- True

--in general:XXXXXX
-- first: (1+2)
-- second: mod 3 2
-- third: 1 == 1
-- True

--but in haskell:
--(1+2) is evaluated last! It is only computed when it is actually needed to calculate the value of isOdd (1 + 2).
--The record of unevaluated expressions is called a "chunk".


Note lazy evaluation, recursion -> termination of recursion -> recursion returns
e.g. [myDrop.hs]


Parametric polymorphism#

Prelude> :type last
last :: [a] -> a
ghci> last [1,2,3]

If a list of type [Char] is passed to last, the compiler will substitute Char for all occurrences of a in the type signature of the last function, resulting in a last function of type [Char] -> Char. This can be done with Int or any other type. This type of polymorphism is called parametric polymorphism.

Parametric polymorphism has no knowledge of the actual type of the input parameter.


Haskell does not have subtyping polymorphism or coercion polymorphism.

Types of multi-parameter functions#


Prelude> :type take
take :: Int -> [a] -> [a]
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.