Evolution of Concepts in Programming Languages

Programming Languages---Course 66.443

General Information

Reading

UTOPIA 84? 94? 04?

The Fundamental Role of Abstraction

Data Abstraction

Object-oriented Modeling and Programming

Control Abstraction

Support for Reasoning

Testing vs. Program Verification

Support for Large Projects

The Role of Modularity in Large Projects

Overview of ML

Some Examples of ML Programming

raju.cs.rpi.edu% sml
Standard ML of New Jersey, Version 75, November 11, 1991
Arrays have changed; see Release Notes
val it = () : unit

- odd(3);          
std_in:2.1-2.3 Error: unbound variable or constructor odd

- 7 mod 2;
val it = 1 : int

- ~7 mod 2;
val it = 1 : int

- ~7 rem 2;
val it = ~1 : int

- fun odd(n) = (n mod 2 = 1);
val odd = fn : int -> bool

- odd(3);
val it = true : bool


- (* iota(n) returns the list [0,1,2,...,n-1] *)
- fun iota(n) = if n = 0 then nil else iota(n-1) @ [n-1];
val iota = fn : int -> int list

- iota(12);
val it = [0,1,2,3,4,5,6,7,8,9,10,11] : int list

- rev(it);
val it = [11,10,9,8,7,6,5,4,3,2,1,0] : int list

- (* another (algorithmicly better) way to define iota *)
- fun reverse'iota(n) = if n=0 then nil else (n-1)::reverse'iota(n-1);
val reverse'iota = fn : int -> int list

- reverse'iota(8);
val it = [7,6,5,4,3,2,1,0] : int list

- fun iota(n) = rev(reverse'iota(n));
val iota = fn : int -> int list
- iota(10);
val it = [0,1,2,3,4,5,6,7,8,9] : int list

- odd(hd(it));
val it = false : bool

- val l1 = iota(10);
val l1 = [0,1,2,3,4,5,6,7,8,9] : int list

- odd(hd(l1));
val it = false : bool

- tl(l1);
val it = [1,2,3,4,5,6,7,8,9] : int list

- odd(hd(it));
val it = true : bool

- (* count(l, p) returns the number of elements of list l that make 
-    predicate p true *)
- fun count(l, p) = if null(l) then 0 else
=   count(tl(l),p) + (if p(hd(l)) then 1 else 0);
val count = fn : 'a list * ('a -> bool) -> int

- count(l1,odd);
val it = 5 : int

- count(l1,true);
std_in:20.1-20.14 Error: operator and operand don't agree (tycon mismatch)
  operator domain: int list * (int -> bool)
  operand:         int list * bool
  in expression:
    count (l1,true)

- fun always(n) = true;
val always = fn : 'a -> bool

- count(l1,always);   (* count(l,always) = length(l) *)
val it = 10 : int

- count(tl(l1),odd);
val it = 5 : int

- count(tl(tl(l1)),odd);
val it = 4 : int

- (* functions on lists can be defined using pattern-matching *)
- fun count(nil,p) = 0
=   | count(x::xs,p) = count(xs,p) + (if p(x) then 1 else 0);
val count = fn : 'a list * ('a -> bool) -> int

- count(l1,odd);
val it = 5 : int


- (* select returns the list of elements that satisfy a predicate p *)
- fun select(nil,p) = nil
=   | select(x::xs,p) = if p(x) then x::select(xs,p) else select(xs,p);
val select = fn : 'a list * ('a -> bool) -> 'a list

- select(l1,odd);
val it = [1,3,5,7,9] : int list

- fun even(n) = ~odd(n);
std_in:6.15 Error: overloaded variable "~" not defined at type:
   int -> bool

- fun even(n) = not(odd(n));
val even = fn : int -> bool

- select(l1,even);
val it = [0,2,4,6,8] : int list

- (* it's possible to construct and pass an "anonymous function" *)
- select(l1, fn(n) => n mod 2 = 1);  (* same as select(l1,odd) *)
[Major collection...
[Increasing heap to 2076k]
 26% used (428368/1645032), 560 msec]
[Increasing heap to 2160k]
val it = [1,3,5,7,9] : int list


- (* map f [x1,x2,...,xn] returns [f(x1),f(x2),...,f(xn)] *)
- fun square(n) = n*n;
std_in:10.19 Error: overloaded variable "*" cannot be resolved

- (* we must give ML some type information in this case *)
- fun square(n:int) = n*n;
val square = fn : int -> int

- val l4 = map square l1;
val l4 = [0,1,4,9,16,25,36,49,64,81] : int list

- select(l4,odd);
val it = [1,9,25,49,81] : int list

- val ms = map square;
val ms = fn : int list -> int list

- ms l1;
val it = [0,1,4,9,16,25,36,49,64,81] : int list

- ms (ms l1);
val it = [0,1,16,81,256,625,1296,2401,4096,6561] : int list


(* reduce combines the elements of a list using a 2-argument function F *)
- exception EmptyList;
exception EmptyList
- fun reduce(F,nil) = raise EmptyList
=   | reduce(F,[a]) = a
=   | reduce(F,x::xs) = F(x,reduce(F,xs));
val reduce = fn : ('a * 'a -> 'a) * 'a list -> 'a

- reduce(op +, l1);
val it = 45 : int

- reduce(op *, l1);
val it = 0 : int

- reduce(op *, tl l1);
val it = 362880 : int

- fun fact(n) = reduce(op *, tl(iota(n+1)));
val fact = fn : int -> int

- fact(9);
val it = 362880 : int

- fact(5);
val it = 120 : int

val sl = ["Now"," is"," the"," time..."] : string list

- reduce(op ^, sl);
val it = "Now is the time..." : string

- (* It's possible to define new types; here's a very simple case *)
- datatype color = Red | Green | Blue;
datatype  color
con Blue : color
con Green : color
con Red : color

- fun isRed(c) = (c=Red);
val isRed = fn : color -> bool

- isRed(Red);
val it = true : bool

- isRed(Blue);
val it = false : bool

- isRed(5);
std_in:8.1-8.8 Error: operator and operand don't agree (tycon mismatch)
  operator domain: color
  operand:         int
  in expression:
    isRed (5)

(* Recursive type: labelled binary tree *)
- datatype 'label btree =
=   Empty 
= | Node of 'label * 'label btree * 'label btree;
datatype 'a  btree
con Empty : 'a btree
con Node : 'a * 'a btree * 'a btree -> 'a btree

- Node("abc",Empty,Empty);
val it = Node ("abc",Empty,Empty) : string btree

- val t1 = Node("xyz",it,it);
val t1 = Node ("xyz",Node ("abc",Empty,Empty),Node ("abc",Empty,Empty)) : string btree

- val t2 = Node("foo",Empty,t1);
val t2 = Node ("foo",Empty,Node ("xyz",Node #,Node #)) : string btree

- fun lookup(x, Empty) = false
=   | lookup(x, Node(y,left,right)) =
=        if x=y then true
=        else if x < y then lookup(x,left)
=        else lookup(x,right);
std_in:4.18 Error: overloaded variable "<" cannot be resolved
- fun lookup(x, Empty) = false
   | lookup(x, Node(y,left,right)) =
         if x=y then true
        else if (x:string) < y then lookup(x,left)
        else (* x > y *) lookup(x,right);
= = = = val lookup = fn : string * string btree -> bool

- lookup("xyz",t2);
val it = true : bool
- lookup("foo",t2);
val it = true : bool

- lookup("bar",t2);
val it = false : bool
- fun sum(Empty) = 0
=   | sum(Node(a,left,right)) = a + sum(left) + sum(right);
val sum = fn : int btree -> int

- fun cat(Empty) = ""
=   | cat(Node(a,left,right)) = a ^ cat(left) ^ cat(right);

val cat = fn : string btree -> string
- t2;
val it = Node ("foo",Empty,Node ("xyz",Node #,Node #)) : string btree

- cat(t2);
val it = "fooxyzabcabc" : string

- fun insert(x,Empty) = Node(x,Empty,Empty)
=   | insert(x,T as Node(y,left,right)) =
=        if x=y then T
=        else if (x:string)<y then Node(y,insert(x,left),right)
=        else Node(y,left,insert(x,right));
val insert = fn : string * string btree -> string btree

- val t3 = insert("now",insert("is",insert("the",insert("time",Empty))));
val t3 = Node ("time",Node ("the",Node #,Empty),Empty) : string btree

- insert("for",t3);
val it = Node ("time",Node ("the",Node #,Empty),Empty) : string btree

- cat(it);
val it = "timetheisfornow" : string

- (* Here's an abstract data type definition---"abstract" because
-    it hides the representation.  We read it from a file "bst.ml" *)
- use "bst.ml";
[opening bst.ml]
structure StringBST : 
  sig
    datatype 'a btree
      con Empty : 'a btree
      con Node : 'a * 'a btree * 'a btree -> 'a btree
    val create : 'a btree
    val insert : string * string btree -> string btree
    val lookup : string * string btree -> bool
  end
[closing bst.ml]
val it = () : unit

- ^D

The File bst.ml

structure StringBST = struct
  datatype 'label btree =
      Empty 
    | Node of 'label * 'label btree * 'label btree;
  val create = Empty;
  fun insert(x,Empty) = Node(x,Empty,Empty) 
    | insert(x,T as Node(y,left,right)) =
         if x=y then T
         else if (x:string) < y then Node(y,insert(x,left),right)
         else Node(y,left,insert(x,right));
  fun lookup(x, Empty) = false
    | lookup(x, Node(y,left,right)) =
         if x=y then true
         else if (x:string) < y then lookup(x,left)
         else lookup(x,right);
end;