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
|