#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2012
    Posts
    7
    Rep Power
    0

    Currying in Python


    Hi,
    this is my first post. I've been programming in Python for just 3 days so I don't know much about modules etc... and what they already offer.
    I saw a discussion about Currying or, better, about Python's lack of Curryng so I decided to implement it in Python myself.
    I will post here my code, but you can find more information here:
    http://mtomassoli.wordpress.com/2012/03/18/currying-in-python/
    (sorry, but I'm not allowed to insert active links)

    Any comment is deeply appreciated.

    Here's the code:
    Code:
    def genCur(func, unique = True, minArgs = -1):
        """ Generates a 'curried' version of a function. """
        def g(*myArgs, **myKwArgs):
            def f(*args, **kwArgs):
                if len(args) or len(kwArgs):    # some more args!
                    # Allocates data to assign to the next 'f'.
                    newArgs = myArgs + args
                    newKwArgs = dict.copy(myKwArgs)
    
                    # Adds/updates keyword arguments.
                    if unique:
                        # We don't want repeated keyword arguments.
                        for k in kwArgs.keys():
                            if k in newKwArgs:
                                raise(Exception("Repeated kw arg while unique = True"))
                    newKwArgs.update(kwArgs)
    
                    # Checks whether it's time to evaluate func.
                    if minArgs >= 0 and minArgs <= len(newArgs) + len(newKwArgs):
                        return func(*newArgs, **newKwArgs)  # time to evaluate func
                    else:
                        return g(*newArgs, **newKwArgs)     # returns a new 'f'
                else:                               # the evaluation was forced
                    func(*myArgs, **myKwArgs)
            return f
        return g
    
    def cur(f, minArgs = -1):
        return genCur(f, True, minArgs)
    
    def curr(f, minArgs = -1):
        return genCur(f, False, minArgs)
    
    # Simple Function.
    def func(a, b, c, d, e, f, g = 100):
        print(a, b, c, d, e, f, g)
    
    # NOTE: '<====' means "this line prints to the screen".
    
    # Example 1.
    f = cur(func)                   # f is a "curried" version of func
    c1 = f(1)
    c2 = c1(2, d = 4)               # Note that c is still unbound
    c3 = c2(3)(f = 6)(e = 5)        # now c = 3
    c3()                            # () forces the evaluation              <====
                                    #   it prints "1 2 3 4 5 6 100"
    c4 = c2(30)(f = 60)(e = 50)     # now c = 30
    c4()                            # () forces the evaluation              <====
                                    #   it prints "1 2 30 4 50 60 100"
    
    print("\n------\n")
    
    # Example 2.
    f = curr(func)                  # f is a "curried" version of func
                                    # curr = cur with possibly repeated
                                    #   keyword args
    c1 = f(1, 2)(3, 4)
    c2 = c1(e = 5)(f = 6)(e = 10)() # ops... we repeated 'e' because we     <====
                                    #   changed our mind about it!
                                    #   again, () forces the evaluation
                                    #   it prints "1 2 3 4 10 6 100"
    
    print("\n------\n")
    
    # Example 3.
    f = cur(func, 6)        # forces the evaluation after 6 arguments
    c1 = f(1, 2, 3)         # num args = 3
    c2 = c1(4, f = 6)       # num args = 5
    c3 = c2(5)              # num args = 6 ==> evalution                    <====
                            #   it prints "1 2 3 4 5 6 100"
    c4 = c2(5, g = -1)      # num args = 7 ==> evaluation                   <====
                            #   we can specify more than 6 arguments, but
                            #   6 are enough to force the evaluation
                            #   it prints "1 2 3 4 5 6 -1"
    
    print("\n------\n")
    
    # Example 4.
    def printTree(func, level = -1):
        if level == -1:
            printTree(cur(func), level + 1)
        elif level == 6:
            func(g = '')()      # or just func('')()
        else:
            printTree(func(0), level + 1)
            printTree(func(1), level + 1)
    
    printTree(func)
    
    print("\n------\n")
    
    def f2(*args):
        print(", ".join(["%3d"%(x) for x in args]))
    
    def stress(f, n):
        if n: stress(f(n), n - 1)
        else: f()               # enough is enough
    
    stress(cur(f2), 100)
  2. #2
  3. Banned ;)
    Devshed Supreme Being (6500+ posts)

    Join Date
    Nov 2001
    Location
    Woodland Hills, Los Angeles County, California, USA
    Posts
    9,607
    Rep Power
    4247
    Originally Posted by Kiuhnm
    Hi,
    this is my first post. I've been programming in Python for just 3 days so I don't know much about modules etc... and what they already offer.
    I saw a discussion about Currying or, better, about Python's lack of Curryng so I decided to implement it in Python myself.
    Nice work. However, there are some good implementations of currying listed in the Python cookbook and also available for free online from here:
    http://code.activestate.com/recipes/...th-a-function/
    Note that this particular recipe is over 10 years old.

    Also note that as of Python 2.5, there has been official support for it in the standard library using functools.partial(), so your assertion about python's lack of currying is slightly outdated by about 5.5 years:
    http://docs.python.org/library/funct...ctools.partial
    Last edited by Scorpions4ever; March 19th, 2012 at 12:12 AM.
    Up the Irons
    What Would Jimi Do? Smash amps. Burn guitar. Take the groupies home.
    "Death Before Dishonour, my Friends!!" - Bruce D ickinson, Iron Maiden Aug 20, 2005 @ OzzFest
    Down with Sharon Osbourne

    "I wouldn't hire a butcher to fix my car. I also wouldn't hire a marketing firm to build my website." - Nilpo
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2012
    Posts
    7
    Rep Power
    0
    Originally Posted by Scorpions4ever
    Nice work. However, there are some good implementations of currying listed in the Python cookbook and also available for free online from here:
    [...url removed...]
    Note that this particular recipe is over 10 years old.

    Also note that as of Python 2.5, there has been official support for it in the standard library using functools.partial(), so your assertion about python's lack of currying is slightly outdated by about 5.5 years:
    [...url removed...]
    No, that's not currying.
    Look at the examples that come with my code.

    Basically, partial() gives you manual currying, while my cur gives you a generalization of real currying.
    Let's say you've written this function:
    Code:
    def f(a, b, c, d, e, f):
        .....
    You can create a "curried" version like this:
    Code:
    cf = cur(f)
    Now, cf supports full currying. You can write
    Code:
    cf(1,2)(3,4)(f = 2)(e=1)(f=3)()
    By the way, note how we changed our mind about argument f.
    You don't need to use partial each time. Currying is completely automatic.
    Let's say you want to write a piece of code in a heavy functional programming style.
    Then just define your functions this way:
    Code:
    def f1(...):
        ....
    f1 = cur(f1)
    
    def f2(...):
        ....
    f2 = cur(f2)
    From now on you'll have full currying support without cluttering your code with extraneous calls.
    I explain all that and more in my article. I even talk about partial().
  6. #4
  7. Contributing User
    Devshed Demi-God (4500 - 4999 posts)

    Join Date
    Aug 2011
    Posts
    4,837
    Rep Power
    480
    bravo.

    Had I responded first, it would have had similar content to
    Scorpions4ever's post.

    Your uniqueness test might run faster as a set intersection
    Code:
    if set(kwArgs).intersection(set(newKwArgs)):
        raise SyntaxError(("Repeated kw arg while unique = True")

    Your examples could include use as a decorator.
    [code]Code tags[/code] are essential for python code and Makefiles!
  8. #5
  9. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2012
    Posts
    7
    Rep Power
    0
    Originally Posted by b49P23TIvg
    bravo.
    Thank you!

    Originally Posted by b49P23TIvg
    Had I responded first, it would have had similar content to
    Scorpions4ever's post.

    Your uniqueness test might run faster as a set intersection
    Code:
    if set(kwArgs).intersection(set(newKwArgs)):
        raise SyntaxError(("Repeated kw arg while unique = True")
    Thanks for the tip. I'll use set intersection and SyntaxError instead of Exception.

    Originally Posted by b49P23TIvg
    Your examples could include use as a decorator.
    If a decorator is a Python thing, then I still don't know about them
  10. #6
  11. Contributing User
    Devshed Demi-God (4500 - 4999 posts)

    Join Date
    Aug 2011
    Posts
    4,837
    Rep Power
    480
    python decorators are something like adverbs in j.

    www.jsoftware.com
    [code]Code tags[/code] are essential for python code and Makefiles!
  12. #7
  13. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2012
    Posts
    7
    Rep Power
    0
    Originally Posted by b49P23TIvg
    python decorators are something like adverbs in j.

    www.jsoftware.com
    BTW, I included this in my code:

    Code:
    # - Thanks to b49P23TIvg for suggesting that I should use a set operation
    #     instead of repeated membership tests.
    I hope you don't mind.

IMN logo majestic logo threadwatch logo seochat tools logo