Rational
Rational is a Turing-complete Programming Language based on Rational numbers designed by User:Cel7t. A program in Rational is just a single Rational number, represented in Hexadecimal for convenience. A transpiler to Unlambda can be found here: [1]
An Example
The Identity function in Rational is given by 1A
or 1A.8C
How does it work?
There are two parts to any Rational program; the Natural part and the Fractional part.
Programs with just Natural Parts
For programs with just Natural parts, such as 1A
, the number is first converted to base 4.
1A (hexadecimal) = 011010 (binary) = 122 (base 4)
Now, as there is no Fractional part, we use the default evaluation method.
The default evaluation maps 1
to S
, 2
to K
and 0
to `
. 3
is mapped to nothing.
S
and K
are the S and K combinators, and `
is the backquote operator from Unlambda.
Note that any Rational program has an infinite number of `
to the left for this reason, and that's by design.
Thanks to this any list of combinators ABCDE...
will be evaluated as ...````ABCDE...
hence ((((A) B) C) D) E...
Putting it all together, we get 1A = ...``SKK
And this evaluates to the identity function.
/Complete/ evaluation of a Rational program requires one to find the smallest number that is computationally equally to ours.
Hence, if we only consider Natural numbers, the Complete evaluation of 1A
will be 19
Complete evaluation is not guaranteed to be possible in finite time.
Programs with Fractional Parts
The most interesting part of Rational is programs with Fractional parts.
Let us take 1A.A3
as our example.
We first convert .A3
to base 4.
.A3 (hexadecimal) = .10100011 (binary) = .2203 (base 4)
Now, we take the digits after the .
and break them into a list of digit sequences delimited by zeroes
.2203 = (22, 3)
Note that there are an infinite number of empty sequences after 22
and 3
. This is by design too.
The last mapping was done to the 2nd sequence so we take our size
as 2 + 1 = 3
.
The size
is always the index of the last nonempty mapping plus 1.
Now, for the Natural part, we first break it into chunks of size n such that 2^n is the smallest power of 2 greater than or equal to size
.
This can be calculated with ceiling(log(size)/log(2))
Now, every chunk of binary digits is mapped to values as such:
0
is mapped to `
, and other numbers are mapped to the list of sequences such that 1
= first entry in that list, 2
= second entry, etc.
Hence, in our example 1A = 011010 = 01 10 10 = 22 3 3
The sequences in the list are evaluated as follows:
1 = `
, 2 = S
and 3 = K
. Hence our sequence becomes 22 3 3 = SS K K = SSKK
.
Hence, 1A.A3 = ...```SSKK
.
Why Rational is interesting
Rational offers a unique perspective on programs as numbers and computation as a mapping of rational numbers to other rational numbers. There is an interesting exercise that can be done with Rational, which is trying to find the smallest Rational number that equates to a given program.
Using the Transpiler
You will need Guile Scheme to run the Transpiler. The Transpiler takes in a Rational program and outputs an equivalent Unlambda expression.
./rational.scm "1A.A3" ```SSKK