I want to write a function, allTrees, to generate a list of all possible binary trees that store the number of leaves each tree has.
Here are my data types and my attempt at the allTrees function:
data BTree = L | B BTree BTree
deriving (Eq, Ord, Show)
data SpecTree = S Integer BTree
deriving (Eq, Ord, Show)
leafNode :: SpecTree
leafNode = S 1 L
branch :: SpecTree -> SpecTree -> SpecTree
branch (S size1 sub1) (S size2 sub2) = S (size1 + size2) (B sub1 sub2)
allTrees :: [SpecTree]
allTrees = leafNode : branch allTrees allTrees
Expected output:
take 9 allTrees = [S 1 L,S 2 (B L L),S 3 (B L (B L L)),S 3 (B (B L L) L),S 4 (B L (B L (B L L))),S 4 (B L (B (B L L) L)),S 4 (B (B L L) (B L L)),S 4 (B (B L (B L L)) L),S 4 (B (B (B L L) L) L)]
Actual output:
take 9 allTrees = [S 1 L,S 2 (B L L),S 3 (B L (B L L)),S 4 (B L (B L (B L L))),S 5 (B L (B L (B L (B L L)))),S 6 (B L (B L (B L (B L (B L L))))),S 7 (B L (B L (B L (B L (B L (B L L)))))),S 8 (B L (B L (B L (B L (B L (B L (B L L))))))),S 9 (B L (B L (B L (B L (B L (B L (B L (B L L))))))))]
My output is close but not quite it. I think foldM may be useful here, but not sure how I can use it.
The problem is as Carl describes in the comments. You're generating infinitely many different trees, but because of the order you generate them in, you don't get them all. It's like generating "all integers" by starting at 1 and doubling each time. Every integer is new, and you never run out, but you miss most integers. In your case, you generate just the degenerate right-child-only trees, because that's the direction you bias your exploration, and you never run out of room to explore that direction.
Instead, as Carl also suggested in the comments, if you want to ensure you hit every possible tree, generate them in an order that ensures you don't miss any: smallest first.
allTreesOfSize :: Integer -> [BTree]
allTreesOfSize 0 = [] -- Not used in the recursive case
allTreesOfSize 1 = [L]
allTreesOfSize n = do
lSize <- [1..n-1]
let rSize = n - lSize
B <$> allTreesOfSize lSize <*> allTreesOfSize rSize
allTrees :: [SpecTree]
allTrees = do
n <- [1..]
S n <$> allTreesOfSize n
> take 5 allTrees
[ S 1 L
, S 2 (B L L)
, S 3 (B L (B L L))
, S 3 (B (B L L) L)
, S 4 (B L (B L (B L L)))
]
#amalloy's answer is elegant, but it does require you to think about a way to generate the data in your specific domain (namely, how to split the "size" between the subtrees).
In general, you might really want to apply a function to all pairs from two infinite lists. Here's a function that does it. I bet it can still be done more elegantly.
data BTree = L | B BTree BTree
deriving (Eq, Ord, Show)
data SpecTree = S Integer BTree
deriving (Eq, Ord, Show)
leafNode :: SpecTree
leafNode = S 1 L
branch :: SpecTree -> SpecTree -> SpecTree
branch (S size1 sub1) (S size2 sub2) = S (size1 + size2) (B sub1 sub2)
allTrees :: [SpecTree]
allTrees = leafNode : infApply branch allTrees allTrees
infApply :: (a -> b -> c) -> [a] -> [b] -> [c]
infApply f xs ys = map (uncurry f) (infProduct xs ys)
-- All possible pairs from two infinite lists.
infProduct xs ys = infterleave (infNested xs ys)
-- A nested (infinite) list of (infinite) lists of pairs from the two given (infinite) lists.
infNested xs ys = [[(x, y) | x <- xs] | y <- ys]
-- Interleave the elements of an infinite collection of infinite lists.
infterleave xss =
infterleave' xss 0 0
where
infterleave' :: [[a]] -> Int -> Int -> [a]
infterleave' xss n m =
let (result, remainder) = snoc (xss !! n)
newXss = take n xss ++ [remainder] ++ drop (n+1) xss
in
result : (infterleave' newXss (if n < m then n+1 else 0) (if n < m then m else m+1))
snoc (x:xs) = (x, xs)
Related
Using the function mapEither for multiset's I can turn a MultiSet into a pair of two multisets. When f is returning Left the element is inserted into the first Multiset of the pair, and if f is returning Right the element is inserted into the second MultiSet of the pair.
How can I insert the same element into both MultiSets at the same time, as if f were returning Right and Left at the same time?
f:: LocalType -> Either LocalType LocalType
f (Sometype lt) = Left lt -- And Right lt
f lt = Left lt
parRule :: (MultiSet LocalType) -> (MultiSet LocalType)
parRule sequent = do
let list = MultiSet.mapEither f sequent
For reference, I use Data.Multiset package, https://hackage.haskell.org/package/multiset-0.3.4.3/docs/Data-MultiSet.html.
You can use a type like These to capture the ability to return both. You can then use toAscOccurList and fromOccurList (or fromAscOccurList if your function is monotonic) to compute the new MultiSet.
You could use These as Daniel Wagner suggests, but I would use a slightly different function to start with, which seems like a slightly better match to the library API. Furthermore, I would recommend a different implementation strategy for performance.
data SP a b = SP !a !b
toPair :: SP a b -> (a, b)
toPair (SP a b) = (a, b)
mapPairOcc :: (Ord b, Ord c) => (a -> Occur -> ((b, Occur), (c, Occur))) -> MultiSet a -> (MultiSet b, MultiSet c)
mapPairOcc f = toPair . mapPairOcc' f
mapPairOcc' :: (Ord b, Ord c) => (a -> Occur -> ((b, Occur), (c, Occur))) -> MultiSet a -> SP (MultiSet b) (MultiSet c)
mapPairOcc' f = foldl' go (SP empty empty) . toAscOccurList
where
go (SP bs cs) a
| ((b, bn), (c, cn)) <- f a
= SP (insertMany b bn bs) (insertMany c cn cs)
When you know that f is strictly monotone in the sense that
a < a' ==> fst (f a) < fst (f a') /\ snd (f a) < snd (f a')
it's possible to do better, building the results in O(n) time. The best way to do this seems to be to use Data.Map internals. I'll reuse the SP type from above.
import Data.Map.Lazy (Map)
import Data.MultiSet (MultiSet, Occur)
import qualified Data.MultiSet as MS
import qualified Data.Map.Internal as M
import Control.Monad (guard)
-- | Map over the keys and values in a map, producing
-- two maps with new keys and values. The passed function
-- must be strictly monotone in the keys in the sense
-- described above.
mapMaybeWithKey2Mono :: (k -> a -> (Maybe (l,b), Maybe (m,c))) -> Map k a -> (Map l b, Map m c)
mapMaybeWithKey2Mono f = toPair . mapMaybeWithKey2Mono' f
mapMaybeWithKey2Mono' :: (k -> a -> (Maybe (l,b), Maybe (m,c))) -> Map k a -> SP (Map l b) (Map m c)
mapMaybeWithKey2Mono' _ M.Tip = SP M.Tip M.Tip
mapMaybeWithKey2Mono' f (M.Bin _ kx x l r)
| (fl, fr) <- f kx x
= SP (groink fl mfl1 mfr1) (groink fr mfl2 mfr2)
where
groink :: Maybe (q, x) -> Map q x -> Map q x -> Map q x
groink m n o = case m of
Just (k', y) -> M.link k' y n o
Nothing -> M.link2 n o
SP mfl1 mfl2 = mapMaybeWithKey2Mono' f l
SP mfr1 mfr2 = mapMaybeWithKey2Mono' f r
Using this new general Map function, we can define the function we want on multisets:
mapPairAscOcc :: (a -> Occur -> ((b, Occur), (c, Occur))) -> MultiSet a -> (MultiSet b, MultiSet c)
mapPairAscOcc f m
| (p, q) <- mapMaybeWithKey2Mono go . MS.toMap $ m
= (MS.fromOccurMap p, MS.fromOccurMap q)
where
-- a -> Occur -> (Maybe (b, Occur), Maybe (c, Occur))
go a aocc
| ((b, bocc), (c, cocc)) <- f a aocc
= ( (b, bocc) <$ guard (bocc > 0)
, (c, cocc) <$ guard (cocc > 0) )
I took the function mapEither from the Data.MultiSet and modified it such that it supports These type.
-- | /O(n)/. Map and separate the 'This' and 'That' or 'These' results
-- modified function of mapEither to map both cases in case f return These
-- code of mapEither found in source code,
mapThese :: (Ord b, Ord c) => (a -> These b c) -> MultiSet a -> (MultiSet b, MultiSet c)
mapThese f = (\(ls,rs) -> (MultiSet.fromOccurList ls, MultiSet.fromOccurList rs)) . mapThese' . MultiSet.toOccurList
where mapThese' [] = ([],[])
mapThese' ((x,n):xs) = case f x of
This l -> let (ls,rs) = mapThese' xs in ((l,n):ls, rs)
That r -> let (ls,rs) = mapThese' xs in (ls, (r,n):rs)
These u i -> let (ls,rs) = mapThese' xs in ((u,n):ls, (i,n):rs)
In the case f returns These, both MultiSet's have an added element.
An ADT can be represented using the Scott Encoding by replacing products by tuples and sums by matchers. For example:
data List a = Cons a (List a) | Nil
Can be encoded using the Scott Encoding as:
cons = (λ h t c n . c h t)
nil = (λ c n . n)
But I couldn't find how nested types can be encoded using SE:
data Tree a = Node (List (Tree a)) | Leaf a
How can it be done?
If the Wikipedia article is correct, then
data Tree a = Node (List (Tree a)) | Leaf a
has Scott encoding
node = λ a . λ node leaf . node a
leaf = λ a . λ node leaf . leaf a
It looks like the Scott encoding is indifferent to (nested) types. All it's concerned with is delivering the correct number of parameters to the constructors.
Scott encodings are basically representing a T by the type of its case expression. So for lists, we would define a case expression like so:
listCase :: List a -> r -> (a -> List a -> r) -> r
listCase [] n c = n
listCase (x:xs) n c = c x xs
this gives us an analogy like so:
case xs of { [] -> n ; (x:xs) -> c }
=
listCase xs n (\x xs -> c)
This gives a type
newtype List a = List { listCase :: r -> (a -> List a -> r) -> r }
The constructors are just the values that pick the appropriate branches:
nil :: List a
nil = List $ \n c -> n
cons :: a -> List a -> List a
cons x xs = List $ \n c -> c x xs
We can work backwards then, from a boring case expression, to the case function, to the type, for your trees:
case t of { Leaf x -> l ; Node xs -> n }
which should be roughly like
treeCase t (\x -> l) (\xs -> n)
So we get
treeCase :: Tree a -> (a -> r) -> (List (Tree a) -> r) -> r
treeCase (Leaf x) l n = l x
treeCase (Node xs) l n = n xs
newtype Tree a = Tree { treeCase :: (a -> r) -> (List (Tree a) -> r) -> r }
leaf :: a -> Tree a
leaf x = Tree $ \l n -> l x
node :: List (Tree a) -> Tree a
node xs = Tree $ \l n -> n xs
Scott encodings are very easy tho, because they're only case. Church encodings are folds, which are notoriously hard for nested types.
(for the context to this see this recent SO entry).
I tried to come up with the definition of zip using foldr only:
zipp :: [a] -> [b] -> [(a,b)]
zipp xs ys = zip1 xs (zip2 ys)
where
-- zip1 :: [a] -> tq -> [(a,b)] -- zip1 xs :: tr ~ tq -> [(a,b)]
zip1 xs q = foldr (\ x r q -> q x r ) n xs q
-------- c --------
n q = []
-- zip2 :: [b] -> a -> tr -> [(a,b)] -- zip2 ys :: tq ~ a -> tr -> [(a,b)]
zip2 ys x r = foldr (\ y q x r -> (x,y) : r q ) m ys x r
---------- k --------------
m x r = []
{-
zipp [x1,x2,x3] [y1,y2,y3,y4]
= c x1 (c x2 (c xn n)) (k y1 (k y2 (k y3 (k y4 m))))
--------------- ----------------------
r q
= k y1 (k y2 (k y3 (k y4 m))) x1 (c x2 (c xn n))
---------------------- ---------------
q r
-}
It "works" on paper, but gives two "infinite type" errors:
Occurs check: cannot construct the infinite type:
t1 ~ (a -> t1 -> [(a, b)]) -> [(a, b)] -- tr
Occurs check: cannot construct the infinite type:
t0 ~ a -> (t0 -> [(a, b)]) -> [(a, b)] -- tq
Evidently, each type tr, tq, depends on the other, in a circular manner.
Is there any way to make it work, by some type sorcery or something?
I use Haskell Platform 2014.2.0.0 with GHCi 7.8.3 on Win7.
My issue with using Fix to type zipp (as I pointed out in the comments to Carsten's answer to the prior question) is that no total language contains the Fix type:
newtype Fix a = Fix { unFix :: Fix a -> [a] }
fixList :: ([a] -> [a]) -> [a]
fixList f = (\g -> f (unFix g g)) $ Fix (\g -> f (unFix g g))
diverges :: [a]
diverges = fixList id
This may seem to be an obscure issue, but it really is nice to have an implementation in a total language, because that also constitutes a formal proof of termination. So let's find a type for zipp in Agda.
First, let's stick for a while with Haskell. If we manually unfold the definitions of zip1 and zip2 for some fixed lists, we find that all of the unfoldings have proper types, and we can apply any unfolding of zip1 to any unfolding of zip2, and the types line up (and we get the correct results).
-- unfold zip1 for [1, 0]
f0 k = [] -- zip1 []
f1 k = k 0 f0 -- zip1 [0]
f2 k = k 1 f1 -- zip1 [1, 0]
-- unfold zip2 for [5, 3]
g0 x r = [] -- zip2 []
g1 x r = (x, 3) : r g0 -- zip2 [3]
g2 x r = (x, 5) : r g1 -- zip2 [3, 5]
-- testing
f2 g2 -- [(1, 5), (0, 3)]
f2 g0 -- []
-- looking at some of the types in GHCI
f0 :: t -> [t1]
f1 :: Num a => (a -> (t1 -> [t2]) -> t) -> t
g0 :: t -> t1 -> [t2]
g1 :: Num t1 => t -> ((t2 -> t3 -> [t4]) -> [(t, t1)]) -> [(t, t1)]
We conjecture that the types can be unified for any particular combination of zip1-s and zip2-s, but we can't express this with the usual foldr, because there is an infinite number of different types for all the unfoldings. So we switch to Agda now.
Some preliminaries and the usual definition for dependent foldr:
open import Data.Nat
open import Data.List hiding (foldr)
open import Function
open import Data.Empty
open import Relation.Binary.PropositionalEquality
open import Data.Product
foldr :
{A : Set}
(B : List A → Set)
→ (∀ {xs} x → B xs → B (x ∷ xs))
→ B []
→ (xs : List A)
→ B xs
foldr B f z [] = z
foldr B f z (x ∷ xs) = f x (foldr B f z xs)
We notice that the types of the unfoldings depend on the length of the to-be-zipped list, so we concoct two functions to generate these types. A is the type of the elements of the first list, B is the type of the elements of the second list and C is a parameter for the argument that we ignore when we get to the end of the list. n is the length of the list, of course.
Zip1 : Set → Set → Set → ℕ → Set
Zip1 A B C zero = C → List (A × B)
Zip1 A B C (suc n) = (A → Zip1 A B C n → List (A × B)) → List (A × B)
Zip2 : Set → Set → Set → ℕ → Set
Zip2 A B C zero = A → C → List (A × B)
Zip2 A B C (suc n) = A → (Zip2 A B C n → List (A × B)) → List (A × B)
We need to prove now that we can indeed apply any Zip1 to any Zip2, and get back a List (A × B) as result.
unifyZip : ∀ A B n m → ∃₂ λ C₁ C₂ → Zip1 A B C₁ n ≡ (Zip2 A B C₂ m → List (A × B))
unifyZip A B zero m = Zip2 A B ⊥ m , ⊥ , refl
unifyZip A B (suc n) zero = ⊥ , Zip1 A B ⊥ n , refl
unifyZip A B (suc n) (suc m) with unifyZip A B n m
... | C₁ , C₂ , p = C₁ , C₂ , cong (λ t → (A → t → List (A × B)) → List (A × B)) p
The type of unifyZip in English: "for all A and B types and n and m natural numbers, there exist some C₁ and C₂ types such that Zip1 A B C₁ n is a function from Zip2 A B C₂ m to List (A × B)".
The proof itself is straightforward; if we hit the end of either zippers, we instantiate the input type of the empty zipper to the type of the other zipper. The use of the empty type (⊥) communicates that the choice of type for that parameter is arbitrary. In the recursive case we just bump the equality proof by one step of iteration.
Now we can write zipp:
zip1 : ∀ A B C (as : List A) → Zip1 A B C (length as)
zip1 A B C = foldr (Zip1 A B C ∘ length) (λ x r k → k x r) (λ _ → [])
zip2 : ∀ A B C (bs : List B) → Zip2 A B C (length bs)
zip2 A B C = foldr (Zip2 A B C ∘ length) (λ y k x r → (x , y) ∷ r k) (λ _ _ → [])
zipp : ∀ {A B : Set} → List A → List B → List (A × B)
zipp {A}{B} xs ys with unifyZip A B (length xs) (length ys)
... | C₁ , C₂ , p with zip1 A B C₁ xs | zip2 A B C₂ ys
... | zxs | zys rewrite p = zxs zys
If we squint a bit and try to ignore ignore the proofs in the code, we find that zipp is indeed operationally the same as the Haskell definition. In fact, the code becomes exactly the same after all the erasable proofs have been erased. Agda probably doesn't do this erasure, but the Idris compiler certainly does.
(As a side note, I wonder if we could make use of clever functions like zipp in fusion optimizations. zipp seems to be more efficient than Oleg Kiselyov's fold zipping. But zipp doesn't seem to have a System F type; maybe we could try encoding data as dependent eliminators (induction principles) instead of the usual eliminators, and try to fuse those representations?)
Applying the insight from my other answer, I was able to solve it, by defining two mutually-recursive types:
-- tr ~ tq -> [(a,b)]
-- tq ~ a -> tr -> [(a,b)]
newtype Tr a b = R { runR :: Tq a b -> [(a,b)] }
newtype Tq a b = Q { runQ :: a -> Tr a b -> [(a,b)] }
zipp :: [a] -> [b] -> [(a,b)]
zipp xs ys = runR (zip1 xs) (zip2 ys)
where
zip1 = foldr (\ x r -> R $ \q -> runQ q x r ) n
n = R (\_ -> [])
zip2 = foldr (\ y q -> Q $ \x r -> (x,y) : runR r q ) m
m = Q (\_ _ -> [])
main = print $ zipp [1..3] [10,20..]
-- [(1,10),(2,20),(3,30)]
The translation from type equivalency to type definition was purely mechanical, so maybe compiler could do this for us, too!
I have not seen any mention of binary numerals in lambda calculus. Church numerals are unary system.
I had asked a question of how to do this in Haskell here: How to implement Binary numbers in Haskell
But even after I saw and understood that answer, I could not understand how to do this in pure untyped lambda calculus.
So here is my question:
Are binary numerals defined in untyped lambda calculus and have the successor and predecessor functions also been defined for them?
Church encoding of a recursive data type is precisely its fold (catamorphism). Before we venture into the messy and not-very-readable world of Church encoded data types, we'll implement these two functions on the representation given in the previous answer. And because we'd like to transfer then easily to the Church encoded variants, we'll have to do both via fold.
Here's the representation from the previous answer (I picked the one which will be easier to work with) and its catamorphism:
data Bin = LSB | Zero Bin | One Bin
foldBin :: (r -> r) -- ^ Zero case.
-> (r -> r) -- ^ One case.
-> r -- ^ LSB case.
-> Bin
-> r
foldBin z o l = go
where
go LSB = l
go (Zero n) = z (go n)
go (One n) = o (go n)
The suc function adds one to the least significant bit and keeps propagating the carries we get. Once the carry is added to Zero (and we get One), we can stop propagating. If we get to the most significant bit and still have a carry to propagate, we'll add new most significant bit (this is the apLast helper function):
suc :: Bin -> Bin
suc = apLast . foldBin
(\(c, r) -> if c
then (False, One r)
else (False, Zero r))
(\(c, r) -> if c
then (True, Zero r)
else (False, One r))
(True, LSB)
where
apLast (True, r) = One r
apLast (False, r) = r
The pre function is very similar, except the Boolean now tells us when to stop propagating the -1:
pre :: Bin -> Bin
pre = removeZeros . snd . foldBin
(\(c, r) -> if c
then (True, One r)
else (False, Zero r))
(\(c, r) -> if c
then (False, Zero r)
else (False, One r))
(True, LSB)
This might produce a number with leading zero bits, we can chop them off quite easily. full is the whole number at any given time, part is full without any leading zeros.
removeZeros :: Bin -> Bin
removeZeros = snd . foldBin
(\(full, part) -> (Zero full, part))
(\(full, part) -> (One full, One full))
(LSB, LSB)
Now, we have to figure out the Church encoding. We'll need Church encoded Booleans and Pairs before we start. Note the following code needs RankNTypes extension.
newtype BoolC = BoolC { runBool :: forall r. r -> r -> r }
true :: BoolC
true = BoolC $ \t _ -> t
false :: BoolC
false = BoolC $ \_ f -> f
if' :: BoolC -> a -> a -> a
if' (BoolC f) x y = f x y
newtype PairC a b = PairC { runPair :: forall r. (a -> b -> r) -> r }
pair :: a -> b -> PairC a b
pair a b = PairC $ \f -> f a b
fst' :: PairC a b -> a
fst' (PairC f) = f $ \a _ -> a
snd' :: PairC a b -> b
snd' (PairC f) = f $ \_ b -> b
Now, at the beginning I said that Church encoding of a data type is its fold. Bin has the following fold:
foldBin :: (r -> r) -- ^ Zero case.
-> (r -> r) -- ^ One case.
-> r -- ^ LSB case.
-> Bin
-> r
Given a b :: Bin argument, once we apply foldBin to it, we get a precise representation of b in terms of a fold. Let's write a separate data type to keep things tidy:
newtype BinC = BinC { runBin :: forall r. (r -> r) -> (r -> r) -> r -> r }
Here you can clearly see it's the type of foldBin without the Bin argument. Now, few helper functions:
lsb :: BinC
lsb = BinC $ \_ _ l -> l
zero :: BinC -> BinC
zero (BinC f) = BinC $ \z o l -> z (f z o l)
one :: BinC -> BinC
one (BinC f) = BinC $ \z o l -> o (f z o l)
-- Just for convenience.
foldBinC :: (r -> r) -> (r -> r) -> r -> BinC -> r
foldBinC z o l (BinC f) = f z o l
We can now rewrite the previous definitions in terms of BinC nearly with 1:1 correspondence:
suc' :: BinC -> BinC
suc' = apLast . foldBinC
(\f -> runPair f $ \c r -> if' c
(pair false (one r))
(pair false (zero r)))
(\f -> runPair f $ \c r -> if' c
(pair true (zero r))
(pair false (one r)))
(pair true lsb)
where
apLast f = runPair f $ \c r -> if' c
(one r)
r
pre' :: BinC -> BinC
pre' = removeZeros' . snd' . foldBinC
(\f -> runPair f $ \c r -> if' c
(pair true (one r))
(pair false (zero r)))
(\f -> runPair f $ \c r -> if' c
(pair false (zero r))
(pair false (one r)))
(pair true lsb)
removeZeros' :: BinC -> BinC
removeZeros' = snd' . foldBinC
(\f -> runPair f $ \full part -> pair (zero full) part)
(\f -> runPair f $ \full part -> pair (one full) (one full))
(pair lsb lsb)
The only significant difference is that we can't pattern match on pairs, so we have to use:
runPair f $ \a b -> expr
instead of:
case f of
(a, b) -> expr
Here are the conversion functions and a few tests:
toBinC :: Bin -> BinC
toBinC = foldBin zero one lsb
toBin :: BinC -> Bin
toBin (BinC f) = f Zero One LSB
numbers :: [BinC]
numbers = take 100 $ iterate suc' lsb
-- [0 .. 99]
test1 :: [Int]
test1 = map (toInt . toBin) numbers
-- 0:[0 .. 98]
test2 :: [Int]
test2 = map (toInt . toBin . pre') numbers
-- replicate 100 0
test3 :: [Int]
test3 = map (toInt . toBin) . zipWith ($) (iterate (pre' .) id) $ numbers
Here's the code written in untyped lambda calculus:
lsb = λ _ _ l. l
zero = λ f. λ z o l. z (f z o l)
one = λ f. λ z o l. o (f z o l)
foldBinC = λ z o l f. f z o l
true = λ t _. t
false = λ _ f. f
if' = λ f x y. f x y
pair = λ a b f. f a b
fst' = λ f. f λ a _. a
snd' = λ f. f λ _ b. b
(∘) = λ f g x. f (g x)
removeZeros' = snd' ∘ foldBinC
(λ f. f λ full part. pair (zero full) part)
(λ f. f λ full part. pair (one full) (one full))
(pair lsb lsb)
apLast = λ f. f λ c r. if' c (one r) r
suc' = apLast ∘ foldBinC
(λ f. f λ c r. if' c
(pair false (one r))
(pair false (zero r)))
(λ f. f λ c r. if' c
(pair true (zero r))
(pair false (one r)))
(pair true lsb)
pre' = removeZeros' ∘ snd' ∘ foldBinC
(λ f. f λ c r. if' c
(pair true (one r))
(pair false (zero r)))
(λ f. f λ c r. if' c
(pair false (zero r))
(pair false (one r)))
(pair true lsb)
The following paper answers your question. As you can see, there have been investigated
several ways to encode binary numerals in lambda calculus.
An Investigation of Compact and Efficient Number Representations in
the Pure Lambda Calculus
Torben AE. Mogensen
http://link.springer.com/content/pdf/10.1007%2F3-540-45575-2_20
Abstract. We argue that a compact right-associated binary number
representation gives simpler operators and better efficiency than the
left-associated binary number representation proposed by den Hoed and
investigated by Goldberg. This representation is then generalised to
higher number-bases and it is argued that bases between 3 and 5 can
give higher efficiency than binary representation.
I had to implement the haskell map function to work with church lists which are defined as following:
type Churchlist t u = (t->u->u)->u->u
In lambda calculus, lists are encoded as following:
[] := λc. λn. n
[1,2,3] := λc. λn. c 1 (c 2 (c 3 n))
The sample solution of this exercise is:
mapChurch :: (t->s) -> (Churchlist t u) -> (Churchlist s u)
mapChurch f l = \c n -> l (c.f) n
I have NO idea how this solution works and I don't know how to create such a function. I have already experience with lambda calculus and church numerals, but this exercise has been a big headache for me and I have to be able to understand and solve such problems for my exam next week. Could someone please give me a good source where I could learn to solve such problems or give me a little guidance on how it works?
All lambda calculus data structures are, well, functions, because that's all there is in the lambda calculus. That means that the representation for a boolean, tuple, list, number, or anything, has to be some function that represents the active behavior of that thing.
For lists, it is a "fold". Immutable singly-linked lists are usually defined List a = Cons a (List a) | Nil, meaning the only ways you can construct a list is either Nil or Cons anElement anotherList. If you write it out in lisp-style, where c is Cons and n is Nil, then the list [1,2,3] looks like this:
(c 1 (c 2 (c 3 n)))
When you perform a fold over a list, you simply provide your own "Cons" and "Nil" to replace the list ones. In Haskell, the library function for this is foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
Look familiar? Take out the [a] and you have the exact same type as Churchlist a b. Like I said, church encoding represents lists as their folding function.
So the example defines map. Notice how l is used as a function: it is the function that folds over some list, after all. \c n -> l (c.f) n basically says "replace every c with c . f and every n with n".
(c 1 (c 2 (c 3 n)))
-- replace `c` with `(c . f)`, and `n` with `n`
((c . f) 1 ((c . f) 2) ((c . f) 3 n)))
-- simplify `(foo . bar) baz` to `foo (bar baz)`
(c (f 1) (c (f 2) (c (f 3) n))
It should be apparent now that this is indeed a mapping function, because it looks just like the original, except 1 turned into (f 1), 2 to (f 2), and 3 to (f 3).
So let's start by encoding the two list constructors, using your example as reference:
[] := λc. λn. n
[1,2,3] := λc. λn. c 1 (c 2 (c 3 n))
[] is the end of list constructor, and we can lift that straight from the example. [] already has meaning in haskell, so let's call ours nil:
nil = \c n -> n
The other constructor we need takes an element and an existing list, and creates a new list. Canonically, this is called cons, with the definition:
cons x xs = \c n -> c x (xs c n)
We can check that this is consistent with the example above, since
cons 1 (cons 2 (cons 3 nil))) =
cons 1 (cons 2 (cons 3 (\c n -> n)) =
cons 1 (cons 2 (\c n -> c 3 ((\c' n' -> n') c n))) =
cons 1 (cons 2 (\c n -> c 3 n)) =
cons 1 (\c n -> c 2 ((\c' n' -> c' 3 n') c n) ) =
cons 1 (\c n -> c 2 (c 3 n)) =
\c n -> c 1 ((\c' n' -> c' 2 (c' 3 n')) c n) =
\c n -> c 1 (c 2 (c 3 n)) =
Now, consider the purpose of the map function - it is to apply the given function to each element of the list. So let's see how that works for each of the constructors.
nil has no elements, so mapChurch f nil should just be nil:
mapChurch f nil
= \c n -> nil (c.f) n
= \c n -> (\c' n' -> n') (c.f) n
= \c n -> n
= nil
cons has an element and a rest of list, so, in order for mapChurch f to work propery, it must apply f to the element and mapChurch f to rest of the list. That is, mapChurch f (cons x xs) should be the same as cons (f x) (mapChurch f xs).
mapChurch f (cons x xs)
= \c n -> (cons x xs) (c.f) n
= \c n -> (\c' n' -> c' x (xs c' n')) (c.f) n
= \c n -> (c.f) x (xs (c.f) n)
-- (c.f) x = c (f x) by definition of (.)
= \c n -> c (f x) (xs (c.f) n)
= \c n -> c (f x) ((\c' n' -> xs (c'.f) n') c n)
= \c n -> c (f x) ((mapChurch f xs) c n)
= cons (f x) (mapChurch f xs)
So since all lists are made from those two constructors, and mapChurch works on both of them as expected, mapChurch must work as expected on all lists.
Well, we can comment the Churchlist type this way to clarify it:
-- Tell me...
type Churchlist t u = (t -> u -> u) -- ...how to handle a pair
-> u -- ...and how to handle an empty list
-> u -- ...and then I'll transform a list into
-- the type you want
Note that this is intimately related to the foldr function:
foldr :: (t -> u -> u) -> u -> [t] -> u
foldr k z [] = z
foldr k z (x:xs) = k x (foldr k z xs)
foldr is a very general function that can implement all sorts of other list functions. A trivial example that will help you is implementing a list copy with foldr:
copyList :: [t] -> [t]
copyList xs = foldr (:) [] xs
Using the commented type above, foldr (:) [] means this: "if you see an empty list return the empty list, and if you see a pair return head:tailResult."
Using Churchlist, you can easily write the counterpart this way:
-- Note that the definitions of nil and cons mirror the two foldr equations!
nil :: Churchlist t u
nil = \k z -> z
cons :: t -> Churchlist t u -> Churchlist t u
cons x xs = \k z -> k x (xs k z)
copyChurchlist :: ChurchList t u -> Churchlist t u
copyChurchlist xs = xs cons nil
Now, to implement map, you just need to replace cons with a suitable function, like this:
map :: (a -> b) -> [a] -> [b]
map f xs = foldr (\x xs' -> f x:xs') [] xs
Mapping is like copying a list, except that instead of just copying the elements verbatim you apply f to each of them.
Study all of this carefully, and you should be able to write mapChurchlist :: (t -> t') -> Churchlist t u -> Churchlist t' u on your own.
Extra exercise (easy): write these list functions in terms of foldr, and write counterparts for Churchlist:
filter :: (a -> Bool) -> [a] -> [a]
append :: [a] -> [a] -> [a]
-- Return first element of list that satisfies predicate, or Nothing
find :: (a -> Bool) -> [a] -> Maybe a
If you're feeling like tackling something harder, try writing tail for Churchlist. (Start by writing tail for [a] using foldr.)