Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Python feature request : operator for function composition

Reply
Thread Tools

Python feature request : operator for function composition

 
 
Kay Schluehr
Guest
Posts: n/a
 
      02-03-2008
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.
 
Reply With Quote
 
 
 
 
Arnaud Delobelle
Guest
Posts: n/a
 
      02-03-2008
On Feb 3, 5:09*am, Kay Schluehr <(E-Mail Removed)> 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

 
Reply With Quote
 
 
 
 
Kay Schluehr
Guest
Posts: n/a
 
      02-03-2008
On 3 Feb., 10:13, Arnaud Delobelle <(E-Mail Removed)> wrote:
> On Feb 3, 5:09 am, Kay Schluehr <(E-Mail Removed)> 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 ).
 
Reply With Quote
 
Arnaud Delobelle
Guest
Posts: n/a
 
      02-03-2008
On Feb 3, 9:43*am, Kay Schluehr <(E-Mail Removed)> wrote:
> On 3 Feb., 10:13, Arnaud Delobelle <(E-Mail Removed)> wrote:
>
>
>
> > On Feb 3, 5:09 am, Kay Schluehr <(E-Mail Removed)> 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

 
Reply With Quote
 
Kay Schluehr
Guest
Posts: n/a
 
      02-03-2008
On 3 Feb., 10:55, Arnaud Delobelle <(E-Mail Removed)> wrote:
> On Feb 3, 9:43 am, Kay Schluehr <(E-Mail Removed)> wrote:
>
>
>
> > On 3 Feb., 10:13, Arnaud Delobelle <(E-Mail Removed)> wrote:

>
> > > On Feb 3, 5:09 am, Kay Schluehr <(E-Mail Removed)> 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.

 
Reply With Quote
 
George Sakkis
Guest
Posts: n/a
 
      02-03-2008
On Feb 3, 12:09 am, Kay Schluehr <(E-Mail Removed)> 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
 
Reply With Quote
 
Kay Schluehr
Guest
Posts: n/a
 
      02-03-2008
On Feb 3, 11:34 pm, George Sakkis <(E-Mail Removed)> wrote:
> On Feb 3, 12:09 am, Kay Schluehr <(E-Mail Removed)> 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





 
Reply With Quote
 
Dustan
Guest
Posts: n/a
 
      02-04-2008
On Feb 2, 11:09 pm, Kay Schluehr <(E-Mail Removed)> 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.
 
Reply With Quote
 
Arnaud Delobelle
Guest
Posts: n/a
 
      02-04-2008
On Feb 4, 3:00 pm, Dustan <(E-Mail Removed)> wrote:
> On Feb 2, 11:09 pm, Kay Schluehr <(E-Mail Removed)> 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
 
Reply With Quote
 
Dustan
Guest
Posts: n/a
 
      02-04-2008
On Feb 4, 10:11 am, Arnaud Delobelle <(E-Mail Removed)> 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.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: Is '[' a function or an operator or an language feature? Tim Chase Python 7 07-17-2010 06:37 PM
Is '[' a function or an operator or an language feature? Peng Yu Python 1 07-17-2010 01:57 PM
An idea for fast function composition Arnaud Delobelle Python 4 02-17-2008 12:07 AM
Pythonic function composition Alan G Isaac Python 6 10-25-2004 08:13 PM
Defining a new function by composition Edgardo Hames Ruby 7 08-07-2004 09:31 AM



Advertisments