You can represent the "marks" of LoF as data-structures in Python composed entirely of tuples. For example:

- A mark: ()
- A mark next to a mark: (), ()
- A mark within a mark: ((),)
- and so on...

It is known that the propositional calculus can be represented by the "arithmetic of the mark". The two rules suffice:

((),) == nothing

() == (), ()

There are details, but essentially math and logic can be derived from the behaviour of "the mark".

After reading "Every Bit Counts" I spent some time trying to come up with an EBC coding for the circle language (Laws of Form expressed as tuples, See Burnett-Stuart's "The Markable Mark")

With a little skull-sweat I found the following encoding:

- For the empty state (no mark) write '0'.
- Start at the left, if you encounter a boundary (one "side" of a mark, or the left, opening, parenthesis) write a '1'.
- Repeat for the contents of the mark, then the neighbors.

_ = 0

() = 100

(), () = 10100

((),) = 11000

and so on...

I recognized these numbers as the patterns of the language called 'Iota'.

Briefly, the SK combinators:

S = λx.λy.λz.xz(yz)

K = λx.λy.x

Or, in Python:

S = lambda x: lambda y: lambda z: x(z)(y(z))

K = lambda x: lambda y: x

can be used to define the combinator used to implement Iota:

i = λc.cSK

or,

i = lambda c: c(S)(K)

And the bitstrings are decoded like so: if you encounter '0' return i, otherwise decode two terms and apply the first to the second.

In other words, the empty space, or '0', corresponds to i:

_ = 0 = i

and the mark () corresponds to i applied to itself:

() = 100 = i(i)

which is an Identity function I.

The S and K combinators can be "recovered" by application of i to itself like so (this is Python code, note that I am able to use 'is' instead of the weaker '==' operator. The i combinator is actually recovering the very same lambda functions used to create it. Neat, eh?):

K is i(i(i(i))) is decode('1010100')

S is i(i(i(i(i)))) is decode('101010100')

Where decode is defined (in Python) as:

decode = lambda path: _decode(path)[0]

def _decode(path):

bit, path = path[0], path[1:]

if bit == '0':

return i, path

A, path = _decode(path)

B, path = _decode(path)

return A(B), path

(I should note that there is an interesting possibility of encoding the tuples two ways: contents before neighbors (depth-first) or neighbors before content (breadth-first). Here we look at the former.)

So, in "Laws of Form" K is ()()() and S is ()()()() and, amusingly, the identity function I is ().

The term '(())foo' applies the identity function to foo, which matches the behaviour of the (()) form in the Circle Arithmetic (()) == _ ("nothing".)

(())A = i(i)(A) = I(A) = A

I just discovered this (that the Laws of Form have a direct mapping to the combinator calculus by means of λc.cSK) and I haven't found anyone else mentioning yet (although this article might, I haven't worked my way all the way through it yet.)

There are many interesting avenues to explore from here, and I personally am just beginning, but this seems like something worth reporting.

## 1 comment:

I am so embarrassed. Any content-free binary tree fits the *ii form and of course the one-combinator basis(es) are content-free binary trees. D'oh!

Post a Comment