So once again the time arrives when Santa is looking for Rudolph and I am
writing a blog post for the yearly F# advent

As always I hope I can provide you some insight and fun – practicability is not
the main focus (as you surely find out soon).

the task at hand

Santa cannot remember christmas-eves date (he is getting quite old kids) but he
remembers it to be a the day after a prime number (tomorrow as it happens) – so
whenever the date showing on his calendar is happens to be prime he rushes out
to see if his elves are getting ready too (this prevents lot’s of running for
Santa).

Of course he has to do this using F# but the Crinch got really nasty this
year and so Santa is only allowed to use pure functions.

the real thing

Ok the story was lame … I just want to show you how powerful Lambda-Calculus
is by writing a prime-test using nothing (ok almost nothing) but functions.

The reason why I say almost nothing is that I’ll want to keep some types around
(it’s F# after all) and I’ll need rank-2 types for some of this (meaning I
have to get some for-all types … in there). In order to do this I am usinginterfaces and object-expressions – you can find more about this and
the reasons behind this in this blog-post here.

So no this is not 100% pure lambda-calculus and I take some freedom – but
you can remove the glimmer and translate everything down mechanically – you
just have to write a lot more 😉

DISCLAIMER The following code is not about performance at all – indeed
it will work for small numbers but you’ll have to wait quite some time to check
if 1000 is prime or not – so please keep that in mind.

Representing the booleans

Later on I have to make some decisions (Is this a prime? Is a number zero? …)
and of course you’ll want booleans and if for these situations.

So the first task is to represent both of these using nothing but (lambda-) functions –
also known as Church encodings.

The basic idea is simple: both true and false will be functions of two
arguments (yeah there is only one … curry … bla bla – you know this stuff)
and true will just decide to return the first argument while false will
return the second.

pairs

The idea behind this is similar to the idea behind booleans – A pair of two
values a and b is represented as a higher-order function – you give the pair
a function taking two arguments and the pair is feeding a and b into it.
I like to think of this function as a selector function because most often
you feed in a function to select the first or second argument out.

numbers

To represent numbers you start by observing that a natural number n is1+1+1+...+0 (with n times 1+) where 1+ is the sucessor-function and0 is the initial value.

Now of course in lambda calculus there are no values but what we are doing is
to give a higher-order function taking in both the successor and the initial
value and then the task is to apply the sucessor n-times to the initial value.

So let’s start with the type/interface:

type NatC =
abstract apply : ('a -> 'a) -> 'a -> 'a

then zero is quite simple:

let zero : NatC =
{ new NatC with member __.apply _ z = z }

As you can see zero just ignores the successor argument and returns theinitial value.

check for equality

Next I need to know if two numbers are equal – I think an easy approach is to
first think how to decide if a number is zero:

let isZero (n : NatC) : BoolC =
n.apply (fun _ -> falseC) trueC

which is simply done by applying const false n-times to true (so if n=0
we will keep true and if not we will override this n-1 times with true).

From this we can easily decide if two numbers are equal: they are if the
difference is zero – but since the way the subtraction of n-m will yield always
0 if m > n here we have to do it twice and make sure that both differences are:

divisibility

Now it’s getting a bit more interesting because now I want to know if a
number m divides another n – and I’ll do this by repeatedly subtracting m fromn and look if I end up at m – and because I am to lazy to introduce looping
or recursion I’ll just do it n times and cap the value at m:

Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.

You can adjust all of your cookie settings by navigating the tabs on the left hand side.

Strictly Necessary Cookies

Strictly Necessary Cookie should be enabled at all times so that we can save your preferences for cookie settings.

disable

If you disable this cookie, we will not be able to save your preferences. This means that every time you visit this website you will need to enable or disable cookies again.