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