Python feature request : operator for function composition

Discussion in 'Python' started by Kay Schluehr, Feb 3, 2008.

  1. Kay Schluehr

    Kay Schluehr Guest

    As you know, there is no operator for function composition in Python.
    When you have two functions F and G and want to express the
    composition F o G you have to create a new closure

    lambda *args, **kwd: F (G (*args, **kwd))

    or you write a composition in functional style

    compose( F, G )

    None of these solutions is particular terse.

    Proposal
    -------------
    I want to propose overloading the logical __and__ operator i.e. '&'
    for functions s.t. F & G means "compose(F,G)". This suggestion is non-
    conflicting since & is not used as an operator in combination with
    function objects yet: given a function object F and an arbitrary
    object X the combination F & X raises a TypeError when evaluated.

    Alternatives
    -----------------
    One could use other unused operators like F << G or F * G to write
    terse function composition expressions. I' m not sure which one is
    best and would use the proposed & if no one presents arguments against
    it.
     
    Kay Schluehr, Feb 3, 2008
    #1
    1. Advertising

  2. On Feb 3, 5:09 am, Kay Schluehr <> wrote:
    > As you know, there is no operator for function composition in Python.
    > When you have two functions F and G and  want to express the
    > composition F o G you have to create a new closure
    >
    > lambda *args, **kwd: F (G (*args, **kwd))
    >
    > or you write a composition in functional style
    >
    > compose( F, G )
    >
    > None of these solutions is particular terse.
    >
    > Proposal
    > -------------
    > I want to propose overloading the logical __and__ operator i.e. '&'
    > for functions s.t. F & G means "compose(F,G)". This suggestion is non-
    > conflicting since & is not used as an operator in combination with
    > function objects yet: given a function object F and an arbitrary
    > object X the combination F & X raises a TypeError when evaluated.
    >
    > Alternatives
    > -----------------
    > One could use other unused operators like F << G or F * G to write
    > terse function composition expressions. I' m not sure which one is
    > best and would use the proposed & if no one presents arguments against
    > it.


    What about other callable objects?

    --
    Arnaud
     
    Arnaud Delobelle, Feb 3, 2008
    #2
    1. Advertising

  3. Kay Schluehr

    Kay Schluehr Guest

    On 3 Feb., 10:13, Arnaud Delobelle <> wrote:
    > On Feb 3, 5:09 am, Kay Schluehr <> wrote:
    >
    >
    >
    > > As you know, there is no operator for function composition in Python.
    > > When you have two functions F and G and want to express the
    > > composition F o G you have to create a new closure

    >
    > > lambda *args, **kwd: F (G (*args, **kwd))

    >
    > > or you write a composition in functional style

    >
    > > compose( F, G )

    >
    > > None of these solutions is particular terse.

    >
    > > Proposal
    > > -------------
    > > I want to propose overloading the logical __and__ operator i.e. '&'
    > > for functions s.t. F & G means "compose(F,G)". This suggestion is non-
    > > conflicting since & is not used as an operator in combination with
    > > function objects yet: given a function object F and an arbitrary
    > > object X the combination F & X raises a TypeError when evaluated.

    >
    > > Alternatives
    > > -----------------
    > > One could use other unused operators like F << G or F * G to write
    > > terse function composition expressions. I' m not sure which one is
    > > best and would use the proposed & if no one presents arguments against
    > > it.

    >
    > What about other callable objects?
    >
    > --
    > Arnaud


    Supporting general callables would be very fine. I thing a callable
    ABC with two protocols named __call__ and __compose__ would be most
    adequate. I'm just not sure if Python 2.X requires a more ad hoc
    implementation for builtin callable types? On the level of user
    defined classes a bundling between __call__ and __compose__ shall
    remain optional ( unlike e.g. the dependency between __iter__ and
    __next__ for iterables ).
     
    Kay Schluehr, Feb 3, 2008
    #3
  4. On Feb 3, 9:43 am, Kay Schluehr <> wrote:
    > On 3 Feb., 10:13, Arnaud Delobelle <> wrote:
    >
    >
    >
    > > On Feb 3, 5:09 am, Kay Schluehr <> wrote:

    >
    > > > As you know, there is no operator for function composition in Python.
    > > > When you have two functions F and G and  want to express the
    > > > composition F o G you have to create a new closure

    >
    > > > lambda *args, **kwd: F (G (*args, **kwd))

    >
    > > > or you write a composition in functional style

    >
    > > > compose( F, G )

    >
    > > > None of these solutions is particular terse.

    >
    > > > Proposal
    > > > -------------
    > > > I want to propose overloading the logical __and__ operator i.e. '&'
    > > > for functions s.t. F & G means "compose(F,G)". This suggestion is non-
    > > > conflicting since & is not used as an operator in combination with
    > > > function objects yet: given a function object F and an arbitrary
    > > > object X the combination F & X raises a TypeError when evaluated.

    >
    > > > Alternatives
    > > > -----------------
    > > > One could use other unused operators like F << G or F * G to write
    > > > terse function composition expressions. I' m not sure which one is
    > > > best and would use the proposed & if no one presents arguments against
    > > > it.

    >
    > > What about other callable objects?

    >
    > > --
    > > Arnaud

    >
    > Supporting general callables would be very fine. I thing a callable
    > ABC with two protocols named __call__ and __compose__ would be most
    > adequate. I'm just not sure if Python 2.X requires a more ad hoc
    > implementation for builtin callable types? On the level of user
    > defined classes a bundling between __call__ and __compose__ shall
    > remain optional ( unlike e.g. the dependency between __iter__ and
    > __next__ for iterables ).


    This would conflict with the fact that the operators you suggest for
    composition can already be overloaded for other purposes.

    --
    Arnaud
     
    Arnaud Delobelle, Feb 3, 2008
    #4
  5. Kay Schluehr

    Kay Schluehr Guest

    On 3 Feb., 10:55, Arnaud Delobelle <> wrote:
    > On Feb 3, 9:43 am, Kay Schluehr <> wrote:
    >
    >
    >
    > > On 3 Feb., 10:13, Arnaud Delobelle <> wrote:

    >
    > > > On Feb 3, 5:09 am, Kay Schluehr <> wrote:

    >
    > > > > As you know, there is no operator for function composition in Python.
    > > > > When you have two functions F and G and want to express the
    > > > > composition F o G you have to create a new closure

    >
    > > > > lambda *args, **kwd: F (G (*args, **kwd))

    >
    > > > > or you write a composition in functional style

    >
    > > > > compose( F, G )

    >
    > > > > None of these solutions is particular terse.

    >
    > > > > Proposal
    > > > > -------------
    > > > > I want to propose overloading the logical __and__ operator i.e. '&'
    > > > > for functions s.t. F & G means "compose(F,G)". This suggestion is non-
    > > > > conflicting since & is not used as an operator in combination with
    > > > > function objects yet: given a function object F and an arbitrary
    > > > > object X the combination F & X raises a TypeError when evaluated.

    >
    > > > > Alternatives
    > > > > -----------------
    > > > > One could use other unused operators like F << G or F * G to write
    > > > > terse function composition expressions. I' m not sure which one is
    > > > > best and would use the proposed & if no one presents arguments against
    > > > > it.

    >
    > > > What about other callable objects?

    >
    > > > --
    > > > Arnaud

    >
    > > Supporting general callables would be very fine. I thing a callable
    > > ABC with two protocols named __call__ and __compose__ would be most
    > > adequate. I'm just not sure if Python 2.X requires a more ad hoc
    > > implementation for builtin callable types? On the level of user
    > > defined classes a bundling between __call__ and __compose__ shall
    > > remain optional ( unlike e.g. the dependency between __iter__ and
    > > __next__ for iterables ).

    >
    > This would conflict with the fact that the operators you suggest for
    > composition can already be overloaded for other purposes.
    >
    > --
    > Arnaud


    True. Extending a callable ABC by __add__ shall be sufficient and does
    not cause ambiguities, Just one tiny teardrop for __add__ not being
    very telling. Otherwise the intended semantics might be constrained to
    builtin function/method/... types and classes that derive from a
    callable ABCs. This is sufficient for covering the intended purpose of
    function composition and does not exclude close relatives ( methods,
    function-like objects ). How someone assigns semantics to a general
    __call__ on his own classes is up to him.
     
    Kay Schluehr, Feb 3, 2008
    #5
  6. On Feb 3, 12:09 am, Kay Schluehr <> wrote:

    > As you know, there is no operator for function composition in Python.
    > When you have two functions F and G and want to express the
    > composition F o G you have to create a new closure
    >
    > lambda *args, **kwd: F (G (*args, **kwd))
    >
    > or you write a composition in functional style
    >
    > compose( F, G )
    >
    > None of these solutions is particular terse.


    What if F takes more than one (positional and/or keyword) arguments?
    How common is this special use case where F takes a single argument
    (the result of G) to deserve a special operator ?

    George
     
    George Sakkis, Feb 3, 2008
    #6
  7. Kay Schluehr

    Kay Schluehr Guest

    On Feb 3, 11:34 pm, George Sakkis <> wrote:
    > On Feb 3, 12:09 am, Kay Schluehr <> wrote:
    >
    > > As you know, there is no operator for function composition in Python.
    > > When you have two functions F and G and want to express the
    > > composition F o G you have to create a new closure

    >
    > > lambda *args, **kwd: F (G (*args, **kwd))

    >
    > > or you write a composition in functional style

    >
    > > compose( F, G )

    >
    > > None of these solutions is particular terse.

    >
    > What if F takes more than one (positional and/or keyword) arguments?
    > How common is this special use case where F takes a single argument
    > (the result of G) to deserve a special operator ?
    >
    > George


    O.K. Point taken. Here is a more general form of compose that is
    applicable when both F and G take *args and **kwd arguments.

    def compose(F,G):
    def prepare_args(args, kwd = {}):
    return args if isinstance(args, tuple) else (args,), kwd
    def apply_composition(*args, **kwd):
    nargs, nkwd = prepare_args(G(*args, **kwd))
    return F(*nargs, **nkwd)
    return apply_composition
     
    Kay Schluehr, Feb 3, 2008
    #7
  8. Kay Schluehr

    Dustan Guest

    On Feb 2, 11:09 pm, Kay Schluehr <> wrote:
    [snip]

    While you're waiting for it to be implemented, you can build your own
    version as a decorator. Here's an example written in haste:

    >>> class composer(object):

    def __init__(self, *funcs):
    self.funcs = funcs
    def __and__(self, other):
    if isinstance(other, composer):
    return composer(*(self.funcs+other.funcs))
    else:
    return composer(*(self.funcs+(other,)))
    def __call__(self, *args, **kargs):
    for func in reversed(self.funcs):
    args = (func(*args, **kargs),)
    if kargs:
    kargs = {}
    return args[0]


    >>> @composer

    def double(x):
    return 2*x

    >>> @composer

    def square(x):
    return x*x

    >>> double_square = double & square
    >>> square_double = square & double
    >>> double_square(2)

    8
    >>> square_double(2)

    16
    >>> double_square(3)

    18
    >>> square_double(3)

    36
    >>> double_square(4)

    32
    >>> square_double(4)

    64

    Probably not the best implementation, but you get the idea.
     
    Dustan, Feb 4, 2008
    #8
  9. On Feb 4, 3:00 pm, Dustan <> wrote:
    > On Feb 2, 11:09 pm, Kay Schluehr <> wrote:
    > [snip]
    >
    > While you're waiting for it to be implemented, you can build your own
    > version as a decorator. Here's an example written in haste:
    >
    > >>> class composer(object):

    >
    > def __init__(self, *funcs):
    > self.funcs = funcs
    > def __and__(self, other):
    > if isinstance(other, composer):
    > return composer(*(self.funcs+other.funcs))
    > else:
    > return composer(*(self.funcs+(other,)))
    > def __call__(self, *args, **kargs):
    > for func in reversed(self.funcs):
    > args = (func(*args, **kargs),)
    > if kargs:
    > kargs = {}
    > return args[0]
    >
    > >>> @composer

    >
    > def double(x):
    > return 2*x
    >
    > >>> @composer

    >
    > def square(x):
    > return x*x
    >
    > >>> double_square = double & square
    > >>> square_double = square & double
    > >>> double_square(2)

    > 8
    > >>> square_double(2)

    > 16
    > >>> double_square(3)

    > 18
    > >>> square_double(3)

    > 36
    > >>> double_square(4)

    > 32
    > >>> square_double(4)

    >
    > 64
    >
    > Probably not the best implementation, but you get the idea.


    This is nice.
    * I wouldn't choose '&' as the composing operator as when I read
    'double & square' I think 'take an x, double it & square it' which is
    the wrong interpretation (perhaps << instead?).
    * I would call the decorator 'composable'.

    --
    Arnaud
     
    Arnaud Delobelle, Feb 4, 2008
    #9
  10. Kay Schluehr

    Dustan Guest

    On Feb 4, 10:11 am, Arnaud Delobelle <> wrote:
    > This is nice.


    Thanks.

    > * I wouldn't choose '&' as the composing operator as when I read
    > 'double & square' I think 'take an x, double it & square it' which is
    > the wrong interpretation (perhaps << instead?).


    A very good point that I didn't think about; I just blindly took the
    OP's chosen operator. Another thing I realized after writing this was
    that I could have also written a corresponding __rand__ method
    (__rlshift__ with your alternative operator), in case the object to
    the right of the operator is a composer object and to the left is a
    simple function.

    > * I would call the decorator 'composable'.


    The thing about that, though, is that this can also be used as a
    composition in function style. However, I can't think of any name that
    encompasses both uses. And you're right in that composer wasn't a very
    good choice of name. As I say, it was written in haste.
     
    Dustan, Feb 4, 2008
    #10
  11. Kay Schluehr

    Kay Schluehr Guest

    This won't work for builtin functions. It hardly works for functions
    and methods defined in 3rd party modules and in no way for functions
    defined in C extensions. It adds boilerplate statically to remove it
    at runtime.
     
    Kay Schluehr, Feb 4, 2008
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Peng Yu
    Replies:
    1
    Views:
    207
    Paul McGuire
    Jul 17, 2010
  2. Tim Chase
    Replies:
    7
    Views:
    212
    Emile van Sebille
    Jul 17, 2010
  3. Matthew Gilson

    Feature Request: `operator.not_in`

    Matthew Gilson, Apr 19, 2013, in forum: Python
    Replies:
    0
    Views:
    102
    Matthew Gilson
    Apr 19, 2013
  4. Terry Jan Reedy

    Re: Feature Request: `operator.not_in`

    Terry Jan Reedy, Apr 19, 2013, in forum: Python
    Replies:
    0
    Views:
    128
    Terry Jan Reedy
    Apr 19, 2013
  5. Matthew Gilson

    Re: Feature Request: `operator.not_in`

    Matthew Gilson, Apr 19, 2013, in forum: Python
    Replies:
    0
    Views:
    119
    Matthew Gilson
    Apr 19, 2013
Loading...

Share This Page