Types#
Features#
Strong typing
Static
Can be automatically inferred
Basic types#
Char
Bool
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
'a'
:: symbol
Prelude> 'a' :: Char -- Explicit signature
'a'
Function calls#
Prelude> odd 3
True
Prelude> compare 2 3
LT
e.g. LT EQ GT
Prelude> (compare 2 3) == LT
True
Prelude> compare (sqrt 3) (sqrt 6) -- Parentheses cannot be removed here
LT
Composite data types: Lists and Tuples#
Differences?#
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.
Recursiveness#
[[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]
1
Prelude> head []
*** Exception: Prelude.head: empty list
Prelude> tail "list"
"ist"
ghci> last [1,2,3]
3
Prelude> take 2 [1, 2, 3, 4, 5]
[1,2]
Prelude> drop 2 [1, 2, 3, 4, 5]
[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
"foo"
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))
(4,"am",(16,True))
Prelude> :t ()
() :: ()
Prelude> fst (1, 'a')
1
Prelude> snd (1, 'a')
'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)
Purity#
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
Variables#
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.
e.g.:
[myDrop.hs]
-- 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".
Recursion#
Note lazy evaluation, recursion -> termination of recursion -> recursion returns
Explanation
e.g. [myDrop.hs]
Polymorphism#
Parametric polymorphism#
Prelude> :type last
last :: [a] -> a
ghci> last [1,2,3]
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.
Missing#
Haskell does not have subtyping polymorphism or coercion polymorphism.
Types of multi-parameter functions#
e.g.
Prelude> :type take
take :: Int -> [a] -> [a]