from typing import Tuple def square(num: int) -> int: return num * num def double(num: int) -> int: return num + num def square_with_print_return(num: int) -> Tuple[int, str]: logs = "Currrent num " + str(num) + ". " return (square(num), logs) def double_with_print_return(num: int) -> Tuple[int, str]: logs = "Currrent num " + str(num) + ". " return (double(num), logs) def bind(func, tuple: Tuple[int, str]) -> Tuple[int, str]: res = func(tuple[0]) return (res[0], tuple[1] + res[1]) def unit(number: int) -> Tuple[int, str]: return (number, "") # Now you can do this, more or less nested: #print(bind(square_with_print_return, unit(5))) #print(bind(square_with_print_return, (bind(square_with_print_return, bind(square_with_print_return,bind(square_with_print_return,unit(5))))))) # it's nicer with infix. This class is a hack. class Infix: def __init__(self, function): self.function = function def __ror__(self, other): return Infix(lambda x, self=self, other=other: self.function(other, x)) def __or__(self, other): return self.function(other) def __rlshift__(self, other): return Infix(lambda x, self=self, other=other: self.function(other, x)) def __rshift__(self, other): return self.function(other) def __call__(self, value1, value2): return self.function(value1, value2) x=Infix(lambda f,x: bind(f,x)) print( square_with_print_return |x| (double_with_print_return |x| unit(4) ) ) # And some extra functional stuff def curry(f,x): def curried_function(*args, **kw): return f(*((x,)+args),**kw) return curried_function curry=Infix(curry) import operator add5 = operator.add |curry| 5 print(add5(6)) def add5_with_print_return(num: int) -> Tuple[int,str]: logs = "Current num is " + str(num) + ". " return (5+num,logs) print( add5_with_print_return |x| (double_with_print_return |x| unit(4) ) )