<<

Pure (1)

The main focus of this module is on pure COMP4075: Lecture 3 functional programming to: Pure Functional Programming: • help you learn how to solve problems purely Introduction • help you understand the pros and cons of

Henrik Nilsson doing so • ultimately allow you to chose the right University of Nottingham, UK language/paradigm/techniques, or mix, for the task at hand.

COMP4075: Lecture 3 – p.1/28 COMP4075: Lecture 3 – p.2/28

Pure Functional Programming (2) Imperative vs. Declarative (1) • Imperative Languages: • Using Haskell as a medium of instruction as it is: - Implicit state. - the leading pure functional language - Computation essentially a sequence of - familiar to many of you from previous modules. side-effecting actions. • But the module is not primarily about Haskell: - Examples: Procedural and OO languages look for the underlying principles! • Declarative Languages (Lloyd 1994): • The use of Haskell here does not imply it is - No implicit state. the only good (functional) language: there are - many good languages out there. But grasping A program can be regarded as a theory. pure functional programming will you a - Computation can be seen as deduction better programmer irrespective of which from this theory. language you choose/have to use. - Examples: Logic and Functional Languages.

COMP4075: Lecture 3 – p.3/28 COMP4075: Lecture 3 – p.4/28 Imperative vs. Declarative (2) Imperative vs. Declarative (3) • Declarative programming has many benefits; Another perspective: e.g., facilitates formal reasoning, program • = Logic + Control transformations, etc. • Declarative programming emphasises the • Immediate payoff of declarative programming logic (“what”) rather than the control (“how”). permeating all code is that it allows intent to be stated much more clearly: what not how • Strategy needed for providing the “how”: does matter! - Resolution ( languages) • - However, implicit control and unconstrained Lazy evaluation (some functional and logic effects do not mix well: purity is prerequisite. programming languages) • Disciplined use of effects still possible in a - (Lazy) narrowing: (functional logic pure setting. programming languages)

COMP4075: Lecture 3 – p.5/28 COMP4075: Lecture 3 – p.6/28

No Control? Relinquishing Control Theme of this and next lecture: relinquishing Declarative languages for practical use tend to control by exploiting lazy evaluation. be only weakly declarative; i.e., not totally free of control aspects. For example: • Evaluation orders • Equations in functional languages are • Strict vs. Non-strict semantics directed. • Lazy evaluation • Order of patterns often matters for pattern • Applications of lazy evaluation: matching. - Writing clear and concise code • Constructs for taking control over the order of - Programming with infinite structures evaluation. (E.g. cut in , seq in - Circular programming Haskell.) - Dynamic programming

COMP4075: Lecture 3 – p.7/28 COMP4075: Lecture 3 – p.8/28 Evaluation Orders (1) Evaluation Orders (2)

Consider: Thus, in general, many possible reduction orders. sqr x = x * x Innermost, leftmost redex first is called dbl x = x + x Applicative Order Reduction (AOR). Recall: main = sqr (dbl (2 + 3)) sqr x = x * x Roughly, any expression that can be evaluated or dbl x = x + x reduced by using the equations as rewrite rules main = sqr (dbl (2 + 3)) is called a reducible expression or redex. Starting from main: Assuming arithmetic, the redexes of the body of main ⇒ sqr (dbl (2 + 3)) ⇒ sqr (dbl 5) main are: 2+3 ⇒ sqr (5 + 5) ⇒ sqr 10 ⇒ 10 * 10 ⇒ 100 dbl (2 + 3) sqr (dbl (2 + 3)) This is just Call-By-Value.

COMP4075: Lecture 3 – p.9/28 COMP4075: Lecture 3 – p.10/28

Evaluation Orders (3) Why Normal Order Reduction? (1)

Outermost, leftmost redex first is called Normal NOR seems rather inefficient. Any use? Order Reduction (NOR): • Best possible termination properties. main ⇒ sqr (dbl (2 + 3)) ⇒ dbl (2 + 3) * dbl (2 + 3) A pure functional languages is just the ⇒ ((2 + 3) + (2 + 3)) * dbl (2 + 3) λ-calculus in disguise. Two central theorems: ⇒ (5 + (2 + 3)) * dbl (2 + 3) - Church-Rosser Theorem I: ⇒ (5 + 5) * dbl (2 + 3) ⇒ 10 * dbl (2 + 3) No term has more than one normal form. ⇒ ... ⇒ 10 * 10 ⇒ 100 - Church-Rosser Theorem II: (Applications of arithmetic operations only con- If a term has a normal form, then NOR sidered redexes once arguments are numbers.) will find it. Demand-driven evaluation or Call-By-Need

COMP4075: Lecture 3 – p.11/28 COMP4075: Lecture 3 – p.12/28 Why Normal Order Reduction? (2) Exercise 1

• More declarative code as control aspects Consider: (order of evaluation) left implicit. fx=1 • More reusable components as usage implies gx=gx control flow main = f (g 0) • Better compositionality Attempt to evaluate main using both AOR and • More expressive power; e.g.: NOR. Which order is the more efficient in this case? (Count the number of reduction steps to - “Infinite” data structures normal form.) - Circular programming

COMP4075: Lecture 3 – p.13/28 COMP4075: Lecture 3 – p.14/28

Strict vs. Non-strict Semantics (1) Strict vs. Non-strict Semantics (2)

• ⊥, or “bottom”, the undefined value, Again, consider: representing errors and non-termination. fx=1 • A function f is strict iff: gx=gx f ⊥ = ⊥ What is the value of f (0/0)? Orof f (g 0)? • AOR: f (0/0) ⇒ ⊥; f (g 0) ⇒ ⊥ For example, + is strict in both its arguments: Conceptually, f ⊥ = ⊥; i.e., f is strict. (0/0)+1 = ⊥ +1 = ⊥ • NOR: f (0/0) ⇒ 1; f (g 0) ⇒ 1 1 + (0/0) = 1+ ⊥ = ⊥ Conceptually, f ⊥ = 1; i.e., f is non-strict. Thus, NOR results in non-strict semantics.

COMP4075: Lecture 3 – p.15/28 COMP4075: Lecture 3 – p.16/28 Lazy Evaluation (1) Lazy Evaluation (2)

Lazy evaluation is a technique for Recall: sqr (dbl (2 + 3)) implementing NOR more efficiently: sqr x = x * x ⇒ • • A redex is evaluated only if needed. dbl x = x + x dbl (2 + 3) * ( ) main = • Sharing employed to avoid duplicating sqr (dbl (2+3)) ⇒ ( (2 + 3) + (•)) * (•) redexes. • Once evaluated, a redex is updated with the ⇒ ( 5 + (•)) * (•) result to avoid evaluating it more than once. As a result, under lazy evaluation, any one redex ⇒ 10 * (•) is evaluated at most once. ⇒ 100

COMP4075: Lecture 3 – p.17/28 COMP4075: Lecture 3 – p.18/28

Lazy Evaluation (3) Lazy Evaluation (4) “Evaluated at most once” needs to be interpreted with care: it referes to individual redex instances. Memoization means caching function results to avoid re-computing them. Also distinct from For example: laziness. • (1 + 2) * (1 + 2) 1 + 2 evaluated twice as not the same redex. • f x = x + y where y = 6 * 7 6 * 7 evaluated whenever f is called. A good compiler will rearrange such computations to avoid duplication of effort, but this has nothing to do with laziness.

COMP4075: Lecture 3 – p.19/28 COMP4075: Lecture 3 – p.20/28 Exercise 2 Implicit (1)

Evaluate main using AOR, NOR, and lazy • Leaving the control flow implicit often allows evaluation: for succinct, to-the-point definitions. fxyz = x z * • While not a “game changer”, the improvement gx =f(x * x) (x * 2) x over explicit control flow can be substantial. main = g(1+2) (Only consider an applications of an arithmetic operator a redex once the arguments are numbers.) How many reduction steps in each case? Answer: 7, 8, 6 respectively

COMP4075: Lecture 3 – p.21/28 COMP4075: Lecture 3 – p.22/28

Implicit Control Flow (2) Implicit Control Flow (3)

Consider: Avoiding duplication of code and computation in foo x y z a strict language: |x<0 =(a+b,a * b) foo x y z | x == 0 = (b + , b * c) |x<0 =leta=fyz |x>0 =(c+a,c * a) b=gyz where in (a + b, a * b) a = |x==0=letb=gyz b = c=gyz c = in (b + c, b * c) Lazy evaluation ensures that only two of a, b, c |x>0 =letc=gyz are evaluated, depending on which ones are a=fyz needed in the case determined by x. in (c + a, c * a)

COMP4075: Lecture 3 – p.23/28 COMP4075: Lecture 3 – p.24/28 Implicit Control Flow (4) Infinite Data Structures (1)

where take0_ =[] f y z = taken[] =[] g y z = take n (x:xs) = x : take (n-1) xs h y z = from n = n : from (n+1) (Syntax still Haskell-like to facilitate comparison with previous version.) nats = from 0

main = take 5 nats

COMP4075: Lecture 3 – p.25/28 COMP4075: Lecture 3 – p.26/28

Infinite Data Structures (2) Reading

main ⇒1 take 5 (•) ⇒4 0:take 4 (•) • John W. Lloyd. Practical advantages of declarative programming. In Joint Conference ⇒6 0:1:take 3 (•) ⇒8 ... on Declarative Programming, GULP-PRODE’94, 1994. ⇒ • ⇒ 0:1:2:3:4:take 0 ( ) [0,1,2,3,4] • John Hughes. Why Functional Programming Matters. The Computer Journal, 32(2):98–197, April 1989. nats ⇒2 from 0 ⇒3 0: from 1

⇒5 0:1: from 2 ⇒7 ... ⇒ 0:1:2:3:4: from 5

COMP4075: Lecture 3 – p.27/28 COMP4075: Lecture 3 – p.28/28