In Python, you can write all the code you want that relates to what does not exist; To be specific, you can write code that refers to names that have no values attached to them. And you can compile this code. The only problem will occur at runtime if the names still don't have values attached to them.
Here is an example of code that you can run tested under Python 2 and Python 3.
def my_func(a, b): return foo(a) + bar(b) try: my_func(1, 2) except NameError: print("didn't work")
If you don't want the names to be just anchored in the local namespace, but you want to be able to fine-tune them for each function, I think the best practice in Python would be to use named arguments. You can always close function arguments and return a new function object as follows:
def my_func_factory(foo, bar): def my_func(a, b): return foo(a) + bar(b) return my_func my_func0 = my_func_factory(lambda x: 2*x, lambda x:2*x) print(my_func0(1, 2))
EDIT: Here is your example modified using the above idea.
def domonad(monad, *cmf): def m_chain(fns, bind=monad['bind'], unit=monad['unit']): """what this function does is not relevant to the question""" def m_chain_link(chain_expr, step): return lambda v: bind(chain_expr(v), step) return reduce(m_chain_link, fns, unit) return m_chain(cmf) identity_m = { 'bind':lambda v,f:f(v), 'unit':lambda v:v } maybe_m = { 'bind':lambda v,f:f(v) if v else None, 'unit':lambda v:v } print(domonad(identity_m, lambda x: 2*x, lambda x:2*x)(2))
Please let me know how this works for you.
EDIT: Alright, another version after your comment. You can write arbitrary m_ functions following this pattern: they check kwargs for the "monad" key. This should be specified as a named argument; There is no way to pass it as a positional argument because of the *fns argument, which collects all the arguments into a list. I provided default values for bind() and unit() if they are not defined in the monad, or the monad is not provided; they probably don't do what you want, so replace them with something better.
def m_chain(*fns, **kwargs): """what this function does is not relevant to the question""" def bind(v, f): # default bind if not in monad return f(v), def unit(v): # default unit if not in monad return v if "monad" in kwargs: monad = kwargs["monad"] bind = monad.get("bind", bind) unit = monad.get("unit", unit) def m_chain_link(chain_expr, step): return lambda v: bind(chain_expr(v), step) return reduce(m_chain_link, fns, unit) def domonad(fn, *fns, **kwargs): return fn(*fns, **kwargs) identity_m = { 'bind':lambda v,f:f(v), 'unit':lambda v:v } maybe_m = { 'bind':lambda v,f:f(v) if v else None, 'unit':lambda v:v } print(domonad(m_chain, lambda x: 2*x, lambda x:2*x, monad=identity_m)(2)) print(domonad(m_chain, lambda x: None, lambda x:2*x, monad=maybe_m)(2))