Add Homework 6
This commit is contained in:
parent
148784ef4c
commit
c98765240c
454
DeBruijn.hs
Normal file
454
DeBruijn.hs
Normal file
|
@ -0,0 +1,454 @@
|
||||||
|
-- | This module illustrates beta-reduction on nameless lambda calculus
|
||||||
|
-- terms using de Bruijn indexes.
|
||||||
|
module DeBruijn where
|
||||||
|
|
||||||
|
import Prelude hiding (print,and,or,not,pred,succ,fst,snd,either,length,sum,product)
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- * Syntax
|
||||||
|
--
|
||||||
|
|
||||||
|
-- ** Abstract syntax
|
||||||
|
|
||||||
|
-- | The de Bruijn index of a variable.
|
||||||
|
type Var = Int
|
||||||
|
|
||||||
|
-- | Nameless lambda calculus terms. Note that we can check alpha-equivalence
|
||||||
|
-- with plain old Haskell (==). This is a more complicated and expensive
|
||||||
|
-- operation in named lambda calculuses.
|
||||||
|
data Exp
|
||||||
|
= Ref Var -- ^ variable reference
|
||||||
|
| App Exp Exp -- ^ application
|
||||||
|
| Abs Exp -- ^ lambda abstraction
|
||||||
|
deriving (Eq,Show)
|
||||||
|
|
||||||
|
|
||||||
|
-- ** Syntactic sugar
|
||||||
|
|
||||||
|
-- | Build an abstraction that takes two arguments.
|
||||||
|
abs2 :: Exp -> Exp
|
||||||
|
abs2 = Abs . Abs
|
||||||
|
|
||||||
|
-- | Build an abstraction that takes three arguments.
|
||||||
|
abs3 :: Exp -> Exp
|
||||||
|
abs3 = abs2 . Abs
|
||||||
|
|
||||||
|
-- | Build an abstraction that takes four arguments.
|
||||||
|
abs4 :: Exp -> Exp
|
||||||
|
abs4 = abs3 . Abs
|
||||||
|
|
||||||
|
-- | Build an application to apply a function to two arguments.
|
||||||
|
app2 :: Exp -> Exp -> Exp -> Exp
|
||||||
|
app2 f e1 e2 = App (App f e1) e2
|
||||||
|
|
||||||
|
-- | Build an application to apply a function to three arguments.
|
||||||
|
app3 :: Exp -> Exp -> Exp -> Exp -> Exp
|
||||||
|
app3 f e1 e2 e3 = App (app2 f e1 e2) e3
|
||||||
|
|
||||||
|
-- | Build an application to apply a function to four arguments.
|
||||||
|
app4 :: Exp -> Exp -> Exp -> Exp -> Exp -> Exp
|
||||||
|
app4 f e1 e2 e3 e4 = App (app3 f e1 e2 e3) e4
|
||||||
|
|
||||||
|
|
||||||
|
-- ** Pretty printing
|
||||||
|
|
||||||
|
-- | Pretty print a nameless lambda calculus expression.
|
||||||
|
pretty :: Exp -> String
|
||||||
|
pretty e = case e of
|
||||||
|
Ref x -> show x
|
||||||
|
Abs e -> "λ" ++ pretty e
|
||||||
|
App l r -> inAppL l ++ " " ++ group r
|
||||||
|
where
|
||||||
|
group (Ref x) = show x
|
||||||
|
group e = "(" ++ pretty e ++ ")"
|
||||||
|
inAppL (App l r) = inAppL l ++ " " ++ group r
|
||||||
|
inAppL e = group e
|
||||||
|
|
||||||
|
-- | Print a pretty-printed lambda calculus expression to standard output.
|
||||||
|
print :: Exp -> IO ()
|
||||||
|
print = putStrLn . pretty
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- * Semantics
|
||||||
|
--
|
||||||
|
|
||||||
|
-- ** Substitution
|
||||||
|
|
||||||
|
-- | Variable substitution. `sub x arg e` substitues arg for every x in e.
|
||||||
|
--
|
||||||
|
-- Both the abstraction and reference cases are interesting.
|
||||||
|
--
|
||||||
|
-- Each time we enter a new abstraction in e, we must:
|
||||||
|
-- 1. Increment the x that we're looking for.
|
||||||
|
-- 2. Increment all of the free variables in arg since now the references
|
||||||
|
-- will have to skip over one more lambda to get to the lambda they
|
||||||
|
-- refer to.
|
||||||
|
--
|
||||||
|
-- For each variable reference y in e, there are three possibilities:
|
||||||
|
-- 1. It's the variable x we're looking for, in which case we replace it.
|
||||||
|
-- 2. It's a variable bound in e (y < x), in which case we do nothing.
|
||||||
|
-- 3. It's a variable that is free in e (y > x), in which case we
|
||||||
|
-- decrement it since now the reference has to skip over one less
|
||||||
|
-- lambda (i.e. the lambda that we beta-reduced away) to get to the
|
||||||
|
-- lambda it refers to.
|
||||||
|
--
|
||||||
|
-- >>> sub 0 (Ref 1) (Abs (Ref 1))
|
||||||
|
-- Abs (Ref 2)
|
||||||
|
--
|
||||||
|
sub :: Var -> Exp -> Exp -> Exp
|
||||||
|
sub x arg (App l r) = App (sub x arg l) (sub x arg r)
|
||||||
|
sub x arg (Abs e) = Abs (sub (x+1) (inc 0 arg) e)
|
||||||
|
sub x arg (Ref y)
|
||||||
|
| y == x = arg -- found an x, replace it!
|
||||||
|
| y < x = Ref y -- not x and bound in e, leave it alone
|
||||||
|
| y > x = Ref (y-1) -- not x and free in e, decrement it
|
||||||
|
|
||||||
|
-- | Increment the free variables in an expression.
|
||||||
|
--
|
||||||
|
-- The argument d (for "depth") indicates the number of abstractions we
|
||||||
|
-- have recursed into so far. A variable that is smaller than the depth
|
||||||
|
-- is not free, and so should not be incremented.
|
||||||
|
--
|
||||||
|
-- >>> inc 0 (Ref 0)
|
||||||
|
-- Ref 1
|
||||||
|
--
|
||||||
|
-- >>> inc 0 (App (Ref 1) (Abs (Ref 0)))
|
||||||
|
-- App (Ref 2) (Abs (Ref 0))
|
||||||
|
--
|
||||||
|
inc :: Int -> Exp -> Exp
|
||||||
|
inc d (App l r) = App (inc d l) (inc d r)
|
||||||
|
inc d (Abs e) = Abs (inc (d+1) e)
|
||||||
|
inc d (Ref x) = Ref (if x < d then x else x+1)
|
||||||
|
|
||||||
|
|
||||||
|
-- ** Small-step semantics
|
||||||
|
|
||||||
|
-- | Do one step of normal order reduction and return the result.
|
||||||
|
-- If no redex is found, return Nothing.
|
||||||
|
--
|
||||||
|
-- The first case matches a redex and does a substitution. The rest of
|
||||||
|
-- the cases implement a search for next redex.
|
||||||
|
--
|
||||||
|
-- >>> step (App (Abs (Ref 0)) (Ref 1))
|
||||||
|
-- Just (Ref 1)
|
||||||
|
--
|
||||||
|
-- >>> step (App (abs2 (App (Ref 0) (Ref 1))) (Ref 2))
|
||||||
|
-- Just (Abs (App (Ref 0) (Ref 3)))
|
||||||
|
--
|
||||||
|
-- >>> step (App (abs2 (App (Ref 2) (Ref 1))) (Ref 0))
|
||||||
|
-- Just (Abs (App (Ref 1) (Ref 1)))
|
||||||
|
--
|
||||||
|
step :: Exp -> Maybe Exp
|
||||||
|
step (App (Abs e) r) = Just (sub 0 r e) -- found a redex, do beta reduction!
|
||||||
|
step (App l r) = case step l of
|
||||||
|
Just l' -> Just (App l' r)
|
||||||
|
Nothing -> fmap (App l) (step r)
|
||||||
|
step (Abs e) = fmap Abs (step e)
|
||||||
|
step (Ref _) = Nothing
|
||||||
|
|
||||||
|
|
||||||
|
-- | Evaluate an expression to normal form using normal order recution.
|
||||||
|
-- Return a list of expressions produced by each step of reduction.
|
||||||
|
-- Note that this list may be infinite if the reduction never reaches
|
||||||
|
-- a normal form!
|
||||||
|
steps :: Exp -> [Exp]
|
||||||
|
steps e = e : case step e of
|
||||||
|
Nothing -> []
|
||||||
|
Just e' -> steps e'
|
||||||
|
|
||||||
|
-- | Evaluate an expression to normal form using normal order reduction.
|
||||||
|
-- Note that this function will not terminate if the reduction never
|
||||||
|
-- reaches a normal form!
|
||||||
|
eval :: Exp -> Exp
|
||||||
|
eval = last . steps
|
||||||
|
|
||||||
|
-- | Print a reduction sequence for an expression.
|
||||||
|
printReduce :: Exp -> IO ()
|
||||||
|
printReduce = mapM_ print . steps
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- * Church encodings
|
||||||
|
--
|
||||||
|
|
||||||
|
-- ** Church booleans
|
||||||
|
|
||||||
|
-- | λxy.x
|
||||||
|
true :: Exp
|
||||||
|
true = abs2 (Ref 1)
|
||||||
|
|
||||||
|
-- | λxy.y
|
||||||
|
false :: Exp
|
||||||
|
false = abs2 (Ref 0)
|
||||||
|
|
||||||
|
-- | λbte.bte
|
||||||
|
if_ :: Exp
|
||||||
|
if_ = abs3 (app2 (Ref 2) (Ref 1) (Ref 0))
|
||||||
|
|
||||||
|
-- | λb. if b false true
|
||||||
|
not :: Exp
|
||||||
|
not = Abs (app3 if_ (Ref 0) false true)
|
||||||
|
|
||||||
|
-- | λpq. if p q p
|
||||||
|
and :: Exp
|
||||||
|
and = abs2 (app3 if_ (Ref 1) (Ref 0) (Ref 1))
|
||||||
|
|
||||||
|
-- | λpq. if p p q
|
||||||
|
or :: Exp
|
||||||
|
or = abs2 (app3 if_ (Ref 1) (Ref 1) (Ref 0))
|
||||||
|
|
||||||
|
-- | Church Boolean tests:
|
||||||
|
--
|
||||||
|
-- >>> true == eval (app2 and (app2 or false true) (App not false))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- >>> false == eval (app2 or (App not true) (app2 and true false))
|
||||||
|
-- True
|
||||||
|
|
||||||
|
|
||||||
|
-- ** Church numerals
|
||||||
|
|
||||||
|
-- | λfx.x
|
||||||
|
zero :: Exp
|
||||||
|
zero = abs2 (Ref 0)
|
||||||
|
|
||||||
|
-- | λfx.fx
|
||||||
|
one :: Exp
|
||||||
|
one = abs2 (App (Ref 1) (Ref 0))
|
||||||
|
|
||||||
|
-- | λfx.f(fx)
|
||||||
|
two :: Exp
|
||||||
|
two = abs2 (App (Ref 1) (App (Ref 1) (Ref 0)))
|
||||||
|
|
||||||
|
-- | λfx.f(f(fx))
|
||||||
|
three :: Exp
|
||||||
|
three = abs2 (App (Ref 1) (App (Ref 1) (App (Ref 1) (Ref 0))))
|
||||||
|
|
||||||
|
-- | Build a Church numeral for an arbitrary natural number.
|
||||||
|
--
|
||||||
|
-- >>> map num [0,1,2,3] == [zero,one,two,three]
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
num :: Int -> Exp
|
||||||
|
num n = abs2 (help n (Ref 0))
|
||||||
|
where help 0 e = e
|
||||||
|
help n e = App (Ref 1) (help (n-1) e)
|
||||||
|
|
||||||
|
-- | λnfx.f(nfx)
|
||||||
|
succ :: Exp
|
||||||
|
succ = abs3 (App (Ref 1) (app2 (Ref 2) (Ref 1) (Ref 0)))
|
||||||
|
|
||||||
|
-- | λnmfx.nf(mfx)
|
||||||
|
add :: Exp
|
||||||
|
add = abs4 (app2 (Ref 3) (Ref 1) (app2 (Ref 2) (Ref 1) (Ref 0)))
|
||||||
|
|
||||||
|
-- | λnmf.n(mf)
|
||||||
|
mult :: Exp
|
||||||
|
mult = abs3 (App (Ref 2) (App (Ref 1) (Ref 0)))
|
||||||
|
|
||||||
|
-- | λn. n (λx.false) true
|
||||||
|
isZero :: Exp
|
||||||
|
isZero = Abs (app2 (Ref 0) (Abs false) true)
|
||||||
|
|
||||||
|
-- | λnfx.n (λgh.h(gf)) (λu.x) (λu.u)
|
||||||
|
--
|
||||||
|
-- See: https://en.wikipedia.org/wiki/Church_encoding#Derivation_of_predecessor_function
|
||||||
|
pred :: Exp
|
||||||
|
pred = abs3 (app3 (Ref 2)
|
||||||
|
(abs2 (App (Ref 0) (App (Ref 1) (Ref 3))))
|
||||||
|
(Abs (Ref 1))
|
||||||
|
(Abs (Ref 0)))
|
||||||
|
|
||||||
|
-- | Church numeral tests:
|
||||||
|
--
|
||||||
|
-- >>> three == eval (app2 add two one)
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- >>> num 15 == eval (app2 mult (app2 add two three) three)
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- >>> num 5 == eval (App pred (num 6))
|
||||||
|
-- True
|
||||||
|
|
||||||
|
|
||||||
|
-- ** Church tuples
|
||||||
|
|
||||||
|
-- | λxys.sxy
|
||||||
|
--
|
||||||
|
-- >>> two == eval (App fst (app2 pair two true))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- >>> true == eval (App snd (app2 pair two true))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
pair :: Exp
|
||||||
|
pair = abs3 (app2 (Ref 0) (Ref 2) (Ref 1))
|
||||||
|
|
||||||
|
-- | λt.t(λxy.x)
|
||||||
|
fst :: Exp
|
||||||
|
fst = Abs (App (Ref 0) true)
|
||||||
|
|
||||||
|
-- | λt.t(λxy.y)
|
||||||
|
snd :: Exp
|
||||||
|
snd = Abs (App (Ref 0) false)
|
||||||
|
|
||||||
|
-- | λxyzs.sxyz
|
||||||
|
--
|
||||||
|
-- >>> one == eval (App sel13 (app3 tuple3 one two three))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- >>> two == eval (App sel23 (app3 tuple3 one two three))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- >>> three == eval (App sel33 (app3 tuple3 one two three))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
tuple3 :: Exp
|
||||||
|
tuple3 = abs4 (app3 (Ref 0) (Ref 3) (Ref 2) (Ref 1))
|
||||||
|
|
||||||
|
-- | λt.t(λxyz.x)
|
||||||
|
sel13 :: Exp
|
||||||
|
sel13 = Abs (App (Ref 0) (abs3 (Ref 2)))
|
||||||
|
|
||||||
|
-- | λt.t(λxyz.y)
|
||||||
|
sel23 :: Exp
|
||||||
|
sel23 = Abs (App (Ref 0) (abs3 (Ref 1)))
|
||||||
|
|
||||||
|
-- | λt.t(λxyz.z)
|
||||||
|
sel33 :: Exp
|
||||||
|
sel33 = Abs (App (Ref 0) (abs3 (Ref 0)))
|
||||||
|
|
||||||
|
|
||||||
|
-- ** Church sums
|
||||||
|
|
||||||
|
-- | λfgu.ufg
|
||||||
|
--
|
||||||
|
-- >>> three == eval (app3 either succ not (App inL two))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- >>> false == eval (app3 either succ not (App inR true))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
either :: Exp
|
||||||
|
either = pair
|
||||||
|
|
||||||
|
-- | λxfg.fx
|
||||||
|
inL :: Exp
|
||||||
|
inL = abs3 (App (Ref 1) (Ref 2))
|
||||||
|
|
||||||
|
-- | λxfg.gx
|
||||||
|
inR :: Exp
|
||||||
|
inR = abs3 (App (Ref 0) (Ref 2))
|
||||||
|
|
||||||
|
-- | λfghu.ufgh
|
||||||
|
--
|
||||||
|
-- >>> three == eval (app4 case3 succ not fst (App in13 two))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- >>> false == eval (app4 case3 succ not fst (App in23 true))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- >>> one == eval (app4 case3 succ not fst (App in33 (app2 pair one two)))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
case3 :: Exp
|
||||||
|
case3 = tuple3
|
||||||
|
|
||||||
|
-- | λxfgh.fx
|
||||||
|
in13 :: Exp
|
||||||
|
in13 = abs4 (App (Ref 2) (Ref 3))
|
||||||
|
|
||||||
|
-- | λxfgh.gx
|
||||||
|
in23 :: Exp
|
||||||
|
in23 = abs4 (App (Ref 1) (Ref 3))
|
||||||
|
|
||||||
|
-- | λxfgh.hx
|
||||||
|
in33 :: Exp
|
||||||
|
in33 = abs4 (App (Ref 0) (Ref 3))
|
||||||
|
|
||||||
|
|
||||||
|
-- ** Fixpoint combinator
|
||||||
|
|
||||||
|
-- | λf. (λx.f(xx)) (λx.f(xx))
|
||||||
|
fix :: Exp
|
||||||
|
fix = Abs (App (Abs (App (Ref 1) (App (Ref 0) (Ref 0))))
|
||||||
|
(Abs (App (Ref 1) (App (Ref 0) (Ref 0)))))
|
||||||
|
|
||||||
|
-- | fix (λrn. if (isZero n) one (mult n (r (pred n))))
|
||||||
|
--
|
||||||
|
-- >>> num 6 == eval (App fac three)
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- >>> num 24 == eval (App fac (num 4))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
fac :: Exp
|
||||||
|
fac = App fix (abs2
|
||||||
|
(app3 if_
|
||||||
|
(App isZero (Ref 0)) -- condition
|
||||||
|
one -- base case
|
||||||
|
(app2 mult -- recursive case
|
||||||
|
(Ref 0)
|
||||||
|
(App (Ref 1) (App pred (Ref 0))))))
|
||||||
|
|
||||||
|
|
||||||
|
-- ** Church-encoded lists
|
||||||
|
|
||||||
|
-- | inL (λx.x)
|
||||||
|
nil :: Exp
|
||||||
|
nil = App inL (Abs (Ref 0))
|
||||||
|
|
||||||
|
-- | λht. inR (pair h t)
|
||||||
|
cons :: Exp
|
||||||
|
cons = abs2 (App inR (app2 pair (Ref 1) (Ref 0)))
|
||||||
|
|
||||||
|
-- | fix (λrfbl. either (λx.b) (λp. f (fst p) (r f b (snd p))) l)
|
||||||
|
fold :: Exp
|
||||||
|
fold = App fix (abs4
|
||||||
|
(app3 either
|
||||||
|
(Abs (Ref 2))
|
||||||
|
(Abs (app2 (Ref 3)
|
||||||
|
(App fst (Ref 0))
|
||||||
|
(app3 (Ref 4) (Ref 3) (Ref 2) (App snd (Ref 0)))))
|
||||||
|
(Ref 0)))
|
||||||
|
|
||||||
|
-- | Smart constructor to build a Church-encoded list of natural numbers.
|
||||||
|
list :: [Int] -> Exp
|
||||||
|
list = foldr (app2 cons . num) nil
|
||||||
|
|
||||||
|
-- | fold (λh. add one) zero
|
||||||
|
--
|
||||||
|
-- >>> three == eval (App length (list [2,3,4]))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
length :: Exp
|
||||||
|
length = app2 fold (Abs (App add one)) zero
|
||||||
|
|
||||||
|
-- | fold add zero
|
||||||
|
--
|
||||||
|
-- >>> num 9 == eval (App sum (list [2,3,4]))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- >>> num 55 == eval (App sum (list [1..10]))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
sum :: Exp
|
||||||
|
sum = app2 fold add zero
|
||||||
|
|
||||||
|
-- | fold mult one
|
||||||
|
--
|
||||||
|
-- >>> num 24 == eval (App product (list [2,3,4]))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
product :: Exp
|
||||||
|
product = app2 fold mult one
|
||||||
|
|
||||||
|
-- | λf. fold (\h. cons (f h)) nil
|
||||||
|
--
|
||||||
|
-- >>> eval (list [4,5,6]) == eval (app2 map' (App add two) (list [2,3,4]))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
map' :: Exp
|
||||||
|
map' = Abs (app2 fold (Abs (App cons (App (Ref 1) (Ref 0)))) nil)
|
224
HW6.hs
Normal file
224
HW6.hs
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
module HW6 where
|
||||||
|
|
||||||
|
import Prelude hiding (print,and,or,not,pred,succ,fst,snd,either,length,sum,product)
|
||||||
|
|
||||||
|
import DeBruijn
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- * Part 1: Nameless lambda calculus
|
||||||
|
--
|
||||||
|
|
||||||
|
-- | λx. (λx.x) x
|
||||||
|
--
|
||||||
|
-- >>> eval ex1
|
||||||
|
-- Abs (Ref 0)
|
||||||
|
--
|
||||||
|
ex1 :: Exp
|
||||||
|
ex1 = Abs (App (Abs (Ref 0)) (Ref 0))
|
||||||
|
|
||||||
|
-- | (λxy.xz) z z
|
||||||
|
--
|
||||||
|
-- >>> eval ex2
|
||||||
|
-- App (Ref 0) (Ref 0)
|
||||||
|
--
|
||||||
|
ex2 :: Exp
|
||||||
|
ex2 = App (App (Abs (Abs (App (Ref 1) (Ref 2)))) (Ref 0)) (Ref 0)
|
||||||
|
|
||||||
|
-- | λy. (λxy.yx) y z
|
||||||
|
--
|
||||||
|
-- >>> eval ex3
|
||||||
|
-- Abs (App (Ref 1) (Ref 0))
|
||||||
|
--
|
||||||
|
ex3 :: Exp
|
||||||
|
ex3 = Abs (App (App (Abs (Abs (App (Ref 0) (Ref 1)))) (Ref 0)) (Ref 1))
|
||||||
|
|
||||||
|
|
||||||
|
-- | Is the given nameless lambda calculus term a closed expression? That is,
|
||||||
|
-- does it contain no free variables?
|
||||||
|
--
|
||||||
|
-- >>> closed (Ref 0)
|
||||||
|
-- False
|
||||||
|
--
|
||||||
|
-- >>> closed (Abs (Ref 0))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- >>> closed (Abs (App (Ref 0) (Ref 1)))
|
||||||
|
-- False
|
||||||
|
--
|
||||||
|
-- >>> closed (Abs (App (Abs (App (Ref 0) (Ref 1))) (Ref 0)))
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
closed :: Exp -> Bool
|
||||||
|
closed = cl 0
|
||||||
|
where
|
||||||
|
cl n (Ref i) = i < n
|
||||||
|
cl n (App l r) = cl n l && cl n r
|
||||||
|
cl n (Abs e) = cl (n+1) e
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- * Part 2: Church pair update functions
|
||||||
|
--
|
||||||
|
|
||||||
|
-- | Write a lambda calculus function that replaces the first element in a
|
||||||
|
-- Church-encoded pair. The first argument to the function is the new
|
||||||
|
-- first element, the second argument is the original pair.
|
||||||
|
--
|
||||||
|
-- >>> :{
|
||||||
|
-- eval (app2 pair true (num 3)) ==
|
||||||
|
-- eval (app2 setFst true (app2 pair (num 2) (num 3)))
|
||||||
|
-- :}
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
setFst :: Exp
|
||||||
|
setFst = Abs $ Abs $ Abs $ App (Ref 1) (Abs $ Abs $ App (App (Ref 2) (Ref 4)) (Ref 0))
|
||||||
|
-- \x -> \p -> \f -> p (\a -> \b -> f x b)
|
||||||
|
|
||||||
|
-- | Write a lambda calculus function that replaces the second element in a
|
||||||
|
-- Church-encoded pair. The first argument to the function is the new
|
||||||
|
-- second element, the second argument is the original pair.
|
||||||
|
--
|
||||||
|
-- >>> :{
|
||||||
|
-- eval (app2 pair (num 2) true) ==
|
||||||
|
-- eval (app2 setSnd true (app2 pair (num 2) (num 3)))
|
||||||
|
-- :}
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
setSnd :: Exp
|
||||||
|
setSnd = Abs $ Abs $ Abs $ App (Ref 1) (Abs $ Abs $ App (App (Ref 2) (Ref 1)) (Ref 4))
|
||||||
|
-- \x -> \p -> \f -> p (\a -> \b -> f a x)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- * Part 3: Church encoding a Haskell program
|
||||||
|
--
|
||||||
|
|
||||||
|
-- | Pretend Haskell's Int is restricted to Nats.
|
||||||
|
type Nat = Int
|
||||||
|
|
||||||
|
-- | A simple data for representing shapes.
|
||||||
|
data Shape
|
||||||
|
= Circle Nat
|
||||||
|
| Rectangle Nat Nat
|
||||||
|
deriving (Eq,Show)
|
||||||
|
|
||||||
|
-- | A smart constructor for building squares.
|
||||||
|
square :: Nat -> Shape
|
||||||
|
square l = Rectangle l l
|
||||||
|
|
||||||
|
-- | Compute the area of a shape using a rough approximation of pi.
|
||||||
|
area :: Shape -> Nat
|
||||||
|
area (Circle r) = 3 * r * r
|
||||||
|
area (Rectangle l w) = l * w
|
||||||
|
|
||||||
|
-- | Compute the perimeter of a shape using a rough approximation of pi.
|
||||||
|
perimeter :: Shape -> Nat
|
||||||
|
perimeter (Circle r) = 2 * 3 * r
|
||||||
|
perimeter (Rectangle l w) = 2 * l + 2 * w
|
||||||
|
|
||||||
|
-- | Encode the circle constructor as a lambda calculus term. The term
|
||||||
|
-- should be a function that takes a Church-encoded natural number as input
|
||||||
|
-- and produces a Church-encoded shape as output.
|
||||||
|
circleExp :: Exp
|
||||||
|
circleExp = inL
|
||||||
|
|
||||||
|
-- | Encode the rectangle constructor as a lambda calculus term. The term
|
||||||
|
-- should be a function that takes two Church-encoded natural numbers as
|
||||||
|
-- input and produces a Church-encoded shape as output.
|
||||||
|
rectangleExp :: Exp
|
||||||
|
rectangleExp = Abs $ Abs $ App inR $ app2 pair (Ref 1) (Ref 0)
|
||||||
|
|
||||||
|
-- | Convert a shape into a lambda calculus term. This function helps to
|
||||||
|
-- illustrate how your encodings of the constructors should work.
|
||||||
|
encodeShape :: Shape -> Exp
|
||||||
|
encodeShape (Circle r) = App circleExp (num r)
|
||||||
|
encodeShape (Rectangle l w) = app2 rectangleExp (num l) (num w)
|
||||||
|
|
||||||
|
-- | Encode the square function as a lambda calculus term.
|
||||||
|
squareExp :: Exp
|
||||||
|
squareExp = Abs $ app2 rectangleExp (Ref 0) (Ref 0)
|
||||||
|
|
||||||
|
-- | Encode the area function as a lambda calculus term.
|
||||||
|
areaExp :: Exp
|
||||||
|
areaExp = Abs $ app2 (Ref 0)
|
||||||
|
(Abs $ app2 mult (num 3) (app2 mult (Ref 0) (Ref 0)))
|
||||||
|
(Abs $ App (Ref 0) mult)
|
||||||
|
|
||||||
|
-- | Encode the perimeter function as a lambda calculus term.
|
||||||
|
perimeterExp :: Exp
|
||||||
|
perimeterExp = Abs $ app2 (Ref 0)
|
||||||
|
(Abs $ app2 mult (num 6) (Ref 0))
|
||||||
|
(Abs $ App (Ref 0) $ Abs $ Abs $ app2 mult (num 2) (app2 add (Ref 0) (Ref 1)))
|
||||||
|
|
||||||
|
-- | Some tests of your lambda calculus encodings.
|
||||||
|
--
|
||||||
|
-- >>> :{
|
||||||
|
-- checkEval (area (Circle 3))
|
||||||
|
-- (App areaExp (App circleExp (num 3)))
|
||||||
|
-- :}
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- >>> :{
|
||||||
|
-- checkEval (area (Rectangle 3 5))
|
||||||
|
-- (App areaExp (app2 rectangleExp (num 3) (num 5)))
|
||||||
|
-- :}
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- >>> :{
|
||||||
|
-- checkEval (area (square 4))
|
||||||
|
-- (App areaExp (App squareExp (num 4)))
|
||||||
|
-- :}
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- >>> :{
|
||||||
|
-- checkEval (perimeter (Circle 3))
|
||||||
|
-- (App perimeterExp (App circleExp (num 3)))
|
||||||
|
-- :}
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- >>> :{
|
||||||
|
-- checkEval (perimeter (Rectangle 3 5))
|
||||||
|
-- (App perimeterExp (app2 rectangleExp (num 3) (num 5)))
|
||||||
|
-- :}
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
-- >>> :{
|
||||||
|
-- checkEval (perimeter (square 4))
|
||||||
|
-- (App perimeterExp (App squareExp (num 4)))
|
||||||
|
-- :}
|
||||||
|
-- True
|
||||||
|
--
|
||||||
|
checkEval :: Nat -> Exp -> Bool
|
||||||
|
checkEval n e = num n == eval e
|
||||||
|
|
||||||
|
-- Alright, extra practice, let's go.
|
||||||
|
-- Part 1: 4-tuples (Bonus: n-tuples)
|
||||||
|
absN :: Int -> Exp -> Exp
|
||||||
|
absN n = flip (foldr $ const Abs) $ replicate n ()
|
||||||
|
|
||||||
|
apps :: Exp -> [Exp] -> Exp
|
||||||
|
apps e = foldl App e
|
||||||
|
|
||||||
|
tupleN :: Int -> Exp
|
||||||
|
tupleN n = absN (n+1) $ apps (Ref 0) $ map Ref $ [n, (n-1) .. 1]
|
||||||
|
|
||||||
|
selN :: Int -> Int -> Exp
|
||||||
|
selN n m = Abs $ App (Ref 0) $ absN m $ Ref $ m - n
|
||||||
|
|
||||||
|
tuple4 :: Exp
|
||||||
|
tuple4 = tupleN 4
|
||||||
|
|
||||||
|
sel14 :: Exp
|
||||||
|
sel14 = selN 1 4
|
||||||
|
|
||||||
|
sel24 :: Exp
|
||||||
|
sel24 = selN 2 4
|
||||||
|
|
||||||
|
sel34 :: Exp
|
||||||
|
sel34 = selN 3 4
|
||||||
|
|
||||||
|
sel44 :: Exp
|
||||||
|
sel44 = selN 4 4
|
||||||
|
|
||||||
|
-- Wait, the extra credit practice is mostly mechanical?
|
||||||
|
-- Boo!
|
Loading…
Reference in New Issue
Block a user