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
