About SICP The following F# code is derived from the examples provided in the book:
      "Structure and Interpretation of Computer Programs, Second Edition" by Harold Abelson and Gerald Jay Sussman with Julie Sussman.
      http://mitpress.mit.edu/sicp/

(* SICP Chapter #03 Examples in F# *)
#light

let rec superexp = fun (b:float) (l:int) -> if l = 0 then b else b**(superexp b (l-1)) ;;

superexp 1.1 16 ;;
superexp 1.3 36 ;;
superexp 1.444 622 ;;
superexp 1.4446678 46598 ;;


(* Functions defined in previous chapters *)
let rec gcd a = function
   | 0 -> a
   | b -> gcd b (a % b)
let square_real x = x * x
let average x y = (x + y) / 2.0
let rec has_no_divisors n c =
   c = 1 || (n % c <> 0 && has_no_divisors n (c-1))
let isPrime n = has_no_divisors n (n-1)
let rec enumerate_interval low high =
   if low > high
      then []
      else low :: enumerate_interval (low+1) high
let compose f g x = f(g x)
let isOdd n = (n % 2 = 1)
let isEven = compose not isOdd

(* 3.1.1 - Assignment and Local State - Local State Variables *)
let balance = ref 100

exception InsufficientFunds of int

let withdraw amount =
   if !balance >= amount
      then begin balance := (!balance - amount); !balance end
      else raise (InsufficientFunds (!balance))

let _ = withdraw 25
let _ = withdraw 25
let _ = try withdraw 60 with InsufficientFunds b -> b
let _ = withdraw 15

let new_withdraw =
   let balance = ref 100
   in fun amount ->
      if !balance >= amount
         then begin balance := (!balance - amount); !balance end
         else raise (InsufficientFunds (!balance))

let make_withdraw balance =
   let balance = ref balance
   in fun amount ->
      if !balance >= amount
         then begin balance := (!balance - amount); !balance end
         else raise (InsufficientFunds (!balance))

let w1 = make_withdraw 100
let w2 = make_withdraw 100

let _ = w1 50
let _ = w2 70
let _ = try w2 40 with InsufficientFunds b -> b
let _ = w1 40

type ('a, 'b, 'c) account_rec = { withdraw:'a; deposit:'b; balance:'c }

let make_account balance =
   let balance = ref balance in
   let withdraw amount =
      if !balance >= amount
         then begin balance := (!balance - amount); !balance end
         else raise (InsufficientFunds (!balance))
   and deposit amount =
      balance := (!balance + amount); !balance
   and getbalance = !balance
   in { withdraw=withdraw; deposit=deposit; balance=getbalance }

let acc = make_account 100
let _ = acc.withdraw 50
let _ = try acc.withdraw 60  with InsufficientFunds b -> b
let _ = acc.deposit 40
let _ = acc.withdraw 60

let acc2 = make_account 100

(* Exercise 3.1 *)
(* exercise left to reader to define appropriate functions
   let a = make_accumulator 5
   let _ = a.f 10
   let _ = a.f 10 *)

(* Exercise 3.2 *)
(* exercise left to reader to define appropriate functions
   let s = make_monitored sqrt
   let _ = s.f 100
   let _ = s.how_many_calls() *)

(* Exercise 3.3 *)
(* exercise left to reader to define appropriate functions
   let acc = make_account 100 "secret-password"
   let _ = acc.withdraw 40 "secret-password"
   let _ = acc.withdraw 50 "some-other-password" *)

(* 3.1.2 - Assignment and Local State - The Benefits of Introducing Assignment *)
let random_init = ref 7

let rand_update x =
   let a = 27
   and b = 26
   and m = 127
   in (a * x + b) % m

let rand =
   let x = random_init
   in fun () -> x := (rand_update !x); !x

let cesaro_test () = gcd (rand()) (rand()) = 1

let monte_carlo trials experiment =
   let rec iter trials_remaining trials_passed =
      match trials_remaining with
       | 0 -> float_of_int trials_passed / float_of_int trials
       | _ ->
         if experiment()
            then iter (trials_remaining - 1) (trials_passed + 1)
            else iter (trials_remaining - 1) trials_passed
   in iter trials 0

let estimate_pi trials = sqrt (6. / (monte_carlo trials cesaro_test))

(* second version (no assignment) *)
let random_gcd_test trials initial_x =
   let rec iter x trials_passed =
      let x1 = rand_update x in
      let x2 = rand_update x1
      in function
         | 0 -> (float trials_passed) / (float trials)
         | trials_remaining ->
            iter  x2
               (if gcd x1 x2 = 1 then (trials_passed + 1) else trials_passed)
               (trials_remaining - 1)
   in iter initial_x 0 trials

(* alternate translation *)
let random_gcd_test' trials initial_x =
   let rec iter trials_remaining trials_passed x =
      let x1 = rand_update x in
      let x2 = rand_update x1
      in
         if trials_remaining = 0
            then float_of_int trials_passed / float_of_int trials
            else
               if gcd x1 x2 = 1
                  then iter (trials_remaining - 1) (trials_passed + 1) x2
                  else iter (trials_remaining - 1) trials_passed x2
   in iter trials 0 initial_x

let estimate_pi' trials = sqrt (6. / (random_gcd_test' trials !random_init))

(* Exercise 3.6 *)
(* exercise left to reader to define appropriate functions
   fun random_in_range low high =
      let range = high - low
      in low + random range
      end *)

(* 3.1.3 - Assignment and Local State - The Cost of Introducing Assignment *)
let make_simplified_withdraw balance =
   let balance = ref balance in
   fun amount ->
      balance := !balance - amount;
      !balance

let w = make_simplified_withdraw 25
let _ = w 20
let _ = w 10

let make_decrimenter balance =
   fun amount -> balance - amount

let d = make_decrimenter 25
let _ = d 20
let _ = d 10

let _ = (make_decrimenter 25) 20
let _ = (fun amount -> 25 - amount) 20
let _ = 25 - 20

let _ = (make_simplified_withdraw 25) 20
(* we add an additional step here to handle the introduction of the balance ref *)
let _ = (let balance = ref 25 in fun amount -> balance := !balance - amount) 20
let _ = (fun amount -> balance := 25 - amount) 20
let _ = (balance := 25 - 20)

(* Sameness and change *)
let d2 = make_decrimenter 25
let d2' = make_decrimenter 25

let w1' = make_simplified_withdraw 25
let w2' = make_simplified_withdraw 25
let _ = w1' 20
let _ = w1' 20
let _ = w2' 20

let peter_acc = make_account 100
let paul_acc = make_account 100

let peter_acc' = make_account 100
let paul_acc' = peter_acc

(* Pitfalls of imperative programming *)
let factorial n =
   let rec iter product counter =
      if counter > n
         then product
         else iter (counter * product) (counter + 1)
   in iter 1 1

let factorial' n =
   let product = ref 1
   and counter = ref 1 in
   let rec iter () =
      if !counter > n
         then !product
         else
            begin
               product := !product * !counter;
               counter := !counter + 1;
               iter ()
            end
   in iter ()

(* Exercise 3.7 * )
( * exercise left to reader to define appropriate functions
   let paul_acc = make_joint peter_acc "open_sesame" "rosebud" *)

(* 3.2.1 - The Environment Model of Evaluation - The Rules for Evaluation *)
let square x = x * x

let square' = fun x -> x * x

(* 3.2.2 - The Environment Model of Evaluation - Applying Simple Procedures *)
let square'' x = x * x

let sum_of_squares x y =
   square x + square y

let f a =
   sum_of_squares (a + 1) (a * 2)

(* exercise 3.9 *)
let rec factorial_2 = function
   | 0 -> 1
   | n -> n * factorial_2 (n-1)

let rec fact_iter product counter max_count =
   if counter > max_count
      then product
      else fact_iter (counter * product) (counter + 1) max_count
let factorial_3 n = fact_iter 1 1 n

module sicp_localstate_translation =
(* 3.2.3 - The Environment Model of Evaluation - Frames as Repository of Local State *)
   let make_withdraw balance =
      let balance = ref balance
      in fun amount ->
         if !balance >= amount
            then (balance := !balance - amount; !balance)
            else raise (InsufficientFunds (!balance))

   let w1 = make_withdraw 100
   let _ = w1 50
   let w2 = make_withdraw 100

   (* Exercise 3.10 *)
   let make_withdraw' initial_amount =
      let balance = ref initial_amount
      in fun amount ->
         if !balance >= amount
            then (balance := !balance - amount; !balance)
            else raise (InsufficientFunds (!balance))

   let w1' = make_withdraw' 100
   let _ = w1' 50
   let w2' = make_withdraw' 100

module sicp_internaldef_translation =
(* 3.2.4 - The Environment Model of Evaluation - Internal Definitions *)
   (* redefine square to work on floats *)
   let square_real' x = x * x

   (* same as in section 1.1.8 *)
   let sqrt' x =
      let good_enough guess =
         abs_float(square_real guess - x) < 0.001
      and improve guess =
         average guess (x / guess) in
      let rec sqrt_iter guess =
         if good_enough guess
            then guess
            else sqrt_iter (improve guess)
      in sqrt_iter 1.0

   let make_account balance =
      let balance = ref balance in
      let withdraw amount =
         if !balance >= amount
            then (balance := !balance - amount; !balance)
            else failwith "Insufficient funds"
      and deposit amount = balance := !balance + amount; !balance
      and getbalance () = !balance
      in { withdraw=withdraw; deposit=deposit; balance=getbalance }

   let acc = make_account 50
   let _ = acc.deposit 40
   let _ = acc.withdraw 60
   let acc2 = make_account 100

(* 3.3.1 - Modeling with Mutable Data - Mutable List Structure *)

(* Note: ML lists can handle types of 'a ref, but this
         can't be used to set the tail of the list.  To
         do this trick, we need to define a list that
         has a ref for the head and tail.  Means extra work for ML.
         A better solution for F# is to use purely functional
         data structures. *)
exception NotFound

(*
type MyWidget =
  { GetLength : unit -> int;
    Get : unit -> string;
    Update : string -> unit }


 let MakeWidget(s1:string) =
  let state = ref (s1, s1.Length) in
  let get() = fst(!state) in
  let getLength() = snd(!state) in
  let update(s:string) = state := (s,s.Length)  in
  { GetLength = getLength;
    Get = get;
    Update = update }
*)
type 'a mlist = MNil | MCons of 'a ref * 'a mlist ref
type 'a MLIST = { cons      : 'a -> 'a mlist -> 'a mlist;
                  car       : 'a mlist -> 'a;
                  cdr       : 'a mlist -> 'a mlist;
                  set_car   : 'a mlist -> 'a -> unit;
                  set_cdr   : 'a mlist -> 'a mlist -> unit;
                  make_list : 'a list -> 'a mlist;
                  append    : 'a mlist -> 'a mlist -> 'a mlist }

let MList =
   let cons x y = MCons(ref x, ref y)

   let car = function
      | MCons(x, xs) -> !x
      | MNil -> raise NotFound

   let cdr = function
      | MCons(x, xs) -> !xs
      | MNil -> raise NotFound

   let set_car mlist y =
      match mlist with
       | MCons(x, xs) -> (x := y)
       | MNil -> raise NotFound

   let set_cdr mlist ys =
      match mlist with
       | MCons(x, xs) -> (xs := ys)
       | MNil -> raise NotFound

   let make_list xs = List.fold_right (fun v b -> cons v b) xs MNil

   let rec append mlist ys =
      match mlist with
       | MNil -> ys
       | MCons(x, xs) -> cons (!x) (append (!xs) ys)
   in
      { cons      = cons;
        car       = car;
        cdr       = cdr;
        set_car   = set_car;
        set_cdr   = set_cdr;
        make_list = make_list;
        append    = append }


(* Sharing and identity *)
let x = MList.make_list ["a"; "b"]
let z1 = MList.make_list [x; x]
let z2 = MList.make_list [MList.make_list ["A"; "B"]; MList.make_list ["A"; "B"]]

let set_to_wow x =
   let _ = MList.set_car (MList.car x) "Wow"
   in x

let _ = z1
let _ = set_to_wow z1
let _ = z2
let _ = set_to_wow z2

(* Mutation as assignment *)
type dispatch = Car | Cdr
type ('a,'b) pair' = Left of 'a | Right of 'b
type ('a,'b) PAIR = { cons : 'a -> 'b -> dispatch -> ('a,'b) pair';
                      car  : (dispatch -> ('a,'b) pair') -> 'a;
                      cdr  : (dispatch -> ('a,'b) pair') -> 'b }

module Pair' =
   let cons x y =
      let pdispatch = function
         | Car -> Left x
         | Cdr -> Right y
      in pdispatch

   let car z =
      match z Car with
       | Left c -> c
       | _ -> raise NotFound
   let cdr z =
      match z Cdr with
       | Right c -> c
       | _ -> raise NotFound
   in
      { cons = cons;
        car  = car;
        cdr  = car }

type mdispatch = Car | Cdr | SetCar | SetCdr
type ('a,'b) mpair = Left of 'a
                   | Right of 'b
                   | LSet of ('a -> unit)
                   | RSet of ('b -> unit)

type ('a,'b) MPAIR = { cons    : 'a -> 'b -> mdispatch -> ('a, 'b) mpair;
                       car     : (mdispatch -> ('a, 'b) mpair) -> 'a;
                       cdr     : (mdispatch -> ('a, 'b) mpair) -> 'b;
                       set_car : (mdispatch -> ('a, 'b) mpair) -> 'a -> unit;
                       set_cdr : (mdispatch -> ('a, 'b) mpair) -> 'b -> unit }

let MPair =
   let cons x y =
      let a = ref x
      and b = ref y in
      let setx v = (a := v)
      and sety v = (b := v) in
      let pdispatch = function
         | Car -> Left (!a)
         | Cdr -> Right (!b)
         | SetCar -> LSet setx
         | SetCdr -> RSet sety
      in pdispatch

   let car z =
      match z Car with
       | Left c -> c
       | _ -> raise NotFound
   let cdr z =
      match z Cdr with
       | Right c -> c
       | _ -> raise NotFound

   let set_car z x =
      match z SetCar with
       | LSet f -> f x
       | _ -> raise NotFound

   let set_cdr z y =
      match z SetCar with
       | RSet f -> f y
       | _ -> raise NotFound
   in
      { cons    = cons;
        car     = car;
        cdr     = cdr;
        set_car = set_car;
        set_cdr = set_cdr }

(* This example does not require dynamic dispatch and run-time errors.
   Indeed, there is nothing dynamic about this example. It was implemented
   that way in Scheme because this is all that Scheme can do.  In F#,
   you can use static checking to eliminate all run-time errors: *)
type ('a, 'b) cell =
   { car: unit -> 'a;
     cdr: unit -> 'b;
     set_car: 'a -> unit;
     set_cdr: 'b -> unit }

let cons x y =
   let x = ref x
   and y = ref y
   in
      { car = (fun () -> !x);
        cdr = (fun () -> !y);
        set_car = (fun x' -> x := x');
        set_cdr = (fun y' -> y := y') }

let xa = cons 1 2
let za = cons xa xa
(za.cdr()).set_car 17
xa.car()

(* Exercise 3.12 *)
let rec last_pair xs =
   match MList.cdr xs with
    | MNil -> xs
    | tail -> last_pair tail

let rec mappend xs ys =
   let _ = MList.set_cdr (last_pair xs) ys
   in xs

let xb = MList.make_list ['a'; 'b']
let yb = MList.make_list ['c'; 'd']
let zb = mappend xb yb
let _ = zb
let wb = mappend xb yb
let _ = wb
let _ = xb

(* Exercise 3.13 *)
let make_cycle xs =
   let _ = MList.set_cdr (last_pair xs) xs
   in xs
let zc = make_cycle (MList.make_list ['a'; 'b'; 'c'])

(* Exercise 3.14 *)
let mystery x =
   let rec loop x y =
      match x with
       | MNil -> y
       | _ ->
         let temp = MList.cdr x
         in ( MList.set_cdr x y; loop temp x )
   in loop x MNil
let v = MList.make_list ['a'; 'b'; 'c'; 'd']
let wd = mystery v

(* Exercise 3.16 *)
(* Write a function to count the number of cons cells in a binary tree. In
   F#, a cons cell is a Node: *)
type t = CEmpty | CNode of t * t

(* The following incorrect code from the book fails to take cyclic trees into account: *)
let rec count = function
   | CNode(a, b) -> 1 + count a + count b
   | CEmpty -> 0

(* Exercise 3.20 *)
let xe = MPair.cons 1 2
let ze = MPair.cons xe xe
MPair.set_car (MPair.cdr ze) 17
MPair.car xe

Chris Rathman / Chris.Rathman@tx.rr.com