Véase la expresión que define la función dimension:
dimension :- (f, datom) if Atómica?(f) then datom(f) else if Suma-o-Resta?(f) then DimAd( dimension( PrimerOperando(f), datom ), dimension( SegundoOperando(f), datom ) ) else if Producto?(f) then DimProd( dimension( PrimerOperando(f), datom ), dimension( SegundoOperando(f), datom ) ) else DimCoc( dimension( PrimerOperando(f), datom ), dimension( SegundoOperando(f), datom ) )
Interesa examinar la forma de las expresiones que ocurren a la derecha del símbolo :-. Véase que:
( "variable", "variable", ... , "variable" ) "expresión"
donde las variables son los argumentos nominales y la expresión definen la construcción de la imagen de éstas en la función.
"nombre" ("expresión", "expresión", ... , "expresión" ) "variable" ("expresión", "expresión", ... , "expresión" )
donde las expresiones entre paréntesis se llamarán argumentos efectivos de la aplicación y se hace notar que pueden ser, como en algunos casos del ejemplo, a su vez expresiones complejas y no necesariamente sólo variables.
Lo anterior sugiere cierta similitud en el comportamiento de los nombres y las variables. En particular, ambos pueden denotar funciones que son aplicables a un conjunto de argumentos.
Esta similitud es aún más amplia, puesto que ambos pueden denotar también resultados de aplicaciones de funciones. Esto es claro para las variables, como en el caso de f, que, en general denota el resultado de la aplicación de un constructor de fórmulas.
Sucede lo mismo para los nombres. Aunque no hayan sido usados con ese sentido en nuestro caso de estudio, podrían darse, por ejemplo, definiciones como:
1 :- sucesor(cero) 2 :- sucesor(sucesor(cero)) ...
que asociarían la representación corriente (con dígitos) a las expresiones dadas por la definición inductiva de los naturales con los constructores cero y sucesor.
De hecho, el uso de nombres es el mecanismo a través del cual una expresión arbitrariamente compleja puede ser referenciada sin necesidad de mostrar su estructura interna. Es, por tanto, una facilidad de abstracción en la formulación de expresiones.
La diferencia entre nombre y variable reside en que éste representa una expresión sobre cuya forma no se hacen hipótesis, es decir, "<una expresión cualquiera">. Cada nombre está asociado, por el contrario, a una expresión específica.
Estas consideraciones iluminan aspectos parciales de la estructura y el sentido de las expresiones usadas (a la derecha de :-):
<expresión> ::= <variable> | <nombre>
que se lee: expresión es una variable o bien un nombre.
A partir de estas expresiones primitivas pueden formarse otras más complejas, que, como éstas, se interpretan como definiciones y aplicaciones de funciones.
La definición del conjunto de estas expresiones, se completa usando inducción de una manera informal:
<expresión> ::= <variable> | <nombre> | ( <variable>,...,<variable> ) <expresión> | <expresión> ( <expresión>,...,<expresión> )
Se definen expresiones atómicas (variables y nombres) y otras compuestas que se llamarán abstracciones funcionales y aplicaciones.
Nótese que la denominación abstracción funcional se asocia a las expresiones que definen funciones. Esto no esun mero refinamiento de la terminología.
Efectivamente, considérese una expresión sencilla como:
4 + 2
Usando la abstracción funcional puede construirse una expresión de la cual la anterior sea un caso particular, haciendo, por ejemplo:
(x) 4 + x
La nueva expresión da una forma más general, de la cual pueden obtenerse, por aplicaciones convenientes, la expresión original además de otras. El cambio de constantes por argumentos es una técnica de generalización corriente también en la práctica de la programación.
Este concepto resultará de utilidad más adelante.
Es interesante analizar con más cuidado la sintaxis dada, tratando de determinar el tipo de construcciones que ella autoriza. En particular, considérese el caso en que se desea definir una función. Corresponde a una expresión de la forma de abstracción funcional:
( <variable>, ... , <variable> ) <expresión>
En particular, es, obviamente, admisible que contenga un único argumento nominal, como en:
(g) <expresión>
Ahora <expresión> puede ser reemplazada por cualquiera de sus formas válidas. Su significado será, de acuerdo a lo dicho, denotar el correspondiente de g en la función que se está definiendo.
Una de las posibilidades admitidas es que <expresión> sea reemplazada por otra abstracción funcional. Esto debe interpretarse como que el resultado de una función puede, a su vez, ser otra función.
Por ejemplo:
(g) (x) <expresión>
Ahora está dicho que la imagen de g será una cierta función de x. Está por darse, todavía, la definición de esta última.
En particular, g también puede ser considerada como una función, a su vez. Esto no es una novedad, puesto que una variable denota en principio una expresión genérica y, de hecho, así se usó datom en dimensión.
Entonces puede entenderse:
(g) (x) g (g (x))
La función definida sobre g le hace corresponder otra función que dado otro argumento x, aplica g a éste dos veces (twice, en inglés).
Ahora es posible asociar un nombre a esta función:
twice :- (g) (x).g (g (x))
y calcular la imagen en twice de una función conocida, por ejemplo la raíz cuadrada (sqrt):
twice (sqrt) :- (x) sqrt (sqrt (x))
donde aparece claro que la aplicación de twice a una función es otra función. Ésta puede aplicarse, a su vez, a un cierto valor, como en:
twice (sqrt) (16)
cuyo resultado, salvo error u omisión, debería ser 2.
Varias conclusiones deben obtenerse:
El tipo de función que este lenguaje maneja es llamado "<de orden superior"> (en inglés high order functions) para denotar el hecho de que sus argumentos y resultados son, a la vez, funciones.
twice (sqrt) (16)
que denota la composición de dos aplicaciones, que deben entenderse realizadas de izquierda a derecha. La primera devuelve una función, como ya se vió, la cual es aplicada al argumento efectivo 16.
Esto es diferente, aunque similar, a:
twice (sqrt, 16)
En el caso, la última expresión denotaría la aplicación de twice a dos argumentos. Sin embargo, su definición sólo admite uno. Para hacer consistente tal aplicación, la definición de twice debió haber sido:
twice :- (g, x) g (g (x))
que también es válida.
Esto muestra que toda función de más de un argumento puede expresarse en términos de funciones de orden superior de un argumento.
La transformación es muy sencilla:
( x1, ... , xN ) <expresión> (definición con N argumentos)
puede reescribirse como:
(x1), (x2), ... , (xN) <expresión> (función de orden superior de 1 argumento)
Para mayor ejemplo, la suma de enteros podría verse como:
suma :- (x) (y) x + y
es decir, una función de un argumento, que asocia a éste otra función de un argumento, que, a su vez, asocia a este último su suma con el primero.
Para fijar ideas, piénsese en la expresión suma (x) como la función que aplicada a cualquier valor, le suma a éste la cantidad x.
Lo anterior permite simplificar la estructura de las expresiones del lenguaje: en la formulación de la forma general de las abstracciones funcionales y aplicaciones no es necesario denotar una cantidad indeterminada de argumentos. Apenas uno es suficiente para todos los casos:
<expresión> :- <variable> | <nombre> | ( <variable> ) <expresión> | <expresión> ( <expresión> )
Estas expresiones pueden interpretarse usando un único concepto: el de función. Éstas pueden aplicarse entre sí y servir para definir a otras con plena libertad. Así lo autoriza la sintaxis.
El lector atento podrá argumentar que esta libertad es excesiva y posiblemente la crítica sea aceptable.
En efecto, hay expresiones permitidas cuyo sentido es oscuro, si no existente. Véase por ejemplo:
5 (8)
que es un caso de aplicación donde función y argumento efectivo son nombres, pero que carece de sentido puesto que el primero no admite ser aplicado a ningún argumento, y también:
sucesor (sucesor)
donde el argumento efectivo no es un natural, como debiera esperarse.
El problema es importante, y constituye la principal motivación para la introducción del concepto de tipo en los lenguajes.
Por supuesto, existen lenguajes funcionales con tipos, aunque no serán tratados en este desarrollo. Como consecuencia, la formación de expresiones con sentido exigirá la aplicación de una disciplina (no formalizada) adicional a las reglas de sintaxis de los lenguajes que se definan.
Sin embargo, la generalidad de la construcción de expresiones del lenguaje tiene importantes ventajas. En particular, si se reinterpreta el término función como programa se tendrá entonces que, en estos lenguajes, los programas pueden ser, con toda generalidad, argumentos de otros programas y pueden producir, también, otros programas como resultado.
Esta caracterización es, efectivamente, adecuada y se justificará plenamente en la siguiente sección, donde quedará de manifiesto su utilidad.