Concatenative Paradigm#

Tacit Programming Approach (Concatenative)

https://www.wikiwand.com/en/Tacit_programming https://www.wikiwand.com/en/Concatenative_programming_language

To achieve this:

  • Functions do not use explicit parameters

  • Branchless

Using Python Standard Library#

Auxiliary Functions#

import operator
import functools
from typing import Callable, TypeVar

T = TypeVar("T")
R = TypeVar("R")

T1 = TypeVar("T1")
T2 = TypeVar("T2")
T3 = TypeVar("T3")


def compose(
    function_outer: Callable[[T1], T2], function_inner: Callable[[T3], T1]
) -> Callable[[T3], T2]:
    def composed_function(*args: T3) -> T2:
        result = function_inner(*args)
        return function_outer(result)

    return composed_function


def thrush(value: T) -> Callable[[Callable[[T], R]], R]:
    def thrush_step(function: Callable[[T], R]) -> R:
        return function(value)

    return thrush_step


def iterate(function: Callable[[T | R], R]) -> Callable[[int], Callable[[T], R | T]]:
    def iterate_step(times: int) -> Callable[[T], R | T]:
        def inner_step(x: R | T) -> R | T:
            return functools.reduce(lambda x, _: function(x) if callable(function) else x, range(times), x)

        return inner_step

    return iterate_step


def curry(
    function: Callable[[T], R]
) -> Callable[[T], Callable[[T], R] | functools.partial[R]]:
    def curry_step(*args: T) -> Callable[[T], R] | functools.partial[R]:
        result = function
        for arg in args:
            result = functools.partial(function, arg)
        return result

    return curry_step


def uncurry(function: Callable[[T], Callable[[T], R] | R]) -> Callable[[T], R]:
    def uncurry_step(*args: T) -> R:
        result = function
        for arg in args:
            result = result(arg)
        return result

    return uncurry_step

Predecessor and Successor#

predecessor = curry(operator.add)(-1)

assert predecessor(1) == 0
assert predecessor(10) == 9
successor = curry(operator.add)(1)

assert successor(0) == 1
assert successor(10) == 11

Addition#

addition = uncurry(iterate(successor))

assert addition(1, 0) == 1
assert addition(0, 0) == 0
assert addition(0, 1) == 1
assert addition(10, 10) == 20

Multiplication#

multiplication = compose(thrush(0), uncurry(compose(iterate, addition)))

assert multiplication(0, 0) == 0
assert multiplication(2, 0) == 0
assert multiplication(0, 2) == 0
assert multiplication(10, 10) == 100

Exponentiation#

exponentiation = compose(thrush(1), uncurry(compose(iterate, curry(multiplication))))

assert exponentiation(1, 0) == 1
assert exponentiation(0, 1) == 0
assert exponentiation(3, 3) == 27

Using a specialized 3rd Party Library: toolz#

Auxiliary Functions#

import toolz as tz
import operator

thrush_tz = tz.flip(tz.apply)

apply_tuple = lambda f: lambda *args: f(args)
unpack_tuple = lambda f: lambda args: f(*args)

Predecessor and Successor#

predecessor_tz = tz.curry(operator.add)(-1)

assert predecessor_tz(1) == 0
assert predecessor_tz(10) == 9
successor_tz = tz.curry(operator.add)(1)

assert successor_tz(0) == 1
assert successor_tz(10) == 11

Addition#

addition_tz = tz.compose(
    unpack_tuple(tz.pipe),
    tz.juxt(
        apply_tuple(tz.compose(tz.curry(tz.iterate)(successor_tz), tz.first)),
        apply_tuple(tz.compose(tz.curry(tz.nth), tz.last)),
    ),
)

assert addition_tz(1, 0) == 1
assert addition_tz(0, 0) == 0
assert addition_tz(0, 1) == 1
assert addition_tz(10, 10) == 20

Multiplication#

# TODO: Refactor to use `tz.curry` instead of `curry` and `tz.iterate` instead of `iterate`
multiplication_tz = tz.compose(
    thrush_tz(0), uncurry(tz.compose(tz.curry(iterate), curry(addition_tz)))
)

assert multiplication_tz(0, 0) == 0
assert multiplication_tz(2, 0) == 0
assert multiplication_tz(0, 2) == 0
assert multiplication_tz(10, 10) == 100

Exponentiation#

# TODO: Refactor to use `tz.curry` instead of `curry` and `tz.iterate` instead of `iterate`
exponentiation_tz = tz.compose(
    thrush_tz(1), uncurry(tz.compose(tz.curry(iterate), curry(multiplication_tz)))
)

assert exponentiation_tz(1, 0) == 1
assert exponentiation_tz(0, 1) == 0
assert exponentiation_tz(3, 3) == 27