Functional Programming
Functional Programming
int, real and bool, and compound types built from other types using type constructors,
such as int * int, bool * int * real, int -> int, and int list. For each type there is a set of
values of that type. Values of type int correspond to mathematical integers. Values of type
int list are (finite) lists of integers. In SML you can only evaluate an expression if the
expression is well-typed, i.e., has a type.
Similarly, you can only use a declaration if it is well-typed, and when this happens the
declaration introduces bindings that associate types to names throughout a syntactically
determined region.
You can only use an expression, value or a bound name, in a manner that fits with its type.
Type-checking
Type-checking involves figuring out if an expression has a specific type. Given an expression
e and a type t it is pretty simple to check if e has type t, by appealing to some syntaxdirected type rules. For example:
A numeral, such as 0 or 1, has type int.
The infix arithmetic operators + and * can be used on two integers, or on two reals:
e1+e2 has type int if and only if e1 and e2 have type int.
e1*e2 has type int if and only if e1 and e2 have type int.
e1+e2 has type real if and only if e1 and e2 have type real.
e1*e2 has type real if and only if e1 and e2 have type real.
Strong Typing
This means that every value has a defined type, and we can check that type before evaluating
the value. Benefits
- many errors are caught before run-time
- use types to categorise and lookup functions
A type may be monomorphic, polymorphic, or an instance of a type class.
Note: we can always make a monomorphic instance of a polymorphic type.
E.g.
length :: [a] -> Int
(polymorphic)
lengthInt :: [Int] -> Int (monomorphic)
Type Checking Function Definitions
To check a function f :: t1 -> t2 -> -> tk -> t
when there are several cases, i.e.
f p1 p2 pk
| g1 = e1
| g2 = e2
| gl = el
We need to check that
- each of the guards gi is of type Bool
- the value of each ei is of type t
- each pattern pj matches some element of type tj.
E.g.
pattern p:q matches an element of type [t] when
Function Composition
TYPE INFERENCE
The advantages of strong static typing, where types are inferred by the compiler at compile
time, are now generally recognized. As a matter of fact languages such as Haskell, ML,
Mercury or Java all rely on strong typing. In order to gain programming flexibility
avoiding rejecting perfectly safe programs, the type inference algorithm should support
type polymorphism, i.e. it should allow programs to possess more than one type. Two
main options for polymorphism are universal types, where types are parametrized (this
concept is known as parametric polymorphism), and intersection types which introduce
an operator of intersection over types (their duals are the existential types and union
types).
The most popular type inference algorithm is the Damas-Milner algorithm which supports
parametric polymorphism with universal types. In the area of type systems for
programming languages there was a long search for systems more expressive than the
Damas-Milner type system and with decidable typability. In this search there have been a
few positive results in two distinct directions: extensions of the Damas-Milner approach
and systems based on intersection types
Type inference in all these systems rely on some notion of constraint resolution. The type
inference algorithm of the Damas-Milner system (used as the basis of the type systems of
functional programming languages such as ML and Haskell), uses unification to solve
constraints in the type language. Several extensions of this algorithm are based on
extensions of the unification algorithms with some kind of constraint resolution
mechanism.
modules
definitions
expressions
How is polymorphism used in functional programming languages?