Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function Composition #32

Open
creese opened this issue Jun 23, 2013 · 5 comments
Open

Function Composition #32

creese opened this issue Jun 23, 2013 · 5 comments

Comments

@creese
Copy link

creese commented Jun 23, 2013

Often, when I chain functions, I find myself using the following pattern:

(F() >> foo >> bar >> baz)(x)

If this were clojure, I could write:

(-> x foo bar baz)

Notice the input on the left. Is there any to way to do this in python/fn?

@kachayev
Copy link
Owner

Unfortunately, -> is invalid variable name in python, so you can't do anything that looks like clojure thread macros -> and ->>. By the way, it's easy to read only in s-expression notation. But definitely, we can have something like

F(x) >> foo >> bar >> baz

F is not good name for such use case, pipe is more appropriate, I think.

pipe(x) >> foo >> bar >> baz

The only one problem that I see, is that python is strictly evaluated and we need to decide on each reducing step what to do: keep going with new functions or evaluate final result. Possible, we need something like "sentinel" function to execute whole pipe:

pipe(x) >> foo >> bar >> baz >> end

or

pipe(x) >> foo >> bar >> baz >> result

or even

pipe.start(x) >> foo >> bar >> baz >> pipe.end

What do you think about such syntax? Can you give a shining example where it's useful?

@creese
Copy link
Author

creese commented Jul 15, 2013

A lot of my methods tend to look like:

def func(x):
    f = F() >> foo >> bar >> baz
    return f(x)

Lately, I've begun to work with just functions. It feels more Pythonic.

def func(x):
    f = compose(foo, bar, baz)
    return f(x)

or even,

def func(x):
    return thread_last(x, foo, bar, baz)

@microamp
Copy link

creese, where is the above compose function from?

By the way, how is this little sugar for pipe/compose? (I will call it pipe for now.)

from fn.func import F
from fn.iters import first, rest

def pipe(*funcs):
    def _pipe(*args, **kwargs):
        return reduce(lambda f, g: f >> g,
                      rest(funcs),
                      F(first(funcs), *args, **kwargs))()

    return _pipe

Some examples to follow

from functools import partial
from operator import add, mul

inc = partial(add, 1)
double = partial(mul, 2)
triple = partial(mul, 3)

double_triple_inc = pipe(double, triple, inc)
inc_double_triple = pipe(inc, double, triple)

assert double_triple_inc(2) == 13  # 2 * 2 * 3 + 1 = 13
assert double_triple_inc(3) == 19  # 3 * 2 * 3 + 1 = 19
assert inc_double_triple(3) == 24  # (3 + 1) * 2 * 3 = 24

and

from functools import partial
from operator import mul, neg
from random import random

rndm = pipe(random, partial(mul, 10), int, neg)
print([rndm() for i in range(10)])  # some random negative integers

Any thoughts?

EDIT: Fix neg missing from the import statement.

@venuatu
Copy link

venuatu commented Sep 7, 2014

Earlier today I had the crazy idea to do something like this by abusing some operators (or and invert):

mod_primes = ~(pipe(primes)
    | filter(lambda a: a > 20)
    | map(lambda a: a * a)
    | zip_index
)

A simple implementation is at: https://gist.github.com/venuatu/0b35e331aeea9f3feb8f

Would this be a good addition for this library?

@iddan
Copy link

iddan commented Sep 10, 2017

Your implementation refers to lists but it actually can work for any type, does'nt it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants