GNU General Public License v3.0 licensed. Source available on github.com/zifeo/EPFL.
Fall 2014: Functional Programming
[TOC]
- imperative programming : modifying mutable variable, assignments, loops, too close to word by word (Von. Neumann)
- oriented-object programming : orthogonal to other paradigm
- functional programming : theory, data types, operations, laws between values and operation, no mutations
- non-primitive expression
- take leftmost operator
- evaluate its operands
- apply the operator to the operands
- primitive types : as in Java but capitalized
- call-by-value :
def power(x: Double, y: Int): Double = ...
- evaluate all args (left to right)
- replace function application by the function
- replace formal parameters by actual args
- call-by-name :
def power(x: => Double, y: => Int): Double = ...
- apply the function to unreduced args
- substitution model : reduce an expression to a value (no side effect) called
$\lambda$ -calculus - if call-by-value terminates, then call-by-name terminates too
- def : by-name
- value : by-value
- return value : last element of block
- rewriting rule :
$[v_1/x_1,\ldots,v_n/x_n]B$ means expression$B$ in which all occurences of$x_i$ have been replaced by$v_i$ - tail recursion : if a function calls itself as its last action, stack can be reused, it is tail recursive (iterative),
@tailrec
- anonymous function :
(x: Int) => x*x
is the short way (syntactic sugar) of definingdef f(x: Int)=x*x;f
- high-order functions : take a function as or/and return a function
- currying :
def f(arg1)...(argN)=E
equivalent todef f = (arg1 => ... (argN => E))
-
def sum(f: Int => Int)(a: Int, b: Int): Int = ...
has type(Int => Int) => (Int, Int) => Int
- functional type associate to the right
-
override
to force redefinition- private member inside classes
class Rational(x: Int, y: Int) { priate def... }
- self reference :
this
- precondition :
class ... { require(y > 0, "blabl") }
throws anIllegalArgumentException
- assert : like precondition but emits a
AsssertionError
- primary constructor : takes parameters of the class and executes all statements in the class body
- auxiliary constructors :
def this(x: Int) = this(x, 1)
- infix notation : any method with a parameter can be used like an infix operator
- relaxed identifiers : alphanumeric, symbolic, underscore (
x1 * +?%& vector_++
) - precedence rules : letters, |, ^, &, < >, = !, :, + -, * / %, others
def unary_op
is called whenop(object)
- method terminating with a colon associate to the right (not left)
- abstract classes can contain members which are missing an implemenentation
- if no superclass, standard class Object in Java is assumed
- direct and indirect superclasses of a class are called base classes
- object : single-object class called singleton, this is a value
- programs :
object Hello { defmain(agrs: Array[String]) = println("hw") }
- dynamic binding like in Java
- package and import :
import week3.{Rational, Hello}
orimport week3._
(wildcard) - traits : like interface but with concrete methods but cannot have value parameters
- types
- Any : base type of all types (==, !=, equals, hashCode, toString)
- AnyRef : base type of all reference types (java Object)
- AnyVal : base type of all primitive types
- Nothing : subtype of every other type
- exceptions : similar to Java's
- can be set to null if it is a subclass of Object (type is then Null)
- value parameters abreviation :
class Ex(val head: Int)
- type parameters :
class Cons[T](val head: T)
andsingleton[T](elem: T)
direclty inferred in most of the case - function are treated as objects in Scala : a function A => B is an abbreviation for scala.Function1[A,B] which is
trait Function[A,B] { def apply(x: A): B }
- types bounds :
def f[S <: T](r: S)
where<:
means S subtype of T and>:
means supertype of T (even mix both one after the other) - Liskov Substitution Principle : if A <: B, then everything one can to do with a value of type B one should also be able to do with a value of type A
- variance : mutable is not covariant and are if some conditons are met, if A <: B
- C[A] <: C[B] covariance C[+A]
- C[A] >: C[B] contravariant C[-A]
- C[A] nothing C[B] nonvariant C[A]
- in general : function contravariant in args and covariant in result
trait Function1[-T,+U] { def apply(x: T): U }
- covariant type may appear in lower bound
- contrevariant type may appear in upper bound
- case classes : define companion objects with apply methods
- pattern match :
e match { case Number(n) => n; case Sum(e1, e2) => ... }
- forms of pattern : contructors, variable (always lowercase), wildcard (_), constants
- immutable, recursive, homogeneous type
- empty list object Nil
- constrution operation :: :
1 :: 2 :: Nil
same asNil.::(2).::(1)
- list operations
- head : first element
- tail : list composed of all elements except first
- isEmpty
- length
- last
- init : list consisting of all elements exception the last one
- take n : n first element of the list
- drop n : rest of list after having taken n
- (n) : element n
- ++ ys : add a list to another
- reverse
- updated(n, x)
- indexOf x
- contains x
- splitAt n
- pattern match : Nil, p :: ps, List(p1,...,pN)
- tuple :
val tuple = (e1,...eN)
, access to elementtuple._i
, can be pattern matched - implicit parameters : avoid passing some type argument
msort(implicit ord: Ordering)
- high-order list operations
- map f
- filter f
- filterNot f
- partition f : (filter f, filterNot f)
- takeWhile f
- dropWhile f
- span f : (takeWhile f, dropWhile p)
- wildcard function :
((x, y) => x*y)
is equivalent to(_*_)
- list reduction operation
- reduceLeft op : ((x1 op x2) op ...) op xN
- foldLeft z op : ((z op x1) op ...) op xN
- foldRight z op : x1 op (... (xN op z))
- structural induction : to prove a property P(xs) for all lists xs, show P(Nil) holds (base case), for a list xs and some element x show if P(xs) holds then P(x :: xs) also holds (induction step)
- vector : better balanced than List
- +: : add new element in front
- :+ : add new element at end
- base class of List and Vec are Seq and Iterable
- Arrays and Strings support same operations as Seq but come from Java so no subclass
- Range : evenly spaced integers
- x until y
- x to y
- r by s
- more on Seq :
- exists p
- forall p
- zip ys : pairs down elements
- unzip
- flatMap f
- sum
- product
- max
- min
- pattern match on map :
x.map(xy => xy._1*xy._2)
is equivalent tox.map{ case (x,y) => x*y }
- for-expression :
p filter (i => i > 20) map (i => i.name)
is equivalent to `for ( i <- p if i > 20) yield i.name - for translation
for (x <- e1) yield e2
ise1.map(x => e2)
for (x <- e1 if f; s) yield e2
isfor (x <- e1.withFilter(x => f); s) yield e2
for (x <- e1; y <- e2; s) yield e3
ise1.flatMap(x => for (y <- e2; s) yield e3)
- Set : unordered, no duplicates, contains is the fundamental operation- for queries
- Map
- constructor
Map("i" -> 1, "j" -> 2)
- tuple iterable
- are function type key => value :
capitalOfCountry("US")
gives NoSuchElementException is not found - result of get is Option value : Some(x) and None
- default values : withDefaultValue turns map into a total function
- constructor
- repeated parameter :
Polynom(bindings: (Int, Double)*)
get all parameters - queries
- sortWith p
- sorted
- groupBy e
- streams : like List but tail elements is computed on demand
- #:: to replace ::
- by-name evaluation : everything in recomputed
- strict evalutation : normal parameters and val definition (default evaluation)
- lazy evaluation : delay until asked
lazy val x = expr
- infinite streams :
def from(n: Int): Stream[Int] = n #:: from(n+1)