Programming Languages Rational Numbers Rational Numbers

advertisement
CSE 130 : Fall 2007
Programming Languages
Previously: Polymorphism enables Reuse
• Can reuse generic functions:
map :’a * ’b → ’b * ’a
filter: (’a → bool * ’a list) → ’a list
rev: ’a list → ’a list
length: ’a list → int
swap: ’a * ’b → ’b * ’a
Lecture 9: Modules and Signatures
sort: (’a → ’a → bool * ’a list) → ’a list
Ranjit Jhala
UC San Diego
compose: (’a → ’b * ’c → ’b) → ’c → ’a
• If function (algorithm) is “independent” of
type, can reuse code for all types !
Today: Divide-Conquer Programming
• We’ve seen very small programs so far
– 4 yr education chopped into quarters
• Typical software is thousands, millions LOC
Rational Numbers
• Say you want a software package that uses
rational numbers heavily
• Remember rationals ?
– Ratio of two integers: m/n
• How can a PL help to manage complexity ?
– Only one technique humans know about
• Well, seems simple enough:
– Use a pair of integers to represent rationals!
– OCaml type: int * int
Rational Numbers
• Well, every pair of isn’t a rational…
– (5,0) ?
– 2nd elem “denominator” must not be zero
Many functions related to type
module Name =
struct
bindings
Pack into a module
• Types, Values, Exceptions
bound in Name are:
– Name.t, Name.x, Name.f
• How do we print a rational ?
• Bind module to diff name
• When are two rationals “equal” ?
– (6,4) and (30,20) ?
– Need to normalize rationals
– But need to fix to_string
end
module NewName = Rational;;
NewName.add;;
…
NewName.to_string;;
…
1
Many functions related to type
module Name =
struct
bindings
Pack into a module
• Types, Values, Exceptions
bound in Name are:
Many functions related to type
structure Name =
struct
bindings
• Can pack into a module
• Variables, functions, exceptions
“bound” in Name are:
– Name.x , Name.f
– Name.t, Name.x, Name.f
• Bind module to diff name
• Open module
• Can open module
– Everything comes tumbling out
end
open Rational;;
add;;
…
to_string;;
…
Information Hiding
• Prevent the client from passing in any pair
– Hide implementation from client
end
• Not much better than diff files!
– Whats the problem ?
Specify Interfaces by Signatures
module type SIGNAME =
sig
type-bindings
end
• Control usage by restricting the interface
• Interface: what any client needs to know
module Name: SIGNAME =
struct
bindings
end
Signature = Type of a module
• Specifies what module provides
– types
– values and their types
– exceptions and their types
Name : SIGNAME
• Name implements SIGNAME
• Code using Name knows
only the entities in signature
Implementing a Signature
Hiding
• ML checks if Name:SIGNAME
• SIGNAME can hide bindings of Name
– Name must contain all the bindings in SIGNAME
– May and usually will, have more bindings
module type RATIONAL =
sig
exception BadRational
type rational = …
val from_int : …
val to_string : …
val add : …
end
module Rational =
sig
exception BadRational
type rational = int * int
let gcd (x,y) = …
let normalize (x,y) = …
let from_int (x,y) = …
let to_string = …
let add r1 r2 = …
end
– Bindings not in SIGNAME are hidden (private)
– Outside Name, i.e. to clients, invisible
module type RATIONAL =
sig
exception BadRational
type rational = …
val from_int : …
val to_string : …
val add : …
end
module Rational =
sig
exception BadRational
type rational = int * int
let gcd (x,y) = …
let normalize (x,y) = …
let from_int (x,y) = …
let to_string = …
let add r1 r2 = …
end
2
Still doesn’t solve problem
Abstracting Types
Inept client:
• SIGNAME can hide type definitions of Name
Rational.add (2,3) (0,0);;
– Type defs not in SIGNAME are abstract
– Cannot create values of abs. type outside Name
Loops forever…
Solution: Make types abstract
• Client knows “name” of type, not implementation!
• Cannot directly access or create values of abs type
• Restrict the interface to enforce invariants
Benefits of hiding information
1. Forces divide and conquer:
– Restrict interactions
– Can reason about the code in small pieces
• Instead of a massive jungle
• Know exactly how clients can use structure
2. Plug-and-Play:
– Can easily replace implementation
(as long as signature matches)
– Doesn’t break clients
– Separate compilation
module type RATIONAL =
sig
exception BadRational
type rational = int*int
val from_int : …
val to_string : …
val add : …
end
module Rational =
sig
exception BadRational
type rational = int * int
let gcd (x,y) = …
let normalize (x,y) = …
let from_int (x,y) = …
let to_string = …
let add r1 r2 = …
end
Another example: Stack
module Stack =
struct
type 'a stack = Empty | Push of ('a * 'a stack)
exception EmptyStack
let newstack () = Empty
let push s e = Push (e,s)
let pop s =
match s with Empty -> raise EmptyStack
| Push(x,_) -> x
let is_empty s =
match s with Empty -> true
| _ -> false
let rec toList s =
match s with Empty -> []
| Push(top,bot) -> top :: (toList bot)
let to_string f s =
String.concat "," (List.map f (toList s))
end
module type NE1 =
sig
type name = string
type expr
type nExpr = name*expr
val makeExpr:string*(int*int->int)->(nExpr*nExpr) -> nExpr
val constExpr : int -> namedExpr
val eval : namedExpr -> int
end
module type NE2 =
sig
type name
type expr
type nExpr = name*expr
val makeExpr:string*(int*int->int)->(nExpr*nExpr) -> nExpr
val constExpr : int -> namedExpr
val eval : namedExpr -> int
end
module type NE2 =
sig
type name
type expr
type nExpr = name*expr
val makeExpr:string*(int*int->int)->(nExpr*nExpr) -> nExpr
val constExpr : int -> namedExpr
val eval : namedExpr -> int
end
module type NE3 =
sig
type name
type expr
type nExpr
val makeExpr:string*(int*int->int)->(nExpr*nExpr) -> nExpr
val constExpr : int -> namedExpr
val eval : namedExpr -> int
end
3
Another example: PA4
module type EXPR =
sig
type 'a oper = string * ('a list -> 'a)
type 'a expr
val buildF : 'a oper -> 'a expr list -> 'a expr
val buildX : int -> 'a expr
val exprToString : 'a expr -> string
val eval_fn : 'a expr -> 'a list -> 'a
end
Moral
“Perfection is achieved,
not when there is something left to add,
but when there is nothing left to take away.”
- Antoine de St. Exupery,
“The little prince”
PA4: Write two implementations of the above signature.
i.e. implement the expr type in two ways …
Client, i.e. art.ml, remains the same
Key components of a lang
• Units of computation
Deconstructing OCaml
• Types
The anatomy of a
Programming language
• Memory model
In OCaml
Units of computation
4
In OCaml
In Java/Python
• Expressions that evaluate to values
• Everything is an expression
–
–
–
–
–
–
int, bool, real
if-then-else
let-in
match
fun x -> x+1
e1 e2
In Java/Python
In Prolog
• Store and update commands
• Message sends
In Prolog
• Logical facts
• Inference rules
Types
Mexican(CARNITAS)
Food(CARNITAS)
“Fact”
“Fact”
Mexican(X) Æ Food(X) ⇒ Delicious(X)
Delicious(CARNITAS)
“Rule”
“Fact”
5
Types
In OCaml: Static typing
• Used to classify things created by the
programmer
• Types are assigned statically at compile
time
• Classification used to check what can be
done with/to those things
• Without computing values
• Rules state when expressions are typecorrect
Γ ⊢ e1:T1 → T2
Γ ⊢ e2: T1
Γ ⊢ e1 e2 : T2
In OCaml: Static typing
In Python: Dynamic typing
• How can one reuse code for different
types?
• Types assigned to values/objects as they
are computed, ie: dynamically
– parametric types: ‘a * ‘b -> ‘b * ‘a
– implicit forall
• Type “discovered” (inferred)
automatically from code
• Before an operation is performed, check
that operands are compatible with
operation
– less burden on the programmer
In Python: Dynamic typing
Dynamic vs. Static, OO vs. Func
• More programs are accepted by compiler
⇒ More flexible
[1, “abc”, 1.8, [ “efg”, 20]]
let x = if b then 1 else “abc”
let y = if b then x + 1 else x ^ “efg”
Statically typed
Dynamically
typed
OO
Functional
6
Dynamic vs. Static, OO vs. Func
OO
Statically typed
Dynamically
typed
Java
Python,
Smalltalk,
Diesel
Polymorphism
• PL that is polymorphic + dynamically typed ?
• Every dynamically typed PL is polymorphic
– functions simply work on any datatype that can be
operated on at runtime
• Explicit polymorphism in statically typed PL
Functional
Ocaml, Haskell
Lisp/Scheme
– assign at compile time a general polymorphic type
Data model in functional langs
• Environment of bindings (phonebook)
Memory/Data model
aka: what do variables refer to?
• Never change a binding
– add new bindings at the end of phonebook
Data model in functional langs
Data model in OO langs
• Variables = names in phonebook
• Most recent entry looked up during eval
• Variables are named cells in memory
• Can change them by assigning into them
Closures:
• Environment “frozen” in function value
• Variables = names of objects on the heap
– behavior of function cannot be changed later
– easier reasoning, debugging
• x = x + 10
7
Data model in Prolog
• Variables = unknowns to solve for
Mexican(CARNITAS)
Final words on functional
programming
Food(CARNITAS)
∀ X Mexican(X) Æ Food(X) ⇒ Delicious(X)
Delicious(Y)?
Q: What is delicious?
A: CARNITAS!
What’s the point of all this?
Advantages of functional progs
• Functional programming more concise
“one line of lisp can replace 20 lines of C”
(quote from http://www.ddj.com/dept/architect/184414500?pgno=3)
• Recall reverse function in OCaml:
let reverse = fold (::) [];;
• How many lines in C, C++?
Don’t be fooled
Can better reason about progs
• PA made you do certain things using fold
in order to force you to think about it,
fold may not be the easiest way to do it.
• No side effects. Call a function twice
with same params, produces same value
• But many cases where map and fold make
life A LOT EASIER.
• As a result, computations can be
reordered more easily
• They can also be parallelized more easily
8
An example: google
What is MapReduce?
• To make their data processing scale
better, google devised a programming
model called MapReduce
• Programming model based on map, fold
• Used widely inside google (over 800
instances of it used in the google source
tree in late 2004, and still counting)
• Because of the functional aspect
So what?
Remember
• Form the authors: “Inspired by similar
primitives in LISP and other languages”
• The next time you use google, think of
how functional programming has inspired
key technology behind their engine
http://labs.google.com/papers/mapreduce-osdi04-slides/index-auto-0003.html
• The point is this: programmers who only
know Java/C/C++ would probably not
have come up with this idea
– apply a function on each data element (map)
– aggregate the results (fold)
– easily parallelized ⇒ highly scalable
• And of course:
“Free your mind”
-Morpheus
• Many other similar examples in industry
9
Download