About SICP The following Haskell 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 Haskell

module SICP03 where

import Control.Exception (catch)
import Data.IORef (newIORef, readIORef, writeIORef)
import System.IO.Unsafe (unsafePerformIO)

main = do
   section_3_1_1

-- Functions defined in previous chapters
gcd' a 0 = a
gcd' a b = gcd' b (a `mod` b)

-- 3.1.1 - Assignment and Local State - Local State Variables
data Account a b = Account {accountWithdraw :: a -> b,
                            accountDeposit :: a -> b,
                            accountBalance :: b}

section_3_1_1 = do

   withdraw 25
   withdraw 25
   Control.Exception.catch (withdraw 60 >>= print) print
   withdraw 15
   readIORef balance >>= print

   newWithdraw' <- newWithdraw
   newWithdraw' 25
   newWithdraw' 25
   Control.Exception.catch (newWithdraw' 60 >>= print) print
   newWithdraw' 15 >>= print

   b1 <- newIORef 100
   let w1 = makeWithdraw b1
   b2 <- newIORef 100
   let w2 = makeWithdraw b2
   w1 50
   w2 70
   Control.Exception.catch (w2 40 >>= print) print
   w1 40
   readIORef b1 >>= print
   readIORef b2 >>= print

   acc <- makeAccount 100
   accountWithdraw acc 50
   Control.Exception.catch (accountWithdraw acc 60 >>= print) print
   accountDeposit acc 40
   accountWithdraw acc 60
   accountBalance acc >>= print

   acc2 <- makeAccount 100

   -- Exercise 3.1
   a <- makeAccumulator 5
   a 10 >>= print
   a 10 >>= print

   -- Exercise 3,2
   (sqrtMonitored, howManyCalls, resetCount) <- makeMonitored sqrt
   sqrtMonitored 100 >>= print
   sqrtMonitored 25 >>= print
   howManyCalls >>= print

   -- Exercise 3,3
   acc <- makePasswordAccount "secret-password" 100
   passwordAccountWithdraw acc "secret-password" 40
   Control.Exception.catch (passwordAccountWithdraw acc "some-other-password" 50 >>= print) print
   passwordAccountBalance acc "secret-password" >>= print

   -- Exercise 3,4
   -- acc <- makePoliceAccount "secret-password" 100
   -- passwordAccountWithdraw acc "secret-password" 40
   -- Control.Exception.catch (passwordAccountWithdraw acc "some-other-password" 50 >>= print) print
   -- passwordAccountBalance acc "secret-password" >>= print

-- Note: this is a hack, but Haskell discourages globals at the module level
balance = unsafePerformIO (newIORef 100)

withdraw amount = do
   b <- readIORef balance
   if b >= amount
      then writeIORef balance (b - amount)
      else error ("Insufficient Funds: " ++ (show b))
   readIORef balance

newWithdraw = do
   balance <- newIORef 100
   let
      withdraw amount = do
         b <- readIORef balance
         if b >= amount
            then writeIORef balance (b - amount)
            else error ("Insufficient Funds: " ++ (show b))
         readIORef balance >>= return
   return withdraw

makeWithdraw balance amount = do
   b <- readIORef balance
   if b >= amount
      then writeIORef balance (b - amount)
      else error ("Insufficient Funds: " ++ (show b))
   readIORef balance >>= return

makeAccount initBalance = do
   balance <- newIORef initBalance
   let
      withdraw amount = do
         b <- getBalance
         if b >= amount
            then writeIORef balance (b - amount)
            else error ("Insufficient Funds: " ++ show b)
         getBalance
      deposit amount = do
         b <- getBalance
         writeIORef balance (b + amount)
         getBalance
      getBalance = readIORef balance
   return $ Account {accountWithdraw=withdraw, accountDeposit=deposit, accountBalance=getBalance}

-- Exercise 3.1
makeAccumulator initial = do
   accumulator <- newIORef initial
   let
      setAccumulator x = do
         a <- readIORef accumulator
         writeIORef accumulator (a + x)
         readIORef accumulator >>= return
   return setAccumulator

-- Exercise 3.2
makeMonitored proc = do
   callCount <- newIORef 0
   let
      monitored m = do
         n <- readIORef callCount
         writeIORef callCount (n + 1)
         return (proc m)
      howManyCalls = readIORef callCount
      resetCount = do
         writeIORef callCount 0

   return (monitored, howManyCalls, resetCount)

-- Exercise 3.3
data PasswordAccount a b = PasswordAccount {passwordAccountWithdraw :: String -> a -> b,
                                            passwordAccountDeposit :: String -> a -> b,
                                            passwordAccountBalance :: String -> b}

makePasswordAccount secretPassword initBalance = do
   balance <- newIORef initBalance
   let
      password = secretPassword
      withdraw amount = do
         b <- getBalance
         if b >= amount
            then writeIORef balance (b - amount)
            else error ("Insufficient Funds: " ++ show b)
         getBalance
      deposit amount = do
         b <- getBalance
         writeIORef balance (b + amount)
         getBalance
      getBalance = readIORef balance
      wrapPassword m password =
         if password == secretPassword
            then m
            else error "Invalid Password"
   return $ PasswordAccount {passwordAccountWithdraw=wrapPassword withdraw,
                             passwordAccountDeposit=wrapPassword deposit,
                             passwordAccountBalance=wrapPassword getBalance}

-- Exercise 3.4
-- Note: I am having problems getting this correct???  The result is an IO monad wrapping an IO monad.
-- makePoliceAccount secretPassword initBalance = do
--    balance <- newIORef initBalance
--    badPasswordCount <- newIORef 0
--    let
--       password = secretPassword
--       withdraw amount = do
--          b <- getBalance
--          if b >= amount
--             then writeIORef balance (b - amount)
--             else error ("Insufficient Funds: " ++ show b)
--          getBalance
--       deposit amount = do
--          b <- getBalance
--          writeIORef balance (b + amount)
--          getBalance
--       getBalance = readIORef balance
--       wrapPassword m password =
--          if password == secretPassword
--             then do
--                writeIORef badPasswordCount 0
--                m >>= return
--             else do
--                n <- readIORef badPasswordCount
--                writeIORef badPasswordCount (n+1)
--                if n > 7
--                   then error "Call the cops"
--                   else error "Invalid Password"
--                m >>= return
--    return $ PasswordAccount {passwordAccountWithdraw=wrapPassword withdraw,
--                              passwordAccountDeposit=wrapPassword deposit,
--                              passwordAccountBalance=wrapPassword getBalance}

-- 3.1.3 - Assignment and Local State - The Benefits of Introducing Assignment

randomInit = unsafePerformIO (newIORef 7)

randUpdate x =
   (a*x + b) `mod` m
   where
      a = 27
      b = 26
      m = 127

rand = do
   x <- readIORef randomInit
   writeIORef randomInit (randUpdate x)
   readIORef randomInit

cesaroTest = do
   x <- rand
   y <- rand
   return (gcd x y == 1)

monteCarlo trials experiment =
   let
      iter trialsRemaining trialsPassed =
         if trialsRemaining == 0 then
            (fromIntegral trialsPassed :: Double) / (fromIntegral trials :: Double)
         else
            if experiment
               then iter (trialsRemaining-1) (trialsPassed+1)
               else iter (trialsRemaining-1) trialsPassed
   in
      iter trials 0

-- estimatePi trials = do
--    x <- cesaroTest
--    y <- return (monteCarlo trials x)
--    (6 / x) >>= sqrt
--    return 1

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