SKM Calculus

From Esolang
Jump to navigation Jump to search
SKM Calculus
Paradigm(s) Functional
Designed by User:Hakerh400
Appeared in 2022
Computational class Turing complete
Major implementations Implemented
File extension(s) .txt

SKM Calculus is an esoteric computational model invented by User:Hakerh400 in 2022.

Overview

SKM calculus is very similar to SKI calculus, but has additional combinator M, which cannot be implemented in SKI.

Combinators

There are three combinators: K, S and M. Two combinators are equal iff they have the same name (so, for example, any two K are equal, but K is not equal to S).

Expression

An SKM expression is either a combinator, or an application of two expressions. All expressions are finite. Application of expressions a and b is represented as a b. Application is left-associative (for example, a b c is interpreted as (a b) c, and not a (b c)). In application a b, expression a is called function and expression b is called argument.

Reduction step

For any expressions a and b, expression K a b reduces to a in a single step.

For any expressions a, b and c, expression S a b c reduces to a c (b c) in a single step.

For any expression a, expression M a reduces to a iff a reduces either to K or S.

Reduction

Expression a reduces to expression b iff a can be transformed to b by applying finitely many reduction steps.

However, unlike in SKI, reduction is more restrictive. For example, in SKI, if a reduces to b, then f a reduces to f b for any SKI expressions a, b and f. That's not the case in SKM. We can't reduce argument before we reduce the function (except if the function is M, because that is allowed by the third reduction step rule). For example, K (K K K) does not reduce any further, because no reduction steps can be applied to the main expression, despite the fact that K K K reduces to K. On the other hand, M (K K K) indeed reduces to K (first reduces to M K and then to K).

We say "reduces to" instead of "can be reduced to", because there is only one way to reduce expressions. If a reduces to b1 in n1 steps, and a reduces to b2 in n2 steps, then n1 = n2 implies b1 = b2 (here n1 and n2 are natural numbers, while b1 = b2 represents syntactical equality).

Reduction is syntactically represented by a ==> b (expression a reduces to expression b).

Motive

The idea behind the M combinator is to allow implementing safe data structures without having data types. In SKI (and untyped lambda calculus), we can't have a data structure that we can safely inspect. For example, in SKI we can implement natural numbers using Church numerals, but we can't reliably tell whether a number is zero or a successor, because the only way to inspect a value (expression) is to call it and hope the result will be something meaningful. However, we may have any value, even a value that doesn't represent a valid Church numeral and which returns some junk when called.

The point of the M combinator is to have strict pattern matches that give some guarantee about the further control flow. We know that K and S are not the same. We can use them as two different constants and implement data structures using them. If we want to pattern match, we can simply put M before the expression and we can safely assume that the result is either K or S. It may happen, of course, that the expression itself doesn't reduce to K or S, in which case M doesn't reduce either, so we can't have an unexpected result.

In SKI, someone can "trick" a function by supplying a precisely crafted argument to force it spit out something bizzare. For example, in lambda calculus, we can implement ordered pair as pair = λ a b c, c a b. We can use P = pair a b to construct a pair of a and b and then use P (λ a b, a) to access the first element and P (λ a b, b) to access the second element. However, P (λ a b, x) returns x, even if x is neither a or b. Proving things about P would be very hard. We would need types and it complicates things a lot (even if we use simple types instead of dependent types).

In SKM, ordered pair can be implemented as pair = λ a b c, cases c a b (we use lambda expression for simplicity, but any lambda expression can be converted to SKM expression). Here cases is a function that takes three arguments and if the first argument reduces to K then returns the second argument, while if the first argument reduces to S then returns the third argument. Otherwise, it returns M x where x is any expression that does not reduce to K or S. We leave it as a challenge for the reader to implement cases using SKM combinators.

With this implementation of pair in SKM calculus, we can safely assume that pair a b c returns either a or b. If c is some junk value (does not reduce to K or S), then pair a b c returns M something where something is an expression that does not reduce to K or S. We treat M something like "the bottom" in Haskell. So, technically, the result is either a, or b, or bottom. This way, we can perform safe pattern matches and be able to make a theorem prover whithout having to deal with types.