Functional composition in python

Dmitry Groshev lambdadmitry at gmail.com
Sun Aug 29 04:54:13 CEST 2010


On Aug 29, 5:14 am, Steven D'Aprano <st... at REMOVE-THIS-
cybersource.com.au> wrote:
> On Sat, 28 Aug 2010 21:30:39 +0400, Dmitry Groshev wrote:
> > Hello all. Some time ago I wrote a little library:
> >http://github.com/si14/python-functional-composition/, inspired by
> > modern functional languages like F#. In my opinion it is quite useful
> > now, but I would like to discuss it.
> > An example of usage:
>
> > import os
> > from pyfuncomp import composable, c, _
>
> > def comment_cutter(s):
> >     t = s.find("#")
> >     return s if t < 0 else s[0:t].strip()
>
> > @composable #one can use a decorator to make a composable function
> > def empty_tester(x):
> >     return len(x) > 0 and x[0] != "#"
>
> Why do you need a decorator to make a composable function? Surely all
> functions are composable -- it is the nature of functions that you can
> call one function with the output of another function.
>
>
>
> > path_prefix = "test"
>
> > config_parser = (c(open) >>  #or use a transformer function
> >              c(str.strip).map >> #"map" acts like a function modifier
> >              c(comment_cutter).map >>
> >              empty_tester.filter >> #so does "filter"
> >              c(os.path.join)[path_prefix, _].map) #f[a, _, b] is
> > used to make a partial.
> >                                                     #f[a, foo:bar,
> > baz:_] is also correct
>
> > print config_parser("test.txt")
> > print (c("[x ** %s for x in %s]")[2, _] << c(lambda x: x * 2).map)([1,
> > 2, 3])
>
> > Any suggestions are appreciated.
>
> Did you expect us to guess what the above code would do? Without showing
> the output, the above is just line noise.
>
> What does c() do? What does compose() do that ordinary function
> composition doesn't do? You say that "map" acts as a function modifier,
> but don't tell us *what* it modifies or in what way. Same for filter.
>
> So anyone not familiar with C syntax, the use of << is just line noise.
> You need to at say what you're using it for.
>
> --
> Steven

Yep, it's my mistake. I thought this syntax is quite intuitive. Here
is some explanations in code:

@composable
def f1(x):
    return x * 2

@composable
def f2(x):
    return x + 3

@composable
def f3(x):
    return (-1) * x

@composable
def f4(a):
  return a + [0]

@composable
def sqrsum(x, y):
    return x ** 2 + y ** 2

print f1(2) #4
print f2(2) #5
print (f1 << f2 << f1)(2) #14
print (f3 >> f2)(2) #1
print (f2 >> f3)(2) #-5
print (c(float) << f1 << f2)(4) #14.0
print (sqrsum[_, 1] << f1)(2) #17
print (sqrsum[_, _].map)([1, 2, 3, 4, 5]) #[2, 8, 18, 32, 50]
print (c(lambda x: x * 2).map >> c("[x * %s for x in %s]")[3, _])([1,
2, 3]) #[6, 12, 18]

Generally, f1 >> f2 means "lambda x: f2(f1(x))" or "pass the result of
f1 to f2". But in python function can return only one value, so a
composable function should be a function of one argument. So some form
of making partial is needed, and here comes a
f[a, b, _] notation, which means "substitute 3rd argument of f, first
twos are a and b". Finally, we need some form of syntactic sugar for
this: c(map)[c(f),_], so we have a "map" modifier, which transforms
function F to an isomorphism or mapping between lists. For example,
c(lambda x: x * 2).map is equal to lambda x: map(lambda y: y * 2, x).
"Filter" modifier is the same thing for boolean functions.

>What does c() do? What does compose() do that ordinary function
>composition doesn't do?
I need c() or composable() to make an objects with overloaded
operators.

All in all, all this stuff is just a syntactic sugar for nested
functions, maps and filters, which brings a new semantics for old
operators (so one can call it edsl).



More information about the Python-list mailing list