chriswarbo-net: f042e0ad1855acf6b34c55b2e7ae3cab470c3761
1: ---
2: title: A Framework for Self-Improving Code
3: packages: [ 'ghcWithQuickCheck' ]
4: ---
5:
6: Since Haskell functions are opaque (we can't pattern-match them), we'll define a
7: simple Lambda Calculus to represent our functions instead (this was explained
8: in [a previous post](/blog/2014-02-07-lazy_lambda_calculus.html)):
9:
10: ```
11: {.haskell pipe="sh"}
12: git clone ...
13: cat ...
14: ```
15:
16:
17: ```{pipe="cat > append"}
18: #!/bin/sh
19: tee -a code.hs
20: echo "" >> code.hs
21: ```
22:
23: ```{pipe="sh"}
24: chmod +x append
25: (source "$stdenv/setup" && patchShebangs .) 1>&2
26: ```
27:
28: ```{pipe="./append > /dev/null"}
29: import Test.QuickCheck
30: import Control.Applicative hiding (Const)
31: ```
32:
33: ```{.haskell pipe="./append"}
34: -- Lambda Calculus terms
35: data Term a = Var Nat
36: | Lam (Term a)
37: | App (Term a) (Term a)
38: | Const a
39: deriving (Show)
40:
41: -- De Bruijn indices
42: data Nat = Z | S Nat deriving (Show)
43:
44: lookUp :: [a] -> Nat -> Maybe a
45: lookUp xs n = let toInt Z = 0
46: toInt (S m) = 1 + toInt m
47: n' = toInt n
48: in if n' < length xs
49: then Just (xs !! n')
50: else Nothing
51:
52: -- Compiled closures
53: data Val a = F (Partial (Val a) -> Partial (Val a))
54: | C a
55:
56: -- Environment mapping indices to terms
57: type Env a = [Partial (Val a)]
58:
59: -- Lambda calculus evaluator
60: eval' :: Term a -> Env a -> Partial (Val a)
61: eval' (Const c) env = Now (C c)
62: eval' (Var n) env = let Just x = lookUp env n in x
63: eval' (Lam f) env = Now (F (\a -> eval' f (a:env)))
64: eval' (App f x) env = do F f' <- eval' f env
65: Later (f' (eval' x env))
66:
67: eval x = eval' x []
68:
69: -- Turn the general recursion of Lambda Calculus into co-recursion, to avoid
70: -- killing Haskell
71: data Partial a = Now a | Later (Partial a)
72:
73: instance Functor Partial where
74: fmap f (Now x) = Now (f x)
75: fmap f (Later x) = Later (fmap f x)
76:
77: instance Applicative Partial where
78: pure = return
79: (Now f) <*> x = f <$> x
80: (Later f) <*> x = Later (f <*> x)
81:
82: instance Monad Partial where
83: return = Now
84: (Now x) >>= f = Later (f x)
85: (Later x) >>= f = Later (x >>= f)
86: ```
87:
88: ```{pipe="./append"}
89: instance Eq a => Eq (Val a) where
90: C x == C y = x == y
91: _ == _ = False
92:
93: instance Arbitrary a => Arbitrary (Term a) where
94: arbitrary = oneof [Var <$> arbitrary,
95: App <$> arbitrary <*> arbitrary,
96: Lam <$> arbitrary,
97: Const <$> arbitrary]
98:
99: instance Arbitrary Nat where
100: arbitrary = oneof [return Z, S <$> arbitrary]
101:
102: extractTo :: Int -> Partial a -> Maybe a
103: extractTo _ (Now x) = Just x
104: extractTo n _ | n < 1 = Nothing
105: extractTo n (Later x) = extractTo (n-1) x
106:
107: evalFor n t = extractTo n (eval t)
108:
109: normalIn :: Eq a => Int -> Term a -> Bool
110: normalIn n t = evalFor n t /= Nothing
111:
112: testId :: Eq a => Int -> Term a -> Bool
113: testId n t = not (normalIn n t) ||
114: evalFor (n * n) (App (Lam (Var Z)) t) == evalFor n t
115: ```
116:
117: ```{pipe="ghci -v0"}
118: :load code.hs
119: quickCheck testId
120: ```
Generated by git2html.