Theme index -- Keyboard shortcut: 'u'  Previous theme in this lecture -- Keyboard shortcut: 'p'  Next slide in this lecture -- Keyboard shortcut: 'n'Higher-order Functions
17.  Currying

Currying is an idea, which is important in contemporary functional programming languages, such as Haskell. In Scheme, however, the idea is less attractive, due to the parenthesized notation of function calls.

Despite of this, we will discuss the idea of currying in Scheme via some higher-order functions like curry and uncurry. We will also study some ad hoc currying of Scheme functions, which has turned out to be useful for practical HTML authoring purposes, not least when we are dealing with tables.

17.1 The idea of currying17.4 Ad hoc currying in Scheme (1)
17.2 Currying in Scheme17.5 Ad hoc currying in Scheme (2)
17.3 Examples of currying
 

17.1.  The idea of currying
Contents   Up Previous Next   Slide    Subject index Program index Exercise index 

Currying allows us to understand every function as taking at most one parameter. Currying can be seen as a way of generating intermediate functions which accept additional parameters to complete a calculation

The illustration below shows what happens to function signatures (parameter profiles) when we introduce currying.

Figure 17.1    The signatures of curried functions. In the upper frame we show the signature of a function f, which takes three parameters. The frames below show the signature when f is curried. In the literature, the notation shown to the bottom right is most common. The frame to the left shows how to parse the notation (the symbol -> associates to the right).

Currying and Scheme is not related to each other. Currying must be integrated at a more basic level to be elegant and useful

 

17.2.  Currying in Scheme
Contents   Up Previous Next   Slide    Subject index Program index Exercise index 

Despite the observations from above, we can explore and play with currying in Scheme. We will not, however, claim that it comes out as elegant as, for instance, in Haskell.

It is possible to generate curried functions in Scheme.

But the parenthesis notation of Lisp does not fit very well with the idea of currying

The function curry2 generates a curried version of a function, which accepts two parameters. The curried version takes one parameter at a time. Similarly, curry3 generates a curried version of a function that takes three parameters.

The functions uncurry2 and uncurry3 are the inverse functions.

It is worth a consideration if we can generalize curry2 and curry3 to a generation of curryn via a higher-order function curry, which takes n as parameter. We will leave that as an open question.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(define (curry2 f)
  (lambda(x)
    (lambda(y)
      (f x y))))

(define (curry3 f)
  (lambda(x)
    (lambda(y)
      (lambda(z)
       (f x y z)))))

(define (uncurry2 f)
  (lambda (x y)
    ((f x) y)))

(define (uncurry3 f)
  (lambda (x y z)
    (((f x) y) z)))
Program 17.1    Generation of curried and uncurried functions in Scheme.


Exercise 4.8. Playing with curried functions in Scheme

Try out the functions curry2 and curry3 on a number of different functions.

You can, for instance, use then curry functions on plus (+) and map.

Demonstrate, by a practical example, that the uncurry functions and the curry functions are inverse to each other.

There is no solution to this exercise


 

17.3.  Examples of currying
Contents   Up Previous Next   Slide    Subject index Program index Exercise index 

Let us here show a couple of examples of the curry functions from Section 17.2.

Curried functions are very useful building blocks in the functional paradigm

In particular, curried functions are adequate for mapping and filtering purposes

The function font-1 is assumed to take three parameters. The font size (an integer), a color (in some particular representation that we do not care about here) and a text string on which to apply the font information. We show a possible implementation of font-1 in terms of the font mirror function in Program 17.2.

Expression

Value

(font-1 4 red "Large red text")
Large red text
(define curried-font-1 (curry3 font-1))
(define large-font (curried-font-1 5))
((large-font blue) "Very large blue text")
Very large blue text
(define small-brown-font ((curried-font-1 2) brown))
(small-brown-font "Small brown text")
Small brown text
(define large-green-font ((curried-font-1 5) green))
(list-to-string (map large-green-font (list "The" "End")) " ")
The End
Table 17.1    Examples of currying in Scheme.

1
2
3
4
(define (font-1 size color txt)
  (font 'size (as-string size)
        'color (rgb-color-encoding color)
        txt))
Program 17.2    A possible implementation of font-1 in terms of the font HTML mirror function.

 

17.4.  Ad hoc currying in Scheme (1)
Contents   Up Previous Next   Slide    Subject index Program index Exercise index 

In some situations we would wish that the map function, and similar functions, were curried in Scheme. But we cannot generate an f- mapper by evaluating the expression (map f). We get an error message which tells us that map requires at least two parameters.

In this section we will remedy this problem by a pragmatic, ad hoc currying made via use of a simple higher-order function we call curry-generalized.

It is possible to achieve 'the currying effect' by generalizing functions, which requires two or more parameters, to only require a single parameter

In order to motivate ourselves, we will study a couple of attempts to apply a curried mapping function.

Expression

Value

(map li (list "one" "two" "three"))
("<li>one</li>"
 "<li>two</li>"
 "<li>three</li>")
(define li-mapper (map li))
map: expects at least 2 arguments, given 1
(define li-mapper ((curry2 map) li))
(li-mapper (list "one" "two" "three"))
("<li>one</li>"
 "<li>two</li>"
 "<li>three</li>")
Table 17.2    A legal mapping and an impossible attempt to curry the mapping function. The last example shows an application of curry2 to achieve the wanted effect, but as it appears, the solution is not very elegant.

In Program 17.3 we program the function curry-generalized. It returns a function that generalizes the parameter f. If we pass a single parameter to the resulting function, the value of the red lambda expression is returned. If we pass more than one parameter to the resulting function, f is just applied in the normal way.

1
2
3
4
5
6
(define (curry-generalized f)
  (lambda rest
    (cond ((= (length rest) 1)
             (lambda lst (apply f (cons (car rest) lst))))
          ((>= (length rest) 2)
             (apply f (cons (car rest) (cdr rest)))))))
Program 17.3    The function curry-generalized.

The blue expression aggregates the parameters - done in this way to be compatible with the inner parts of the red expression. In a simpler version (cons (car rest) (cdr rest)) would be replace by rest.

In the next section we see an example of curry generalizing the map function.

 

17.5.  Ad hoc currying in Scheme (2)
Contents   Up Previous Next   Slide    Subject index Program index Exercise index 

We may now redefine map to (curry-generalized map). However, we usually bind the curry generalized mapping function to another name, such as gmap (for generalized map).

This section shows an example, where we generate a li mapper, by (gmap li).

Expression

Value

(define gmap (curry-generalized map))
(define li-mapper (gmap li))
(li-mapper (list "one" "two" "three"))
("<li>one</li>" 
 "<li>two</li>" 
 "<li>three</li>")
(gmap li (list "one" "two" "three"))
("<li>one</li>" 
 "<li>two</li>"
 "<li>three</li>")
Table 17.3    Examples of curry generalization of map. Using curry-generalized it is possible to make a li-mapper in an elegant and satisfactory way. The last row in the table shows that gmap can be used instead of map. Thus, gmap can in all respect be a substitution for map, and we may chose to redefine the name map to the value of (curry-generalized map).

If we redefine map to (curry-generalized map), the new mapping function can be used instead of the old one in all respects. In addition, (map f) now makes sense; (map f) returns a function, namely an f mapper. Thus ((map li) "one" "two" "three") does also make sense, and it gives the result shown in one of value cells to the right of Table 17.3.

Generated: Tuesday July 2, 2013, 09:15:36
Theme index -- Keyboard shortcut: 'u'  Previous theme in this lecture -- Keyboard shortcut: 'p'  Next slide in this lecture -- Keyboard shortcut: 'n'