Unification of Methods and Functions

Antoon Pardon apardon at forel.vub.ac.be
Wed May 26 04:16:08 EDT 2004


Op 2004-05-25, Antoon Pardon schreef <apardon at forel.vub.ac.be>:
> Op 2004-05-25, David MacQuigg schreef <dmq at gain.com>:
>> On 25 May 2004 10:30:03 GMT, Antoon Pardon <apardon at forel.vub.ac.be>
>> wrote:
>>

> 3) Higher order functions. These are functions that
>    whose arguments and/or return values can be again
>    functions.
>
> The typical example is a sorting function that takes
> as an argument a function that decides which of two
> elements is the smaller. By providing different
> functions (one that looks at names or one that
> looks at Id number) the same function can be used
> to sort people alphabetical or Numerical by Id.
>
> Now to go a step further. Suppose we have a lot
> of functions Fi: Each of these functions is
> defined as follows Fi(x) = i + x. Now we
> can have an other function S. S is defined as
> follows: S(a) = Fa. Now we can use S to add
> two numbers. Suppose we want to add 2 and 5.
> First we call S(2), this will result in F2.
> Then we call F2(5) which will result in 7.
> Or more directly we could call S(2)(5).
>
> Now python allows to define higher order functions.
> S would be defined as follows in python:
>
> def S(i):
>
>   def Fi(x):
>
>     return i + x
>
>   return Fi
>

Now any normal function can be turned into a higher
order function with a related functionality. Supose
we have a function with the following signature in
python

  def func(a1, a2, a3):


We can then write the higher order function as
follows:

  def Hfunc(a1):

    def Ifunc(a2, a3):

      return func(a1, a2, a3)

    return Ifunc


Now transforming a function to a higher order
equivallent is called currying and currying is
again a higher order function. In python we 
write it as follows

  def curry(f):

    def Hf(a1):

      def If(*ta):

        la = (a1,) + ta
        return apply(f, la)

      return If

    return Hf


So from now on if we have a function we can turn it
into its curried version as follows:

  Hfunc = curry(func)


So now we have all we need for object oriented programming.
To illustrate I'll make a stack pseudo class. We start
with the assumption that we have the following class.

  class Bare:
    pass


First approach


  def Stack():

    st = Bare()
    st.lst = []
    return st


  def Empty(st):

    return len(st.lst) == 0


  def Pop(st):

    result = st.lst[-1]
    del st.lst[-1]
    return result


  def Push(st, el):

    st.lst.append(el)



  stack = Stack()
  Push(stack, 4)
  Push(stack, 'hallo')
  El = Pop(stack)


This is a strickt procedural approach. It has the disadvantages that the
procedures Empty, Pop and Push aren't very tied to the stack and that
we therefore prohibit other structures the use of the same name


We can eliminate these disadvantages as follows:


Second approach


  def Stack():

    def Empty(st):
  
      return len(st.lst) == 0
  
  
    def Pop(st):
  
      result = st.lst[-1]
      del st.lst[-1]
      return result
  
  
    def Push(st, el):
  
      st.lst.append(el)


    st = Bare()
    st.lst = []
    st.Empty = Empty
    st.Pop = Pop
    st.Push = Push
    return st


  stack = Stack()
  stack.Push(stack, 4)
  stack.Push(stack, 'hallo')
  El = stack.Pop(stack)


This looks already somewhat object oriented. The problem here
is that you are essentially obligated to allways provide the
first argument twice. To eliminate this cumbersome repetition
we work with applied curried functions as methods.


Third approach

  def Stack():

    def Empty(st):
  
      return len(st.lst) == 0
  
  
    def Pop(st):
  
      result = st.lst[-1]
      del st.lst[-1]
      return result
  
  
    def Push(st, el):
  
      st.lst.append(el)


    st = Bare()
    st.lst = []
    st.Empty = curry(Empty)(st)
    st.Pop = curry(Pop)(st)
    st.Push = curry(Push)(st)
    return st


  stack = Stack()
  stack.Push(4)
  stack.Push('hallo')
  El = stack.Pop()


And we now have standard python method calls. We will just
make some cosmetic changes here to have it resemble python
class conventions more.


Fourth approach

  def Stack():

    def __init__(self):

      self.lst = []

    def Empty(self):
  
      return len(self.lst) == 0
  
  
    def Pop(self):
  
      result = self.lst[-1]
      del self.lst[-1]
      return result
  
  
    def Push(self, el):
  
      self.lst.append(el)


    self = Bare()
    __init__(self)
    self.Empty = curry(Empty)(self)
    self.Pop = curry(Pop)(self)
    self.Push = curry(Push)(self)
    return self


  stack = Stack()
  stack.Push(4)
  stack.Push('hallo')
  El = stack.Pop()


And this is more or less equivallent to:


  class Stack():

    def __init__(self):

      self.lst = []

    def Empty(self):
  
      return len(self.lst) == 0
  
  
    def Pop(self):
  
      result = self.lst[-1]
      del self.lst[-1]
      return result
  
  
    def Push(self, el):
  
      self.lst.append(el)


  stack = Stack()
  stack.Push(4)
  stack.Push('hallo')
  El = stack.Pop()


-- 
Antoon Pardon



More information about the Python-list mailing list