Functional Programming Functional Vs. Imperative Referential

Functional Programming Functional Vs. Imperative Referential

Functional vs. Imperative Referential transparency Imperative programming concerned with “how.” The main (good) property of functional programming is Functional Programming referential transparency. Functional programming concerned with “what.” COMS W4115 Every expression denotes a single value. Based on the mathematics of the lambda calculus Prof. Stephen A. Edwards (Church as opposed to Turing). The value cannot be changed by evaluating an expression Spring 2003 or by sharing it between different parts of the program. “Programming without variables” Columbia University No references to global data; there is no global data. Department of Computer Science It is inherently concise, elegant, and difficult to create subtle bugs in. There are no side-effects, unlike in referentially opaque Original version by Prof. Simon Parsons languages. It’s a cult: once you catch the functional bug, you never escape. The Joy of Pascal Strange behavior Variables program example(output) This prints 5 then 4. At the heart of the “problem” is fact that the global data var flag: boolean; flag affects the value of f. Odd since you expect function f(n:int): int In particular begin f (1) + f (2) = f (2) + f (1) if flag then f := n flag := not flag else f := 2*n; gives the offending behavior flag := not flag Mathematical functions only depend on their inputs end They have no memory Eliminating assignments eliminates such problems. begin In functional languages, variables not names for storage. flag := true; What does this print? Instead, they’re names that refer to particular values. writeln(f(1) + f(2)); writeln(f(2) + f(1)); Think of them as not very variables. end Simple functional programming in A more complex function Currying ML - fun max a b = Functions are first-class objects that can be manipulated A function that squares numbers: = if a > b then a else b; with abandon and treated just like numbers. val max = fn : int -> int -> int % sml - max 10 5; - fun max a b = if a > b then a else b; Standard ML of New Jersey, Version 110.0.7 val it = 10 : int val max = fn : int -> int -> int - fun square x = x * x; - max 5 10; - val max5 = max 5; val square = fn : int -> int val it = 10 : int val max5 = fn : int -> int - square 5; - - max5 4; val it = 25 : int val it = 5 : int Notice the odd type: - - max5 6; int -> int -> int val it = 6 : int This is a function that takes an integer and returns a - function that takes a function and returns an integer. Tuples and arguments Polymorphism Recursion You can also pass and return tuples to/from functions: Reverse has an interesting type: ML doesn’t have variables in the traditional sense, so you can’t write programs with loops. - fun max(a,b) = if a > b then a else b; - fun reverse(a,b) = (b,a); val max = fn : int * int -> int val reverse = fn : ’a * ’b -> ’b * ’a So use recursion: - max (10,5); - fun sum n = This means it can reverse a two-tuple of any type and any val it = 10 : int = if n = 0 then 0 else sum(n-1) + n; other type: - fun reverse(a,b) = (b,a); val sum = fn : int -> int val reverse = fn : ’a * ’b -> ’b * ’a - reverse (10,5.2); - sum 2; - reverse (10,5); val it = (5.2,10) : real * int val it = 3 : int val it = (5,10) : int * int - reverse ("foo", 3.14159); - sum 3; - max (reverse (10,5)); val it = (3.14159,"foo") : real * string val it = 6 : int val it = 10 : int - sum 4; - val it = 10 : int Power operator Using the same thing twice The let expression You can also define functions as infix operators: Without variables, duplication may appear necessary: The easiest way is to introduce a local name for the thing we need multiple times: - fun x ˆ y = fun f x = = if y = 0 then 1 g(square(max(x,4))) + fun f x = = else x * (x ˆ (y-1)); (if x <= 1 then 1 let val ˆ = fn : int * int -> int else g(square(max(x,4)))); val gg = g(square(max(x,4))) - 2 ˆ 2; in One fix: use an extra function: val it = 4 : int gg + (if x <=1 then 1 else gg) - 2 ˆ 3; fun f1(a,b) = b + (if a <= 1 then 1 else b); end; val it = 8 : int fun f x = f1(x, g(square(max(x,4)))); - let is not assignment Data Types Lists - let val a = 5 in Programs aren’t very useful if they only manipulate Tuples have parenthesis, lists have brackets: = (let scalars. - (5,3); = val a = a + 2 Functional languages are particularly good at val it = (5,3) : int * int = in manipulating more complex data types. - [5,3]; = a val it = [5,3] : int list = end, You’ve already seen tuples, which is a fixed-length list of = a) specific types of things. Concatenate lists with @: = end; ML also has lists, arbitrary-length vectors of things of the - [1,2] @ [3,4,5]; val it = (7,5) : int * int same type. val it = [1,2,3,4,5] : int list Cons Other list functions Fun with recursion Add things to the front with :: (pronnounced “cons”) - null [1,2]; - fun addto (l,v) = val it = false : bool - [1,2,3]; = if null l then nil - null nil; val it = [1,2,3] : int list = else hd l + v :: addto(tl l, v); val it = true : bool - 5 :: it; val addto = fn : int list * int -> int list - null []; val it = [5,1,2,3] : int list val it = true : bool - addto([1,2,3],2); Concatenating is not the same as consing: - val a = [1,2,3,4]; val it = [3,4,5] : int list - [1,2] :: [3,4]; val a = [1,2,3,4] : int list stdIn: Error: operator and operand don’t agree [literal]- hd a; operator domain: int list * int list list val it = 1 : int operand: int list * int list - tl a; in expression: val it = [2,3,4] : int list (1 :: 2 :: nil) :: 3 :: 4 :: nil More recursive fun But why always name functions? Recursion with Lambda Expressions - fun map (f, l) = - map( fn x => x + 5, [10,11,12]); = if null l then nil val it = [15,16,17] : int list Q: How do you call something recursively if it doesn’t have = else f (hd l) :: map(f, tl l); a name? This is called a lambda expression: it’s simply an val map = fn : (’a -> ’b) * ’a list -> ’b list unnamed function. A: Give it one. - fun add5 x = x + 5; The fun operator is similar to a lambda expression: - let = val rec f = val add5 = fn : int -> int - val add5 = fn x => x + 5; = fn x => if null x then nil val add5 = fn : int -> int = else hd x + 1 :: f (tl x) - map(add5, [10,11,12]); - add5 10; = in f end val it = [15,16,17] : int list val it = 15 : int = [1,2,3]; val it = [2,3,4] : int list Pattern Matching Pattern Matching Call-by-need Functions are often defined over ranges More fancy binding Most imperative languages, as well as ML, use call-by-value or call-by-reference. All arguments are x if x ≥ 0 fun map (_,[]) = [] f (x) = evaluated before being passed (copied) to the callee. −x otherwise. | map (f,h :: t) = f h :: map(f,t); But some functional languages use call-by-need. Functions in ML are no different. How to cleverly avoid “_” matches anything - fun infinite x = infinite x; writing if-then: h :: t matchs a list, binding h to the head and t to the val infinite = fn : ’a -> ’b fun map (f,[]) = [] tail. - fun zero _ = 0; | map (f,l) = f (hd l) :: map(f,tl l); val zero = fn : ’a -> int Pattern matching is order-sensitive. This gives an error. - zero (infinite 2); fun map (f,l) = f (hd l) :: map(f,tl l) This doesn’t terminate in ML, but it does with call-by-need. | map (f,[]) = []; Call-by-need deterministic in the absence of side effects. Reduce Another Example Call-by-need Another popular functional language construct: Consider fun find1(a,b) = if b then true else (a = 1); fun reduce (f, z, nil) = z - fun find1(a,b) = Call-by-value: | reduce (f, z, h::t) = f(h, reduce(f, z, t)); = if b then true else (a = 1); reduce(find1, false, [1, 3, 5, 7] val find1 = fn : int * bool -> bool find1(1, find1(3, find1(5, find1(7, false)))) f reduce(f,z,a::b::c) If is “−”, is a − (b − (c − z)) find1(1, find1(3, find1(5, false))) - reduce( fn (x,y) => x - y, 0, [1,5]); - reduce(find1, false, [3,3,3]); find1(1, find1(3, false)) find1(1, false) val it = ˜4 : int val it = false : bool true - reduce( fn (x,y) => x - y, 2, [10,2,1]); val it = 7 : int - reduce(find1, false, [5,1,2]); Call-by-need: val it = true : bool reduce(find1, false, [1, 3, 5, 7] find1(1, find1(3, find1(5, find1(7, false)))) true The Lambda Calculus Bound and Unbound Variables Arguments Fancy name for rules about how to represent and evaluate In λx : ∗ 2 x, x is a bound variable. Think of it as a formal λx : λy : ∗ (+ x y) 2 expressions with unnamed functions. parameter to a function. is equivalent to the ML Theoretical underpinning of functional languages. “∗ 2 x” is the body. fn x => fn y => (x + y) * 2; Side-effect free.

View Full Text

Details

  • File Type
    pdf
  • Upload Time
    -
  • Content Languages
    English
  • Upload User
    Anonymous/Not logged-in
  • File Pages
    6 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