Why functional Python matters

David Mertz mertz at gnosis.cx
Tue Apr 15 16:55:33 EDT 2003


|> However, losing apply() isn't a big deal because there is
|> an exactly equivalent construct.

Not quite.  For Appendix A of my book, I discuss one (realistic) example
where extended call syntax and apply() are not directly substitutable:

  In most cases, the extended call syntax is more readable, since
  the call closely resembles the -declaration- syntax of generic
  positional and keyword arguments.  But in a few
  cases--particularly in higher-order functions--the older
  `apply()` built-in function is still useful.  For example,
  suppose that you have an application that will either perform
  an action immediately or defer it for later, depending on some
  condition.  You might program this application as:

      #*----------- apply() as first-class function -----------#
      defer_list = []
      if some_runtime_condition():
          doIt = apply
      else:
          doIt = lambda *x: defer_list.append(x)
      #...do stuff like read records and options...
      doIt(operation, args, keywds)
      #...do more stuff...
      #...carry out deferred actions...
      map(lambda (f,args,kw): f(*args,**kw), defer_list)

  Since `apply()` is itself a first-class function rather than a
  syntactic form, you can pass it around--or in the example,
  bind it to a name.

Of course, you could define your own apply() in one line of code if the
function were not built-in.

|..."itertools" module....
|I think it'd be good to have a module called "functional" where the
|more unusual functional tools can live

I definitely agree with this idea.  A [functional] module in the
standard library would be GREAT.  Xoltar had a number of useful ideas,
although other people have proposed different implementations of some of
them... so the best version would need to be decided.

But another collection of functions that I would REALLY like to see in
such a hypothetical standard library would be a good set of Higher Order
Functions (HOFs).  My Gnosis Utilities contains gnosis.utils.combinators,
which has at least the ideas for the most useful few.  The
implementations are overly concise and under documented though.  But
it's short enough to list here:

  from operator import mul, add, truth
  apply_each = lambda fns, args=[]: map(apply, fns, [args]*len(fns))
  bools = lambda lst: map(truth, lst)
  bool_each = lambda fns, args=[]: bools(apply_each(fns, args))
  conjoin = lambda fns, args=[]: reduce(mul, bool_each(fns, args))
  all = lambda fns: lambda arg, fns=fns: conjoin(fns, (arg,))
  both = lambda f,g: all((f,g))
  all3 = lambda f,g,h: all((f,g,h))
  and_ = lambda f,g: lambda x, f=f, g=g: f(x) and g(x)
  disjoin = lambda fns, args=[]: reduce(add, bool_each(fns, args))
  some = lambda fns: lambda arg, fns=fns: disjoin(fns, (arg,))
  either = lambda f,g: some((f,g))
  anyof3 = lambda f,g,h: some((f,g,h))
  or_ = lambda f,g: lambda x, f=f, g=g: f(x) or g(x)
  compose = lambda f,g: lambda x, f=f, g=g: f(g(x))
  compose3 = lambda f,g,h: lambda x, f=f, g=g, h=h: f(g(h(x)))
  ident = lambda x: x
  not_ = lambda f: lambda x, f=f: not f(x)

  def shortcut_all(*fns):
      accum = fns[0]
      for fn in fns[1:]:
          accum = and_(accum, fn)
      return accum
  lazy_all = shortcut_all

  def shortcut_any(*fns):
      accum = fns[0]
      for fn in fns[1:]:
          accum = or_(accum, fn)
      return accum
  lazy_any = shortcut_any


Yours, David...

--
Keeping medicines from the bloodstreams of the sick; food from the bellies
of the hungry; books from the hands of the uneducated; technology from the
underdeveloped; and putting advocates of freedom in prisons.  Intellectual
property is to the 21st century what the slave trade was to the 16th.





More information about the Python-list mailing list