DRY functional programming: how to avoid the same line of code in different higher order functions

Viewed 93

Imagine we have the following setup (let it be written in Python, the language is not essential though):

def f1_provider(vars, degree):
    dim1, dim2, dim3 = set_dims(vars, degree)
    def f1(*args1):
        # ...
        # use vars and dims
        # ...
    return f1

def f2_provider(vars, degree):
    dim1, dim2, dim3 = set_dims(vars, degree)
    def f2(*args2):
        # ...
        # use vars and dims
        # ...
    return f2

def f3_provider(vars, degree):
    dim1, dim2, dim3 = set_dims(vars, degree)
    def f3(*args3):
        # ...
        # use vars and dims
        # ...
    return f3

# then use them somewhere:

def f1_user():
    # ...
    f1 = f1_provider(vars1, degree1)
    # ...
def f2_f3_user():
    # ...
    f2 = f2_provider(vars2, degree2)
    f3 = f3_provider(vars2, degree2)
    # ...

As you can see, this design is not so good at least because:

  • The dim1, dim2, dim3 = set_dims(vars, degree) line is being repeated over and over again. I might say the same about signatures of these provider methods - but let's not be so captious, I can bear with that at least :).
  • The set_dims function is applied one extra time: it's result is already known during the f2_provider(vars2, degree2) call. However, it runs once again during the third provider's call.

How can this be reworked in a functional way?

If we were in OOP one should have introduced a class like GeneralProvider, put the set_dims call in its constructor and made these providers public methods. Nope, not so interesting :).

2 Answers

I think this is a workaround, though it's quite ugly. But it should work, in theory). I'll still be waiting for better solutions!

def f1_provider(vars_, dim1, dim2, dim3):
    def f1(*args1):
        # ...
        # use vars_ and dims
        # ...
    return f1

def f2_provider(vars_, dim1, dim2, dim3):
    def f2(*args2):
        # ...
        # use vars_ and dims
        # ...
    return f2

def f3_provider(vars_, dim1, dim2, dim3):
    def f3(*args3):
        # ...
        # use vars_ and dims
        # ...
    return f3


def general_provider(providers, vars_, degree):
    dim1, dim2, dim3 = set_dims(vars_, degree)
    return [f_prov(vars_, dim1, dim2, dim3) for f_prov in providers]

# then use them somewhere:

def f1_user():
    # ...
    f1 = general_provider((f1_provider,), vars1, degree1)
    # ...
def f2_f3_user():
    # ...
    f2, f3 = general_provider((f2_provider, f3_provider), vars2, degree2)
    # ...

Is this appropriate solution for you?

def f1_provider(vars, degree, *dims):
    def f1(*args1):
        # ...
        # use vars and dims
        # ...
    return f1

def f2_provider(vars, degree, *dims):
    def f2(*args2):
        # ...
        # use vars and dims
        # ...
    return f2

def f3_provider(vars, degree, *dims):
    def f3(*args3):
        # ...
        # use vars and dims
        # ...
    return f3

# then use them somewhere:

def f1_user():
    # ...
    dim1, dim2, dim3 = set_dims(vars1, degree1)
    f1 = f1_provider(vars1, degree1, dim1, dim2, dim3)
    # ...
def f2_f3_user():
    # ...
    dim1, dim2, dim3 = set_dims(vars2, degree2)
    f2 = f2_provider(vars2, degree2, dim1, dim2, dim3)
    f3 = f3_provider(vars2, degree2, dim1, dim2, dim3)
    # ...
Related