Currying

Introducción

De la Wikipedia:

In mathematics and computer science, currying or Schönfinkelisation, invented by Moses Schönfinkel and later re-invented by Haskell Curry, is the technique of transforming a function that takes multiple arguments (or an n-tuple of arguments) in such a way that it can be called as a chain of functions each with a single argument.

Véase Practically Groovy: Functional programming with curried closures (Groovy's everyday coding construct goes where no closure has gone before) Recojemos un extracto que introduce las clausuras:

...You've probably never cooked with curry() before, so we'll start from a simple, familiar base. The listing shows a closure referenced as multiply. It has formal parameters x and y and returns the product of these two values ...

generaciondecodigos@nereida:~/src/groovy/curry$ cat -n hellocurry.groovy
     1  #!/usr/bin/env groovy
     2
     3  def multiply = { x, y -> return x * y }  // closure
     4
     5  // triple = { y -> return 3 * y }
     6  def triple = multiply.curry(3)
     7
     8  // quadruple = { y -> return 4 * y }
     9  def quadruple = multiply.curry(4)
    10
    11  def p = triple.call(4)                   // explicit call
    12  println "p: ${p}"                        // p is 12
    13
    14  def q = quadruple(5)                     // implicit call
    15  println "q: ${q}"                        // q is 20
    16
generaciondecodigos@nereida:~/src/groovy/curry$ ./hellocurry.groovy
p: 12
q: 20

This closure is all well and good, but we're going to curry it up just the same. When calling the curry() method you need not supply the full complement of actual parameters. The curried call gives rise to the partial application of the closure. The partial application of a closure is another Closure object in which some values have been fixed.

Ejemplo de Currying: Logging

La currificación permite seguir una técnica en la que

  1. Se comienza por definir funciones de una gran generalidad y abstracción que contemplan el esquema general de resolución pero cuya conducta esta parametrizada en conductas (esto es, en clausuras)
  2. Se derivan progresivamente mediante currificación - es decir definiendo los parámetros conductuales, las clausuras - familias de soluciones al problema

Sigue un ejemplo modificado del libro de König 'Groovy in Action':

~/src/groovy/curry$ cat -n log.groovy 
     1  #!/usr/bin/env groovy
     2  
     3  def configurator = { format, filter, line ->
     4    filter(line) ? format(line) : null
     5  }
     6  
     7  def appender = { config, append, line -> 
     8    def out = config(line)
     9    if (out) append(out)
    10  }
    11  
    12  /*****************************************/
    13  
    14  def dateFormatter   = { line -> "${new Date()}: $line" }
    15  def debugFilter     = { line -> line.contains('debug') }
    16  def consoleAppender = { line -> println line }
    17  
    18  /*****************************************/
    19  
    20  def myConf = configurator.curry(dateFormatter, debugFilter)
    21  def myLog  = appender.curry(myConf, consoleAppender)
    22  
    23  /*****************************************/
    24  
    25  myLog '*********** debug:myLog ************'
    26  myLog 'here is a debug message with myLog'
    27  myLog 'here is a warning message (does not appear)'
    28  myLog 'here is an error message (does not appear)'
    29  myLog 'here is another debug message'
    30  
    31  /*****************************************/
    32  /* Another Log */
    33  import java.lang.management.*
    34  // something like: 1466@machine
    35  name = ManagementFactory.getRuntimeMXBean().getName();
    36  pid = Integer.parseInt(name[0..name.indexOf("@")-1])
    37  
    38  def datePIDFormatter   = { line -> "${new Date()}:$pid: $line" }
    39  def noFilter     = { line -> 1 }
    40  def stderrAppender = { line -> System.err.println line }
    41  
    42  def aConf = configurator.curry(datePIDFormatter, noFilter)
    43  def aLog  = appender.curry(aConf, stderrAppender)
    44  
    45  aLog '*********** aLog ************'
    46  aLog 'here is a debug message'
    47  aLog 'here is a warning message'
    48  aLog 'here is an error message'
    49  aLog 'here is another debug message'
The package java.lang.management provides the management interface for monitoring and management of the Java virtual machine as well as the operating system on which the Java virtual machine is running.

The interface RuntimeMXBean is The management interface for the runtime system of the Java virtual machine. A Java virtual machine has a single instance of the implementation class of this interface. This instance implementing this interface is an MXBean6.1that can be obtained by calling the ManagementFactory.getRuntimeMXBean() method.

The getName method returns the name representing the running Java virtual machine. The returned name string can be any arbitrary string and a Java virtual machine implementation can choose to embed platform-specific useful information in the returned name string. Each running virtual machine could have a different name.
La siguiente sesión en la shell Groovy explica el significadod e las líneas 33-36:
generaciondecodigos@nereida:~/src/java/reflection$ groovysh
Groovy Shell (1.6.5, JVM: 1.6.0_0)
Type 'help' or '\h' for help.
----------------------------------------------------------------
groovy:000> import java.lang.management.*
===> [import java.lang.management.*]
groovy:000> ManagementFactory
===> class java.lang.management.ManagementFactory
groovy:000> ManagementFactory.getRuntimeMXBean()
===> sun.management.RuntimeImpl@384ab40a
groovy:000> name = ManagementFactory.getRuntimeMXBean().getName()
===> 29139@nereida
groovy:000> pid = Integer.parseInt(name[0..name.indexOf("@")-1])
===> 29139
groovy:000>

Veamos algunas ejecuciones de log.groovy:

~/src/groovy/curry$ ./log.groovy 
Wed Dec 02 06:56:17 GMT 2009: *********** debug:myLog ************
Wed Dec 02 06:56:17 GMT 2009: here is a debug message with myLog
Wed Dec 02 06:56:17 GMT 2009: here is another debug message
Wed Dec 02 06:56:17 GMT 2009:1596: *********** aLog ************
Wed Dec 02 06:56:17 GMT 2009:1596: here is a debug message
Wed Dec 02 06:56:17 GMT 2009:1596: here is a warning message
Wed Dec 02 06:56:17 GMT 2009:1596: here is an error message
Wed Dec 02 06:56:17 GMT 2009:1596: here is another debug message
~/src/groovy/curry$
Redirigimos la salida estandard:
~/src/groovy/curry$ ./log.groovy > salida
Wed Dec 02 07:01:46 GMT 2009:1882: *********** aLog ************
Wed Dec 02 07:01:46 GMT 2009:1882: here is a debug message
Wed Dec 02 07:01:46 GMT 2009:1882: here is a warning message
Wed Dec 02 07:01:46 GMT 2009:1882: here is an error message
Wed Dec 02 07:01:46 GMT 2009:1882: here is another debug message
~/src/groovy/curry$ cat -n salida
     1  Wed Dec 02 07:01:46 GMT 2009: *********** debug:myLog ************
     2  Wed Dec 02 07:01:46 GMT 2009: here is a debug message with myLog
     3  Wed Dec 02 07:01:46 GMT 2009: here is another debug message
Redirigimos stderr:
~/src/groovy/curry$ ./log.groovy 2> salida
Wed Dec 02 07:04:45 GMT 2009: *********** debug:myLog ************
Wed Dec 02 07:04:45 GMT 2009: here is a debug message with myLog
Wed Dec 02 07:04:45 GMT 2009: here is another debug message
~/src/groovy/curry$ cat -n salida
     1  Wed Dec 02 07:04:45 GMT 2009:1961: *********** aLog ************
     2  Wed Dec 02 07:04:45 GMT 2009:1961: here is a debug message
     3  Wed Dec 02 07:04:45 GMT 2009:1961: here is a warning message
     4  Wed Dec 02 07:04:45 GMT 2009:1961: here is an error message
     5  Wed Dec 02 07:04:45 GMT 2009:1961: here is another debug message
Juntemos stdout y stderr y los enviamos a un fichero:
~/src/groovy/curry$ ./log.groovy 2&>1&> salida 
~/src/groovy/curry$ cat -n salida 
     1  Wed Dec 02 07:11:12 GMT 2009: *********** debug:myLog ************
     2  Wed Dec 02 07:11:12 GMT 2009: here is a debug message with myLog
     3  Wed Dec 02 07:11:12 GMT 2009: here is another debug message
     4  Wed Dec 02 07:11:12 GMT 2009:2140: *********** aLog ************
     5  Wed Dec 02 07:11:12 GMT 2009:2140: here is a debug message
     6  Wed Dec 02 07:11:12 GMT 2009:2140: here is a warning message
     7  Wed Dec 02 07:11:12 GMT 2009:2140: here is an error message
     8  Wed Dec 02 07:11:12 GMT 2009:2140: here is another debug message



Subsecciones
Casiano Rodríguez León
2010-04-30