COMP4075: Lecture 3 Pure Functional Programming (1) Pure

COMP4075: Lecture 3 Pure Functional Programming (1) Pure

Pure Functional Programming (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 make 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 • Algorithm = 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 (logic programming 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 Prolog, 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 Control Flow (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 + c, b * c) |x<0 =leta=fyz |x>0 =(c+a,c * a) b=gyz where in (a + b, a * b) a = <exprA[y,z]> |x==0=letb=gyz b = <exprB[y,z]> c=gyz c = <exprC[y,z]> 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 = <exprA[y,z]> taken[] =[] g y z = <exprB[y,z]> take n (x:xs) = x : take (n-1) xs h y z = <exprC[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.

View Full Text

Details

  • File Type
    pdf
  • Upload Time
    -
  • Content Languages
    English
  • Upload User
    Anonymous/Not logged-in
  • File Pages
    7 Page
  • File Size
    -

Download

Channel Download Status
Express Download Enable

Copyright

We respect the copyrights and intellectual property rights of all users. All uploaded documents are either original works of the uploader or authorized works of the rightful owners.

  • Not to be reproduced or distributed without explicit permission.
  • Not used for commercial purposes outside of approved use cases.
  • Not used to infringe on the rights of the original creators.
  • If you believe any content infringes your copyright, please contact us immediately.

Support

For help with questions, suggestions, or problems, please contact us