style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">

Funciones de orden superior en Ceylon

Ceylon tiene una característica que en lo personal me parece muy atractiva, algo que en otros lenguajes anuncian con gran fanfarria pero en Ceylon no se ha hecho tanto barullo. Es algo que creo que ayudará mucho para el diseño de APIs en el futuro cercano. Se trata de las funciones de orden superior.

Visto de manera simple, las funciones de orden superior son funciones que reciben otras funciones como parámetros, o bien que devuelven funciones como valor de retorno. El concepto realmente no es complejo; el truco en lenguajes de tipado estático está en que la sintaxis no sea compleja (en lenguajes dinámicos es muy sencillo pasar una referencia si no se tiene que indicar el tipo de retorno de la función, ni su número ni tipo de argumentos).

Una cosa muy necesaria para poder manejar funciones de orden superior en un lenguaje de tipado estático, es tener un tipo que las represente. En el caso de Ceylon, tenemos la clase Callable. Podemos usar este tipo para guardar la referencia a un método de un objeto por ejemplo. Suponiendo que tuviéramos una clase MiClase con un método metodo que reciba un entero y devuelva un string, entonces podemos hacer esto:

MiClase obj = MiClase();
Callable<String,Integer> f = obj.metodo;

Ojo, no es lo mismo obj.metodo() (lo cual es una invocación al método) que obj.metodo, que es simplemente obtener la referencia a ese método; a partir de entonces se puede manejar como una función.

Usando esta sintaxis de declarar la referencia como Callable, nos permite tener la referencia y pasarla a alguna función o método que espere una función con los mismos tipos de parámetros. Sin embargo, no la podemos invocar, porque es un objeto. Si queremos guardar la referencia y además poder invocarla como función, entonces:

MiClase obj = MiClase();
function f(Integer) = obj.metodo;
//Y ahora sí podemos hacer esto
String s = f(1);

function es una palabra reservada en Ceylon, similar a value pero es para referencias a funciones, con inferencia para el tipo de retorno (el compilador ve que obj.metodo devuelve un String y por lo tanto f quedará con valor de retorno String).

Para poder recibir una función como parámetro, siguiendo el mismo ejemplo, se declara de esta forma:

//Esta función recibe una función invocable
void invocable(String f(Integer i)) {
  f(1);
}
//Esta función recibe un Callable, no lo puede invocar
void noInvocable(Callable<String, Integer> f) {
  invocable(f);
}
//Esta función devuelve un Callable
Callable<String, Integer> superior2() {
  return MiClase().metodo;
}

//Todo esto es válido
invocable(superior2());
noInvocable(superior2());
function f(Integer x) = superior2();
f(1);

Como pueden ver, la sintaxis no es nada complicada, y en cambio le da mucho poder al programador y permite una gran flexibilidad en el diseño de APIs. El ejemplo más típico de esto son los callbacks; por ejemplo en Swing y AWT en Java para poder recibir una notificación cuando se oprime un JButton, hay que tener un componente que implemente la interfaz ActionListener, la cual solamente tiene un método, actionPerformed. Se le pasa al botón el ActionListener y cuando el usuario lo oprime, el botón invoca el método actionPerformed de ese objeto.

Esto resulta en complejidad innecesaria, pues las implementaciones de ActionListener en la práctica terminan muy comunmente siendo clases anónimas que invocan un método del objeto donde se declaran, especialmente cuando el mismo componente quiere recibir distintas notificaciones de distintos elementos de la GUI.

Teniendo funciones de orden superior, un botón podría simplemente recibir la función que debe invocar cuando es oprimido, y eso es todo. En el método que recibe la función se especifican los tipos de retorno y de los parámetros que debe tener dicha función; no importa si en realidad será una función o un método de un objeto, su nombre, etc etc. Por lo tanto para que un componente sea notificado por distintos elementos, únicamente debe tener distintos métodos que tengan la firma requerida por los invocadores y es cosa de pasarle las referencias a los métodos correspondientes a cada invocador. Creo que esto:

boton.setListener(botonOprimido);
textField.setListener(textoCapturado);

Es más breve, conciso y entendible que esto:

boton.setListener(new ActionListener() {
  public void actionPerformed(ActionEvent event) {
    botonOprimido();
  }
});
boton.setListener(new ActionListener() {
  public void actionPerformed(ActionEvent event) {
    textoCapturado();
  }
});

O peor aún, implementar la interfaz ActionListener en el componente y luego dentro del método actionPerformed verificar quién envía el evento y de acuerdo a eso, invocar uno u otro método interno.

Esto no es algo nuevo ni algo exclusivo de Ceylon; en Groovy se hace fuerte uso de los closures, en Scala se usan mucho las funciones anónimas, etc. Simplemente es una característica más del lenguaje, que fue incluida en el diseño del mismo precisamente porque las ventajas que le da al programador son muchas y la complejidad del lenguaje aumenta muy poco.

El siguiente release de Ceylon, M2, probablemente ya incluirá funciones de orden superior. Hay más avance del esperado en esta área y tanto para JVM como para Javascript hay ya algo de soporte. Y en el futuro también se podrán definir funciones anónimas, cuya sintaxis ya veremos posteriormente. Mientras tanto, en http://try.ceylon-lang.org/ ya pueden jugar un poco con esta funcionalidad y otras cosas que tiene el lenguaje.

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

¿Lo que hay hasta el momento

¿Lo que hay hasta el momento son referencias a métodos entonces? Cool, vi el post en try ceylon, interesante.

Cual va a ser el tipo de dato para las funciones? Por ejemplo como se declara un arreglo de funciones que reciben int?

En tu ejemplo

void invocable(String f(Integer i))

No debería ser ( es pregunta )

void invocable(String f(Integer))

En este caso f será un invocable que ..que? Recibe un Integer y regresa un String?

Saludos

Imagen de ezamudio

Respuestas

A ver hay varias preguntas ahí, vamos una por una:

El tipo de dato para las referencias a funciones/métodos es Callable, eso lo expliqué al inicio. Callable es una interfaz parametrizada con el tipo de retorno de la función referida y los tipos de sus argumentos (si es que tiene). Un arreglo de funciones que reciben Integer (no hay int en Ceylon, porque no hay tipos nativos, todo es objeto), pues depende del tipo de retorno (suponiendo que fuera void entonces se puede expresar un arreglo de esas funciones como Callable<Void,Integer>[].

Cuando tienes un Callable, no lo puedes invocar, solamente es como un holder, para tener la referencia a la función. Si la quieres invocar, debes usar la otra sintaxis, donde declaras con la palabra reservada function. Es un poco complicado el tema de por qué es así esto pero es bastante interesante, hoy precisamente estábamos hablando de esto algunos miembros del equipo. Pero bueno primero termino de responder... preguntas si debería ser void invocable(String f(Integer)) en vez de void invocable(String f(Integer i))... si la pregunta realmente es que si me equivoqué, la respuesta es no, así es la sintaxis (si pones este en try.ceylon-lang.org verás que sin el identificador del parámetro no compila); si la pregunta es realmente que si la sintaxis debería ser así, sin el identificador... pues tiene sentido, pero para este caso solamente, porque hay otros casos en los que tendrá más sentido que tengas ese identificador pero son más rebuscados y además ahorita todavía no funcionan; pero también se puede discutir acerca de la uniformidad en la sintaxis (aunque parezca superfluo el identificador del parámetro de la función recibida, el tenerlo ahí simplemente hacer que sea más uniforme la sintaxis del código).

Y sí, en este caso f es una función que recibe un Integer y regresa un String; si lo ves solito, String f(Integer i), es bastante obvio que es una función que recibe un entero y devuelve una cadena; pues bien, si quieres definir una función que reciba como argumento una función que recibe un entero y devuelve una cadena, pues nomás declaras eso mismo en tu lista de argumentos.

En Ceylon hay argumentos por default, puedes declarar una función por ejemplo void saluda(String nombre="mundo") y entonces puedes invocarla con el argumento o sin él: saluda("Oscar") es válido y saluda() es equivalente a saluda("mundo"). Pues bien, podrías definir que esa función que recibes como argumento, tenga parámetros por default: void invocable(String f(Integer i=1)) y entonces dentro de invocable puedes invocar f(100) o invocar f() (OJO: esto todavía no funciona con las de orden superior; funciona ya con las normales nada más).

Elaborando un poco más sobre tu ejemplo del arreglo de Callables: Suponte que tienes ese arreglo de funciones y así lo tienes declarado; si tomas elementos de ahí, pues son Callable<Void, Integer> pero no los puedes invocar. Si quieres invocarlos, puedes hacer algo así:

void loco(Callable<Void, Integer>[] funcs) {
  variable Integer i:= 0;
  for (ref in funcs) {
    function f(Integer i)=ref;
    f(i++);
  }
}

(OJO: esto compila, pero no corre en JVM hay por ahí un bug todavía con una conversión entre java.lang.Long y ceylon.language.Integer, pero en JS sí compila y corre aunque tienes que modificarlo un poco si quieres ver algo).

Como puedes ver, el i de la declaración de f no tiene conflicto con la variable local i. Es casi un adorno, para que tengas una sintaxis coherente. En este caso lo que hacemos dentro del for es ya crear una función que invoca el Callable (parece mucha indirección, realmente es va a invocar el Callable, pero aquí ya lo declaraste como función, y por lo tanto lo puedes invocar).

Debes estar preguntándote ¿bueno y por qué tanto relajo y no nomás le ponen un método call a Callable y ya? La respuesta es interesante, pero algo larga. La cosa es que Callable es una interfaz, declarada como Callable<out Return, CallableArgument...> y no tiene métodos. Podría tener un método Return call(CallableArgument...) pero si implementas eso, realmente sólo puedes tener versiones que reciben distinto número de argumentos del mismo tipo. No puedes declarar dentro del lenguaje una interfaz que defina un número variable de argumentos y que mantenga la diversidad de tipos; cuando mucho podrías definir que los argumentos sean tipos unión de todos los que se manejan y... bueno. Ya ves que no es fácil. En Scala su solución fue tener un montón de traits desde Function0[+R], Function1[-T1,+R] hasta Function22[-T1...T2,+R] y así a cada una le pones su def apply():R hasta def apply(p1:T1,p2:T2..blabla...p22:T22):R pero pues por una parte te limita a que sólo puedes tener funciones hasta de 22 argumentos, y por otro lado quedó un efecto medio raro de que el método apply recibe tratamiento especial en el compilador, porque CUALQUIER clase que implemente apply aunque no sea una función, puedes usar la sintaxis objeto(...) en vez de objeto.apply(...) (similar a lo que hace Groovy con el método call, le da ese mismo tratamiento especial que te permite usar esa sintaxis, aunque en Groovy es distinto por ser dinámico, cuando ve x() en tiempo de ejecución checa si x tiene el método call y lo invoca; en Scala si no hay método apply definido o no casan los parámetros y tipo de retorno, no compila).

Entonces... para evitar esos desmadres, que van en contra de la filosofía de diseño de Ceylon porque el beneficio que otorgan no es mucho mayor que la complejidad que agregan, queda mejor una limitante muy simple: Si declaraste un valor o variable como Callable, puedes hacer referencia a funciones que coincidan con el tipo de retorno y argumentos, pero no lo puedes invocar; solamente puedes invocar funciones, que hayas declarado como tales, pero puedes declarar funciones que simplemente son referencias a Callables y así puedes invocarlos. Bueno y realmente ni tienes que usar la palabra function, lo que pasa es que esa es para hacer la inferencia de tipos porque es una declaración local; puedes poner el tipo, es decir en ese último ejemplo del método loco declarar f como String f(Integer i)=ref. Por qué es chido esto? Ah pues porque se puede usar para declarar métodos en una clase que sean como aliases a otros, entre otras cosas; hay varios ejemplos en la sección 6.7 de la especificación de Ceylon.

Imagen de greeneyed

Me gusta

Aparte de los mencionados, el primer ejemplo que me viene a la cabeza es la representación de una tabla de estados/transiciones. Las he implementado en Java, pero con algo así quedarían más elegantes evitando el tener que crear interfaces e instancias para conseguir que compile.

Ah vaya.. entonces las

TL;DR ya había escrito mucho de esto cuando caí en cuenta de algunas cosas y ya no quise ni corregirlas ni borrarlas :P

Ah vaya.. entonces las funciones no tiene tipo por si mismas ?

Por que Callable es finalmente una interfaz ( o clase o un lo que sea del ámbito OO )

Me explico ( y debe de haber términos correctos para todo esto, pero no encuentro la referencia, quizá el Dr. Ferro me pueda guiar acá ):

En los lenguajes de programación las cosas tienen:

0. Identificador
1. Tipo
2. Valor
3. Sintaxis de literal

Por ejemplo si tuvieramos algo como:

a : Integer = 1

el identificador sería a, el tipo Integer, el valor 1 y la sintaxis de la literal sería 1

s : String = "ese"  // s de tipo String, valor "ese" y literal " "
a : Integer[] = [1,2,3] // a, tipo "arreglo de enteros" , valor ( 1,2,3 ) literal [  ]

Y una función podría ser:

f : func(Integer):Integer = func( x: Integer ) : Integer {
    return x + 1;
}
// o tambien porque no
f : func(A) = func(x) { return x+1 }

Y ahí sería:

id: f
tipo: funcion de que recibe entero y regresa entero
valor = una funcion que regresa x + 1
literal = function ( parametros ) retorno { }

( mmhhh leyendo y releyendo el post, tu respuesta y esta doc ) .. .a a ver creo que ya le voy captando.

Si yo quisiera declarar un arreglo de funciones que reciben entero también debería poder hacerlo así en Ceylon

void f(Integer)[] arreglo = {  /*un momento.. cual es la sintaxis para las funciones??*/  };

uahm... ¬¬

:)

Ok, ya. En este momento ( y lo escribiste en tu post ) no has dicho cual será la sintaxis para las funciones, eso viene probablemente en M2, ok, esperemos entonces.

Luego y ver si entendí bien.

Las referencias a métodos tienen el tipo Callable

Callable<R, T1> f = unObjeto.unMetodo

Si se queiren invocar deben de declararse con la palabra reservada function

function R f(T1) = ubObjeto.unMetodo

Ya.

Sobre lo de la i en la funcion, no, no me estaba preguntando si te habías equivocado, sino más bien quería saber cual era la razón de incluir el nombre del parametro en la declaración del tipo de la funcion.

En cuanto al usar Callable y la limitante de 22 funciones en Scala ( 255 en Groovy según recuerdo y 2 en Ryz :P ) y el porque no poner el método Call y ya ( análogo al método apply en Scala) más bien, lo que si me pregunto es porque tienen que "escurrir" esos los detalles de la implementación en estos lenguajes ( NJVMPL como les llamo ) y no quedarse por debajo. La respuesta que tengo hasta ahora es que la JVM no tiene una instrucción especial para este tipo de cosas y nos forza a llamarlo con algo existente.

Para mí, mucho mejor que poner el método callable, sería no permitirlo en lo absoluto.

Ejemplo en otro lenguaje de programación existente: Go ( si @benek si, lo sé ) no hay otra forma de declarar una funcion más que con una literal de funcion con un tipo funcion.

De los ejemplos anteriores:

// funcion f, de tipo funcion que recibe entero y regresa entero  con sintaxis func id ( param ) return { }
func f ( x int ) int {
   return x + 1
}

Se puede declarar un arreglo de estas funciones asi:

package main
import( "fmt" )

func main() {
    arr :=  [] func(int) int {
        func( x int ) int { return x + 1 },
        func( y int ) int { return y * 2 }
    }

    for i := 0 ; i < len ( arr ) ; i++ {
        fmt.Printf("%d\n", arr[i](i))
    }
}

Lo cual escala bien cuando tienes una funciona que recibe otra funcion que devuleve otra funcion que...

 f func(func(int,int) int, int) func(int, int) int

Que más se puede decir... todo esto es super emocionante ( para alguno jo jo jo ) ... espero ya estar de regreso por allá para dedicarle más tiempo a este hobby

:)

Saludos y al pendiente de las siguientes entregas :)

Imagen de ezamudio

sintaxis

OK la sintaxis para declarar una función ya la usé bastante en los ejemplos:

TipoDeRetorno identificador(TipoParametro1 idparm1, TipoParametro2 idparm2, TipoParametroSecuenciado... parm3)

Las referencia a métodos son de tipo Callable efectivamente. La cosa es que si declaras un valor como Callable, no puedes invocar la función a la que hace referencia. Si quieres tener una referencia invocable, declárala como si fuera una función, no hay ningún problema, total puedes tener funciones anidadas, y piensa que estás haciendo en cierta forma un alias:

String iniciales(Integer x) = "Oscar".initial;
//y luego simplemente invocas
print(iniciales(1));

La función iniciales te dará el número de letras iniclales de "Oscar" que le pidas. Si te fijas, no usé la palabra function. Esa palabra reservada es el equivalente de value para funciones. value es para declaraciones locales y permite hacer inferencia de tipos: value x = "Hola" es lo mismo que decir String x="Hola" (es más útil cuando pones por ejemplo value s={1,2,3,4} en vez de Sequence<Integer> x={1,2,3,4}). Puedes declarar una función con su tipo de retorno, o ponerle function y dejar que el compilador infiera el tipo de retorno, pero sólo para funciones locales, es decir que no sean visibles fuera del alcance del bloque donde estás (puedes usar function en declaraciones de métodos privados en una clase por ejemplo, pero no para los métodos públicos; Ceylon sólo hace inferencia de tipos en declaraciones locales).

Yo también pienso que lo mejor es no permitir que los objetos Callable sean invocables, es decir es simplemente una interfaz sin métodos, porque es para guardar referencias a métodos o funciones. Sirven para crear funciones locales, cuando las declaras como tales; lo que pasa es que una función la puedes implementar simplemente poniéndole la referencia a otra función:

shared void funcion1(String x) { /*bla*/ }
//y en otro lado
void funcion2(String x) = funcion1; //o bien objeto.metodo siempre y cuando la firma de metodo sea String metodo(String)

Para declarar en Ceylon una función como esa última que dices, es simplemente:

Callable<Integer, Integer, Integer> f(Integer func(Integer a, Integer b), Integer c)

Imagen de ezamudio

Edición

Creo que realmente el problema es que expliqué horriblemente mal lo de las funciones y referencias a funciones. En cuanto tenga tiempo editaré el post.

¿Saben que?No le crean nada

¿Saben que?

No le crean nada a Enrique... mejor pruébenlo directamente en web http://try-ceylon.rhcloud.com/

jejejeje

Esta super fregón la identación y el highlight y, y, y, y todo Enrique... :) :)

Kudos!!!!

Imagen de ezamudio

jajaja

gracias!! aunque la indentación y highlight son cortesía de CodeMirror, el editor que solamente integré ahí y le moví un poco al syntax HL de javascript para que resaltara las keywords de Ceylon. Me falta ponerle un color distinto a las clases (todo lo que empiece con mayúscula básicamente) y que indente bien porque hay uno que otro detallito donde se apendeja pero no sé bien por qué.

EDIT: por cierto ya viste que si pones el mouse sobre alguna clase, función o método del módulo del lenguaje, te sale su documentación? No he logrado hacer que salga junto al mouse, por ahora sale abajo y se ve bastante pinche pero pues algo es algo... por mientras... (en ese código tuyo pon el mouse sobre "print" por ejemplo). Para que salgan los docs tienes que haber compilado el código (la compilación se trae el js generado y los docs).

Imagen de bferro

La sintaxis entonces no es uniforme

Creo que algo que el lenguaje debe lograr es uniformidad en su sintaxis. Parte de esa uniformidad es en la declaración de referencias de la forma:

tipo valor;
 // Para definir que el identificador valor es de tipo tipo

Esa sintaxis no se mantiene para definir "tipos" funciones lo que puede causar confusión al principio, aunque por supuesto después nos acostumbramos a ella. Me imagino que exista alguna razón en el compilador de Ceylon para hacerlo de la manera en que lo hace.
No me queda claro lo que comenta Enrique de que un objeto Callable es un objeto y por tanto no puede invocarse.
Las funciones son objetos y se invocan, de la misma manera que se invocan objetos. La razón del metódo apply() (que Enrique comenta que tiene un efecto medio raro) es precisamente de hacer universal la aplicación de funciones sobre una lista de argumentos, la aplicación de objetos sobre una lista de argumentos, etc., para lograr que el lenguaje sea precisamente escalable. De esta forma, una llamada a un método tiene la misma sintaxis que la aplicación de un objeto sobre una lista de argumentos. Quien me brinda entonces una clase en la biblioteca tiene la libertad de escribir objetos, objetos anidados, etc, y yo como usuario sigo con mi sintaxis uniforme.

Imagen de ezamudio

uniformidad

La referencia a una función es como mencionas: tipo valor; Callable f (pero bueno, la interfaz Callable es parametrizada y por eso puedes ponerle ahí el tipo de retorno y de cada argumento cuando la función los tiene). Callable es una referencia a una función. La interfaz no tiene definido ningún método, simplemente por eso es que no se puede invocar la función como método (llámese apply, call, etc no hay nada definido).

Como mencioné hace rato, la verdad es que expliqué muy mal la otra parte, lo de function. En realidad no es una referencia a una función; sino que ahí se declara una función (o método, dependiendo del contexto). La implementación de dicha función, puede hacerse simplemente asignando un Callable en vez de tener un bloque de código. Es decir:

Callable<String,Integer> f; //Esto es una referencia a un método o función que recibe Integer y devuelve String
String f1(Integer x) { return x.string; } //Esta es una función "normalita", su implementación es un bloque de código
String f2(Integer x) = f; //La definición de esta función es un Callable.
function f3(Integer x) = f; //Esto es equivalente a f2, pero dejamos que el compilador infiera el tipo de retorno.

Originalmente expliqué mal unas cosas. La primera línea es una referencia a una función, pero la tercera y cuarta línea no son referencias; son funciones, definidas usando la referencia de la primera línea.

Imagen de bferro

A lo que me refiero con la uniformidad es a:

A lo que me refiero con la uniformidad es a cosas como las siguientes:

void higher(String func(Integer i)) {
    print(func(100));

Yo preferiría:

void higher( tipo func ) {
  print(func(100));
}

Ideando por supuesto una sintaxis para definir un tipo función con Integer como argumento y String como retorno
Por supuesto que me queda claro que no puedes invocar un Callable porque no tiene métodos. Lo que no me queda claro aun es porque no se diseña de otra forma

Imagen de ezamudio

alternativa

puedes declarar tu función void higher(Callable<String,Integer> f) si así lo prefieres. Pero para poderla llamar, adentro de higher defines otra función usando f para poderla llamar:

void higher(Callable<String, Integer> ref) {
  String f(Integer i) = ref;
  print(f(100));
}

Hasta donde entiendo yo (porque aquí sí habría que consultar con Gavin), la bronca está en la firma del método call (o como le quieras llamar). No hay manera de poder definir un método con un número variable de parámetros y manteniendo la seguridad de tipos, dentro del lenguaje. A lo mucho se podría definir que los parámetros son Object[] para aceptar cualquier número de parámetros, pero se pierden los tipos de los mismos y entonces cuando tienes una referencia a un método le podrías pasar objetos que no son del tipo que espera.

O sea algo como: void

O sea algo como:

void deseable(  String(Integer)  unaFuncion )  {
     print( unaFuncion( 100 ) );
}

Pero estaría raro porque el tipo seria muy similar la instanciar un String.

Otra opción sería tener una palabra reservada como fRef o algo así y usarlo como

void deseable( String fRef(Integer) unaFuncion ) {
   print( unaFunction(100) );
}

Puagh.. no no .

O con function

void deseable( function String(Integer) unaFuncion)  {
...
}

Ahh este se ve mejor.

Eeen fin. Esa es una de las principales razones por las cuales varios JVMPL decidieron por identificador : tipo incluso Go, lo hizo por eso :P ( #buenoYa )

def deseable( unaFuncion : (Integer) => Unit ) : Unit {
...
}

Para poder tener el identificador de un lado y el tipo del otro.

Esperemos.

Imagen de bferro

La alternativa le pasa la bolita a otro

Con la alternativa que planteas Enrique, le pasas la bolita a otro:
En tu código:

void higher(Callable<String, Integer> ref) {
  String f(Integer i) = ref; //Aquí quedó la bolita
  print(f(100));
>

La línea comentada no tiene la sintaxis uniforme deseada

Imagen de ezamudio

sí es uniforme

Tal vez no tiene la sintaxis que estabas esperando, pero sí es uniforme... lo que pasa es que esa línea NO es una referencia a una función, es la definición de una función, y las funciones se definen con la sintaxis TipoDeRetorno identificador(Tipo1 parm1, Tipo2, parm2...). Lo único que se podría considerar como no uniforme es que en vez de que siga un bloque de código, se define asignándole una referencia a otra función. Eso es lo que expliqué mal en el post original, diciendo que era otra manera de tener una referencia a una función, cuando en realidad esa es una manera de definir una función.

Imagen de bferro

Entonces la definición no es uniforme

Entonces la definición de una función no es uniforme, porque no puedo escribir lo siguiente:

Integer f() =5

para definir la función f. Por supuesto que dará un error diciendo seguramente que Integer no es de tipo Callable. Habrá entonces dos sintaxis para definir una función de nivel tope, la normal y aquella que produce la invocación de un Callable acorde con su firma.

...Pero si se escribe:

...
Pero si se escribe:

Integer f() = /* como vaya a ser la literal de funcion aquí */  

Si va a funcionar. Supongamos que las funciones anónimas se escriban TipoRetorno args bloque entonces esto si compilaría

Integer f() = Integer(){ return 5 };

Porque no usar .call() para mí tiene sentido. Se deben de tratar a las funciones como funciones, no como objetos ( aunque se le parezcan tanto, en particular los closures ) es más, ni siquiera debería poderse declarar como Callable y llamarlo de forma intercambiable, pero a falta de un codigo de bytes que indique apuntador a funcion pues no queda de otra.

JRuby está haciendo muchísimo trabajo en este punto utilizando InvokeDynamic, que le viene perfecto, pero para un lenguaje de tipeo estático conviene porque se reduce la velocidad de ejecución al invocar el método.

Al parecer Java 8 serán implementados de una forma similar

Imagen de bferro

En un lenguaje orientado a objetos todo es un objeto

Dice Oscar:

Se deben de tratar a las funciones como funciones, no como objetos ( aunque se le parezcan tanto, en particular los closures ) es más, ni siquiera debería poderse declarar como Callable y llamarlo de forma intercambiable, pero a falta de un codigo de bytes que indique apuntador a funcion pues no queda de otra.

Todo lo contrario: en un lenguaje orientado a objetos puro todo es un objeto y por tanto las funciones deben ser objetos para que puedan ser manipuladas como "first class citizens".
Todos los lenguajes que así lo consideran tiene que idear un mecanismo para que la aplicación de un función sobre una lista de argumentos tenga una sintaxis y semántica "similar" a la aplicación de un objeto sobre una lista de argumentos. Ceylon idea el suyo como ha estado explicando Enrique, Scala tiene el suyo y otros lenguajes también lo tienen.
La forma en que lo hace Scala es como sabemos utilizando de manera implícita o explícita un método apply. Al hacerlo facilita otras cosas entre ellas la de pattern matching mediante extractores con sus correspondientes inyectores, a tratar el acceso a los elementos de un arreglo como una aplicación de un objeto sobre una lista de un argumento, etc.
De forma implícita es similar a Ceylon, con la distinción que Scala sí define tipos para las funciones y aprovecha esas definiciones para ofrecer métodos que brindan versiones currificadas y "tupleads" de la funcion. Lo similar se puede ver en el siguiente código:

class Class1 {
  def m(arg Int) { println("Valor de arg " +arg }
}

val obj = new Class1

val function1 = obj m _  //La "expansión eta" es explícita

val function2: (Int)=>Unit = m  //La "expansión eta" es implícita

function1(8)

function2(9)

La forma en que lo hace Ceylon también es atractiva, pero yo al menos no considero que otras formas son incorrectas.
Tanto Ceylon como Scala resuelven algo importante que algunos llaman "expansión eta" (convertir un método a una función).
Menciono Scala no para confrontar los dos lenguajes. Creo que con eso se gana poco. Abusamos a veces mucho de la comparación de lenguajes como punto de partida para evaluar las bondades de ellos.

Exacto, estamos de acuerdo en

Exacto, estamos de acuerdo en el concepto pero no en la implementación ( por así decirlo )

Al hacer a las funciones parecidos a otras cosas se les está dando el estatus de ciudadanos de segunda tanto que no se les reconoce sus derechos :P ( derecho a tener una sintaxis propia por ejemplo y pasarlos como si fueran objetos jo jo jo )

Me recuerda a este tweet: "Todos merecen ser tratados igual, no importa si eres negro, amarillo, café o normal" O.o
http://twitpic.com/480utb

:)

Imagen de bferro

La discusión es interesante

A pesar de que estamos discutiendo cosas interesantes, veo poco interés en la comunidad de Java México de participar. Es un buen momento con la evolución de Ceylon de discutir cosas valiosas, aprovechando que Enrique está participando en el proyecto y que también tenemos a Gavin a la mano.
Los más jóvenes deberían meter las manos en esto. Habrá que promover más posts de este tipo y no solamente los de detectar errores y corregir bugs, que por supuesto también son necesarios.

Imagen de ezamudio

así es!

La spec no está escrita en piedra; ha ido cambiando por distintas razones, aunque realmente no he visto cambios de fondo, solamente algunos detalles (de repente desaparece por ahí algún operador o se cambia una interfaz, o un método se mueve hacia arriba o abajo en una jerarquía de clases o interfaces), etc. Es algo muy dinámico porque está en pleno desarrollo el lenguaje y también el SDK. Esto de las funciones de orden superior por ejemplo es algo que hace dos semanas no estaba funcionando aun, y ahora que ya lo tenemos, estoy corrigiendo algunas broncas que salieron por ejemplo con referencias spread y cosas así (ya tenía el operador spread funcionando pero no había podido probar si jalaba con la pura referencia sin invocación, pero bueno para cuando lean esto ya estará el push que arregla ese problema).

Curioso que consideren que las funciones en Ceylon son de segunda; yo lo veo alrevés de como están en Scala, porque me resulta muy curioso que en Scala no se pueda declarar una función solita, fuera de cualquier objeto, usando sintaxis de función; solamente se puede declarar usando la sintaxis de objeto, mientras que en Ceylon sí puedes declarar funciones de nivel tope, anidadas, etc con la sintaxis de función. Pero bueno, son temas de semántica tal vez.

Imagen de bferro

Las funciones en Ceylon Sí son objetos

Parece ser que al escribir tantas cosas, nos empezamos a hacer bolas. Las funciones en Ceylon sí son objetos y por tanto son ciudadanos de primera clase en el lenguaje, y por supuesto que pueden escribirse a nivel tope con una sintaxis "dulce" similar a la manera es que escribimos los métodos, y por supuesto que también pueden ser anidadas.
También pueden escribirse funciones a nivel tope y anidadas en Scala, como ya habíamos comentado en otro post, y también puedo escribir funciones anidadas. La diferencia es de sintaxis, que en Ceylon es más "dulce".
Ceylon es un lenguaje de tipado estático, y al tratar a las funciones como valores, tiene que definir un tipo para la función, como lo define para cualesquiera valores que permite.

Imagen de ezamudio

revisión

Este post necesita una buena revisión. Han cambiado varias cosas y además ahora ya hay funciones anónimas y ya tenemos la sintaxis para definir funciones inline usando argumentos nombrados en vez de posicionales.

style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">