Software Guru Conference & Expo 2014

Ceylon: comparativa con otros lenguajes

Como ya se ha escrito aqui en este sitio, Ceylon es un lenguaje de programación en desarrollo que pretende tomar lo bueno de Java y olvidarse de lo malo.

¿Cómo compilo/ejecuto un programa en Ceylon?

Antes de comenzar comenzar debes decargar la versión actual 0.4 (Analytical Engine).

Compilar:

ceylon compile source\programa.ceylon

Ejecutar:

ceylon run default

El clásico programa Hola mundo

En Ceylon.

holamundo.ceylon

doc "Esto se usa para documentar el programa"
by "el que escribe el programa"
void run(){
  print("Hola mundo");
}

Si todo sale bien debe imprimir:

"Hola mundo"

En Groovy.
holamundo.groovy

println "Hola mundo"

En Python.
holamundo.py

print "Hola mundo"

En Scala.
Holamundo.scala

object Holamundo{

 def main(args: Array[String]){
   
   println("Hola mundo");
}

}

En Java.
Holamundo.java

public class Holamundo{

   public static void main(String[] args){
      System.out.println("Hola mundo");
  }
}

En Clojure.
holamundo.cjl

(printl "Hola mundo")

En C.
holamundo.c

#include "stdio.h"

void main(){
  printf("Hola mundo");
}

¿Cómo importar librerías/paquetes?

En Ceylon.

import tuPaquete { Clase1, Clase2}
import Clase {metodo1, metodo2}

En Groovy.

import tuPaquete
import tuPaquete.Clase;

En Python.

from modulo import nombre
from modulo import *

En Scala.

import tuPaquete._;
import tuPaquete.Clase;

En Java.

import tuPaquete;
import tupaquete.*;

En Clojure.

(import '(tuPaquete))

En C.

#include "modulo.h"

¿Cómo introducir/mostrar datos?

He aqui unos ejemplos de como introducir y mostrar datos.

En Ceylon.

introducirDatos.ceylon

import java.util { Scanner }
import java.lang { System { sin=\Iin } }

doc "método principal del programa"
shared void run(){
  Scanner teclado=Scanner(sin);
  //introducir nombre y edad
  print("Tu nombre: ");
  String nombre=teclado.nextLine();
  print("Tu edad: ");
  Integer edad=teclado.nextInt();
  //mostramos los datos
  print("Hola " nombre " tienes " edad " años de edad");
}

En Groovy.
introducirDatos.groovy

import java.util.Scanner

static main(args){
Scanner teclado = new Scanner(System.in)
def nombre=""
def edad=0
println "Tu nombre: "
nombre=teclado.nextLine()
println "Tu edad: "
edad=teclado.nextInt()

println "Hola ${nombre} tienes ${edad} años de edad"

}

En Python.
holamundo.py

nombre=raw_input("Tu nombre: ")
edad=int(raw_input("Tu edad: "))
print "Hola %s tienes %d años de edad"%(nombre,edad)

En Scala.
IntroduceDatos.scala

object IntroduceDatos{

 def main(args: Array[String]){
   var nombre=readLine("Tu nombre: ");
   println("Tu edad");
   var edad=readInt();
   println("Hola "+nombre+" tienes "+edad+" años de edad");
}

}

En Java.
IntroduceDatos.java

import java.util.Scanner;
public class IntroduceDatos{

   public static void main(String[] args){
   Scanner teclado=new Scanner(System.in);
   String nombre="";
   int edad=0;
      System.out.println("Tu nombre: ");
      nombre=teclado.nextLine();
      System.out.println("Tu edad: ");
      edad=teclado.nextInt();
      System.out.println("Hola " +nombre +" tienes " +edad+"  años de edad");
  }
}

En Clojure.
introduceDatos.cjl

;; introducir datos
(import '(java.util Scanner))
(def scan (Scanner. *in*))
(println "Tu nombre: ")
(def nombre (.nextLine scan))
(println "Tu edad: ")
(def edad (.nextInt scan))
(println "Hola")
(println nombre)
(println "Tienes ")
(println edad)
(println " años de edad")

En C.
introduceDatos.c

#include "stdio.h"

void main(){
  char[10] nombre;
  int edad=0;
  printf("Tu nombre: ");
  gets(nombre);
  printf("Tu edad: ");
  scanf("%d",edad);
  printf("Hola %c tienes %d anyos de edad",nombre,edad);
}

Puede parecer algo confuso, pero haber que pasa con las clases.

Crear clases

En este caso omito a C y a Clojure (usted disculpe).

En Ceylon.

ejemploClase.ceylon

doc "defino una clase"
class Clase(){
  shared void mensaje(){
  print("Hola desde un metodo");
  }
}

doc "método principal del programa"
void run(){
activa(Clase());
}

void activa(Clase clase){
  clase.mensaje();
}

En Groovy.
ejemploClase.groovy

class Clase{
  def mensaje(){
    println "Hola desde un metodo"
  }
}

static main(args){
  activa(new Clase())
}

def activa(clase){
  clase.mensaje()
}

En Python.
holamundo.py

class Clase:
   def mensaje(self):
     print "Hola desde un metodo"

def activa(clase):
   clase.mensaje()

def main():
   activa(Clase())

if __name__ == "__main__":
   main()

En Scala.
EjemploClase.scala

class Clase(){
  def mensaje(){
   println("Hola desde un metodo");
  }
}

object EjemploClase{
 def main(args: Array[String]){
   activa(new Clase());
}
def activa(clase:Clase){
  clase.mensaje();
}

}

En Java.
EjemploClase.java

 
public class EjemploClase{

   public static void main(String[] args){
      new EjemploClase().activa(new Clase());
  }

  public void activa(Clase clase){
    clase.mensaje();
 }
}

class Clase{
  public void mensaje(){
   System.out.println("Hola desde un metodo");
  }
}

¿Por qué Ceylon, ... es mejor que Scala y/o Groovy?

Hacer una comparativa con otros lenguajes es válido, lo que es un poco "injusto" decidir si es o no mejor que X o Y lenguaje, ya que el lenguaje todavía esta en desarrollo. Algunos consideran que Groovy es mejor que Java, otros que Scala no es mejor si no mucho mejor que Java y Groovy juntos. Otros más que en RedHat debio sumarse a Scala y no crear un nuevo lenguaje como Ceylon.

No importa si crees que tu programa esta bien escrito ... siempre va ocurrir un error

Según yo algunos de los problemas que tienen lenguajes como Scala y Groovy son:

  1. Scala: es muy dificil aprender y cada vez se vuelve más complejo (no quiere decir que sea malo)
  2. Groovy: debes tener cuidado de no olvidar crear métodos, inicializar variables,etc.

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.
Imagen de ezamudio

complejidad y otras cosas

Me parece excelente que estés explorando Ceylon y otros lenguajes de la JVM. Quiero hacer unos comentarios al respecto de la complejidad: No sé a qué te refieras exactamente con lo de que Scala se vuelve cada vez más complejo. Los problemas que yo personalmente le veo es que su sistema de tipos es MUY complicado (muy completo, sí, pero por lo mismo muy complicado); basta echarle un ojo a sus bibliotecas de colecciones para darse una idea. La sobrecarga de operadores es algo de lo que comúnmente se abusa y resulta en código que no tienes la menor idea de lo que hace si no estás familiarizado con los operadores utilizados (y para familiarizarte hay que revisar código o leer documentación para saber qué hacen esos operadores). Y finalmente cosas como los implicits terminan por complicar muchísimo las cosas, ya que a veces te encuentras con código que dice simplemente a.b y revisas la documentación de la clase a la que pertenece a y resulta que NO tiene un método/atributo b; después resulta que por ahí existe una biblioteca aparte que le agrega el método b, como si fuera programación dinámica, pero aquí no te lo esperabas porque se supone que estás lidiando con un sistema de tipado estático.

Respecto de los imports en Ceylon: Es importante mencionar que Ceylon cuenta con su propio sistema de módulos, con el que se manejan dependencias tanto al momento de compilación como en tiempo de ejecución. Esto simplifica mucho las cosas porque no es necesario utilizar herramientas complicadas como Maven (y ni hablar de cosas como Nexus para tener tu propio repositorio; Ceylon cuenta con Herd, su manejador de repositorio bastante más sencillo de utilizar).

Cuando en Ceylon defines tu archivo module.ceylon con algo así:

module mi.modulo '1.0.0' {
  import otro.modulo '0.5';
  shared import otro.mas '0.8'; //esto apenas lo vamos a implementar
}

Al compilar tu código (incluso desde CLI), el CMR (Ceylon Module Resolver - resolucionador de módulos de Ceylon) buscará esos dos módulos otro.modulo y otro.mas en los repositorios que tengas configurados (le puedes incluso indicar uno o varios URL's en la línea de comando al compilador); el CMR busca esos módulos si es que están remotos, los descarga a tu repo local y los hace disponibles al compilador para compilar tu código.

Y al momento de la ejecución es incluso más simple: Yo puedo darte un módulo pequeño en Ceylon y cuando lo ejecutas, el CMR primero busca qué dependencias tiene definidas y carga esos módulos de los repositorios que le hayas definido (de manera recursiva para cargar las dependencias de las dependencias etc) y luego ejecuta tu programa. Eso evita las bronca que siempre tenemos en Java de configurar el classpath a mano.

Hice mención de shared import porque eso indica que si alguien más usa mi.modulo, tendrá disponible también otro.mas sin tener que importarlo en la definición de su módulo.

Imagen de echan

Por lo que veo ninguno

Por lo que veo ninguno ejemplo de scala o groovy en el post parecen mas complejos que ceylon, incluso groovy se ve ligeramente mas limpio. Es mas, estos ejemplos son muy sencillos para evaluar la complejidad entre lenguajes, parece que seria necesario primero definir los criterios para evaluar la complejidad.

Por otro lado Ceylon se me hace mas comparable con Kotlin desde el punto de vista de su proposito y caracteristicas, a diferencia de comparalo con, digamos Clojure. otra vez faltan criterios para comparar.

Imagen de Sr. Negativo

Scala

Me refería a que es más difícil de aprender ,según yo, que a Java.

0_o

Imagen de Jose Manuel

Comparación especifica

Me interesa conocer y saber que procesos se llevan a cabo para determinar el uso de un lenguaje en un proyecto, tengo tres preguntas, ojala alguien me explique. Ya busque algo de información, encontré cosas como: productividad, paradigmas, potencia, etc. Se que la mayoría de lenguajes de programación pueden hacer casi cualquier cosa pero me importa saber como es que ustedes se definen por uno u otro.

  • ¿Alguien utiliza algún método científico/profesional para evaluar que lenguaje usar cuando se presenta un cierto panorama? ¿O eso siquiera existe?
  • Cuando se tienen dos o mas lenguajes de programación que pueden realizar las mismas tareas o tienen capacidades similares ¿como se deciden por cual usar?

¿O sera que se usa el que nos dicen los jefazos ? :P

Saludos.

Imagen de ezamudio

ultimo punto

Pero si usas el que te dicen los jefazos, la pregunta persiste: ¿cómo decidieron los jefazos cuál lenguaje utilizar?

Imagen de alcides

Todo depende

Todo depende de cada proyecto en específico, cuáles son sus objetivos y del entorno del mismo.
Motivo por el cual me temo que no existe una receta única e infalible en este sentido.
Algunos elementos a considerar son:

  • Idoneidad de lenguaje para resolver el/los principales problema(s) del proyecto.
  • Disponibilidad de bibliotecas
  • Disponibilidad de programadores
  • Existencia y calidad de la documentación, cursos, etc

Y bueno también hay factores externos como el caso de que el cliente y/o patrón estén casados con ciertas plataformas y/o productos de ciertas empresas, etc.,etc.
Un ensayo IMHO muy interesante que toca el tema es: http://www.paulgraham.com/icad.html

Saludos.

Imagen de Jose Manuel

A jijo, @ezamudio yo estaba

A jijo, @ezamudio yo estaba pensando en que si el jefe lo decide pues uno ya que hace, solo obedecer y no cuestionar. @alcides Ya me doy una idea mas clara sobre como realizan las elecciones de lenguajes. Gracias por el link al ensayo, este me lo leo con un buen te :)

Saludos.

Imagen de echan

tambien vale la pena echar un

tambien vale la pena echar un ojo los reportes "technology radar" de thoughtworks , tienen un apartado de lenguajes y frameworks.

Imagen de echan

definitivamente tiene

definitivamente tiene elementos mas complejos que java pero vale la pena cuando puedes hacer un stringcalculator en una linea

"//;\n1, 2, \n3".split("\\D").tail.map( i => try{ i.toInt } catch { case _ => 0 } ).foldLeft(0)(_ + _)
Imagen de Sr. Negativo

los lenguajes

@Jose Manuel

Si por lo regular los eligen los jefes. Pero han de tener sus (buenas) razones para hacerlo.

Imagen de ezamudio

Groovy vs. Ceylon, Scala vs. Ceylon

Contestando a echan, no creo que Groovy sea más complicado que Ceylon. La principal diferencia entre ambos es que Groovy es de tipado dinámico mientras que Ceylon es de tipado estático. Cada uno tiene sus ventajas y desventajas.

En cuanto a la diferencia entre Ceylon y Scala, ya mencioné varias cosas en mi primer comentario. Ciertamente los ejemplos de código no son ilustrativos de la complejidad de uno u otro lenguaje porque son extremadamente sencillos. A continuación un ejemplo un poco más ilustrativo, no tanto de la complejidad, sino más bien de la legibilidad:

println(bignum sliding 5 map { _ map { _-48 } product } max) //Scala

//Ceylon
value groups = (0..(s.size-5)).map((Integer p) => s.segment(p, 5));
print(max(groups.map((String s) => s.fold(1, (Integer a, Character c) => a*(c.integer-48)))));

//Otra forma, usando comprensiones:
value groups = { for (p in 0..s.size-5) s.segment(p, 5) };
print(max { for (g in groups) g.fold(1, (Integer a, Character c) a*(c.integer-48)) } );

Lo primero que salta a la vista: el código en Ceylon es bastante más largo. Son dos líneas! En Scala sólo es una! Y la de Scala está padrísima, se lee casi como en inglés! Pero ahora imagínate que aprendiste Scala, leíste un par de libros, tomaste un curso, y llegas a un proyecto real y te encuentras con una línea de código como esa. ¿Cuánto tiempo te vas a tardar en descifrarla? porque ya no se trata de que "se lea como en inglés", sino que encientras lo que hace realmente... por qué max está hasta el final? es un atributo de... lo que sea que devuelve map; cuál map, el primero o el segundo? y eso de product? y el sliding?

Ese método de sliding es muy útil, para este ejercicio de Project Euler. Tal vez alguien lo haya usado en la vida real, no sé. Pero me parece que sus colecciones están llenas de métodos que alguien les ha solicitado una vez y dicen "ah qué buena idea" y lo agregan y al final tienen una colección con un montón de métodos que tal vez alguien necesite un día, pero la mayoría de los programadores simplemente ignoran (al grado de no saber que existen).

En Ceylon los iterables no tienen ese método de sliding; lo tuve que escribir para resolver este problema. Esa es la primera línea. Si agregamos un método sliding a Iterable, ya me ahorro esa línea Wow; inflar el módulo del lenguaje, una de las clases básicas, para que mi solución a un problema de Project Euler pueda ser de una línea en vez de dos... PFFF. Pero bueno, la primera línea es lo mismo que "sliding 5" en el ejemplo en Scala. Y si en Ceylon usamos el operador correspondiente para segment, eso se reduce a s[p:5] si es que te importa muchísimo que la línea sea un poco más corta.

Luego, la segunda línea: de cada grupo, sumar el valor de todos sus dígitos. El método fold recibe como valor inicial el 1 y luego una función anónima que multiplica el valor "a" (inicialmente 1) con el caracter "c" (quitándole 48 porque "0" es ASCII 48). Y finalmente el resultado de eso se pasa a la función max.

Si eres un expertazo en Scala tal vez encuentres este ejemplo perfectamente legible y entendible a simple vista, pero si no... cuánto tiempo le vas a dedicar a entenderlo? Y cuánto tiempo le tienes que dedicar a entender el códido de Ceylon si eres apenas un principiante en el lenguaje? El tiempo dedicado a leer código es muy importante, porque puedes pasar más tiempo leyendo código que escribiéndolo; eso de escribir menos código es bueno al principio y tienes menos código que mantener, etc, pero si en leer una línea de código te vas a tardar más del triple del tiempo que te tardarías en entenderlo si fueran tres líneas, pues no valió tanto la pena, sobre todo si varios programadores leen ese código; el ahorro de tiempo que tuvo el programador que lo escribió ya fue negado por el tiempo invertido por los demás en leerlo.

Eso también es complejidad; la curva de aprendizaje para entender realmente el código escrito en un lenguaje. Y se hace más empinada esa curva cuando la misma solución la puedes escribir de quién sabe cuántas maneras, porque puedes usar punto y coma o no; puedes usar paréntesis o no; puedes definir argumentos o usar los argumentos anónimos; puedes usar el punto para indicar que estás invocando "a.b" o no, etc.

El mismo código en Scala, usando paréntesis y puntos y todo:

println(bignum.sliding(5).map({ _.map({ _-48 }).product }).max)

Es cuestión de gustos; para mi, así se entiende mejor que si ves puros espacios. Me queda clarísimo que sliding es un método de bignum (aunque no sepa qué es bignum) y recibe un entero; al resultado de eso le invoco map y le paso una función que a su vez hace un map de cada argumento, restándole 48, y luego saca el producto de eso (que seguramente es una multiplicación), y al final se saca el máximo valor de la colección. Otra versión, todavía más entendible:

println(bignum.sliding(5).map(d => d.map(c => c-48).product).max)

Ahora se entiende un poco mejor (para mi gusto) porque ya veo que al primer map le paso una función con un parámetro d y veo que a ese d le hago otro map con una función que recibe un parámetro c. Pero... y las llaves? eran necesarias entonces, o no? Unos programadores las van a usar, otros no; y entonces Scala se va volviendo algo que cada quien escribe como quiere, y cuando entras a un proyecto donde usan Scala, al final para ti Scala va a ser el lenguaje que escribía el tipo que escribió la mayoría del código, porque su estilo es el que más ves, pero luego ves el de otra persona y ya no sabes por qué las dos cosas compilan, y al final terminas escribiendo código y compilándolo "a ver si compila", "a ver si está bien". Prueba y error. Muy productivo, PFFF.

Imagen de echan

Conciso

que tal ezamudio ...

estoy de acuerdo en 2 cosas, prueba y error no es la mejor forma de "testear" un software, para eso exiten, bueno .. los frameworks para eso.

El otro punto va de la mano de lo anterior, creo que es un tema de cultura. Desde mi punto de vista Scala y otros lenguajes te dan espacio a la experimentacion y probablemente terminarás con cosas que mas adelante no puedes creer porque las hiciste asi, bueno es parte de la curva. Sin embargo, estoy de acuerdo que el abuso de elementos que no fueron hechos para eso ( notacion infija, aplicacion parcial de funciones) solo por el hecho de hacerlo sin ningun sentido es un tache en el estilo, sobre todo si es un codigo de produccion del que depende la operacion de tu compañia y tu equipo, es decir, habrá que apegarse a las reglas de estilo del equipo y del lugar de trabajo, por otro lado con codigo que te pertenece puedes hacer lo que quieras y es ahi donde prefiero lo conciso y directo de una linea a 5 o más que hacen lo mismo, pero ahi es totalmente personal, hay quienes prefieren leer 1 a leer 5. Tampoco hay que ser hojaldra como para publicar un programa sin una linea de comentario, digo .. eso ayuda a la comprension.

Al final de cuentas no creo que tenga mucho que ver el numero de lineas, sino con la consistencia de lo que escribes y es cierto que Scala en muchas ocasiones es un arma de doble filo donde puedes terminar con librerias como slick, squeryl por decir algunas o cosas indecifrables como la version anterior de dispatch