Reputation: 205
I am currently learning Haskell and also participating in a rather theoretical lecture about functional programming at university.
I know that this is purely theoretical/academic question, but nevertheless I am interested how to express different simple functions simply with pure lambda calculus (i.e. without any constants defined).
Some lecture materials of mine define the boolean values such as:
True = \xy.x
False = \xy.y
(\ denoting the lambda symbol)
If they are defined like these selector functions, the if-condition can be easily defined as:
If = \x.x
Now, I'm trying to come up with some short form for the logical "and"-function. My first guess is:
and = \xy.{(If x)[(If y) True False] False}
So basically this lambda function would receive 2 arguments u v where both have to be typed like True/False. If I do various beta-reductions with all 4 combinations of the logic table I receive the right result.
Nevertheless this function looks a little ugly and I'm thinking about making it more elegant. Any proposals here?
Upvotes: 8
Views: 1280
Reputation: 74344
We can just do reductions on your answer to get a pithier one.
First some warm-ups. Obviously IF x ==> x
, and as TRUE TRUE FALSE ==> TRUE
and FALSE TRUE FALSE ==> FALSE
if x
is a boolean then x TRUE FALSE ==> x
.
Now we reduce
\x y . (IF x) ( (IF y) TRUE FALSE ) FALSE
\x y . x ( y TRUE FALSE ) FALSE -- using IF x ==> x
\x y . x ( y ) FALSE -- using y TRUE FALSE ==> y
\x y . x y FALSE -- clean up
and this expression still works with truth tables
AND TRUE TRUE = TRUE TRUE FALSE = TRUE
AND FALSE TRUE = FALSE TRUE FALSE = FALSE
AND TRUE FALSE = TRUE FALSE FALSE = FALSE
AND FALSE FALSE = FALSE FALSE FALSE = FALSE
Upvotes: 11
Reputation: 8937
Church's booleans, right? :) I developed for fun a small module by using the RankNTypes
feature in order to represent them as close to the original Lambda calculus version as possible.
So, if you enable RankNTypes
:
{-# LANGUAGE RankNTypes #-}
You might represent the type of Church booleans as the functions a -> a -> a
for any type a
, thus having a compact representation for True and False very similar to yours. The typing here will allow us to compose the functions more freely, though.
type CBool = forall a. a -> a -> a
ctrue :: CBool
ctrue t f = t
cfalse :: CBool
cfalse t f = f
Now, how is a conjunction written in these terms? Let's say you have a left operand l
and a right operand r
which are both CBool
, that is, functions a -> a -> a
which either return you the first or the second parameter depending on whether it's ctrue
or cfalse
. You can evaluate this function by giving in input r
as the first parameter and l
itself as the third. If l
is ctrue
, then it will evaluate to its first parameter, which is r
: should it be ctrue
again, then our conjunction is satisfied. In any other case, cfalse
will be returned.
cand :: CBool -> CBool -> CBool
cand l r = l r l
The disjunction can be represented in a similar way, with the same trick of directly evaluating the functions representing booleans given in input. Except that here you'll swap the two arguments for evaluating l
: if l
is ctrue
, it will return l
itself otherwise it is all up to r
's value
cor :: CBool -> CBool -> CBool
cor l r = l l r
By enabling RankNTypes, I think this is the closest as you can get to the original definitions of conjunction and disjunction for Church's boolean numbers in Lambda Calculus. I also wrote functions to translate back and forth from regular Bool and CBool, the negation, and also Church's natural numbers with the same style. You can find the whole source code at https://github.com/rtraverso86/haskkit/blob/master/Haskkit/Data/Church.hs.
Upvotes: 3
Reputation: 60463
Well, and True
is the identity function, and and False
is the constant function returning False
, so we can use that.
and = \x. if x id (const False)
which has a nice symmetry with
or = \x. if x (const True) id
(where
id = \x. x
const = \x y. x
)
The common pattern for eliminators takes the data at the end of the argument list which often works elegantly with pointfree style, so if you defined:
cond = \t f b. b t f
then you could express
and = cond id (const False)
or = cond (const True) id
which is the best I've got. There are probably yet more elegant formulations by viewing bools in cool ways.
Upvotes: 4