Commits

Miguel Gordian committed 19f0cd8

Agregar: Tema generics

Se agrega la novena entrega que trata sobre generics en Ceylon.

Se necesita revisar la semántica y buscar la mejor interpretación
para algunos anglicismos.

Se necesitan cambiar todas el texto que se encuentre entre acentos graves simples
a acentos graves dobles, para así dar vista de literales de rst.

  • Participants
  • Parent commits 5083adb

Comments (0)

Files changed (2)

source/generics.rst

+========
+Generics
+========
+
+Esta es la novena parte de el tour de Ceylon. El la parada anterior cubrimos
+os tipos unión, intersección y enumerados. En esta parte vamos a ver acerca de
+los tipos genéricos.
+
+Herencia y subtipos son poderosas herramientas para la abstracción sobre los 
+tipos. Pero esta herramienta tiene sus limitantes. No nos ayuda a expresar
+contenedores de tipos genéricos como colecciones. Para este problema necesitamos
+tipos parametrizados. Ya hemos visto muchos tipos parametrizados - por ejemplo,
+iterables, secuencias y tuplas - pero ahora vamos a explorar esto a detalle.
+
+--------------------------
+Definiendo tipos genéricos
+--------------------------
+
+Programar con tipos genéricos es una de las partes mas difíciles en Java. Esto
+es aun verdad, en cierta parte en Ceylon. Pero debido a que el lenguaje Ceylon
+y el SDK fueron diseñados para generics desde cero, Ceylon esta diseñado para
+aliviar demasiados aspectos dolorosos de modelo de Java.
+
+Justo como en Java, únicamente tipos y métodos puede declarar parámetros de tipo.
+También como en Java, los parámetros de tipo son antes que los parámetros 
+ordinarios, y encerrados entre corchetes angulares.
+
+.. code-block:: ceylon
+    
+    shared interface Iterator<out Element> { ... }
+
+.. code-block:: ceylon
+    
+    shared class Singleton<out Element>(Element element)
+            extends Object()
+            satisfies [Element+]
+            given Element satisfies Object { ... }
+
+.. code-block:: ceylon
+    
+    shared Value sum<Value>({Value+} values)
+            given Value satisfies Summable<Value> { ... }
+
+.. code-block:: ceylon
+    
+    shared <key->Item>[] zip<Key,Item>({Key*} keys, {Item*} items)
+            given Key satisifes Object
+            given Item satisfies Object { ... }
+ 
+Como puedes ver, la convención en Ceylon es usar nombres significativos para 
+los parámetros de tipo (en otros lenguajes de programación la convención es
+usar letras).
+
+Un parámetro de tipo puede tener un parámetro por defecto.
+
+.. code-block:: ceylon
+    
+    shared interface Iterable<out Element, out Absent=Null> ...
+
+
+-------------------
+Argumentos de tipo
+-------------------
+
+A diferencia de Java, siempre necesitamos especificar el parámetro tipo en la
+declaración de tipo (No hay tipos ``raw`` en Ceylon. El siguiente código no 
+compilara:
+
+.. code-block:: ceylon
+    
+    Iterator it = ...; //error: missing type agmuente to parameter Element of Iterable
+
+En vez de ello, necesitamos proveer el argumento tipo como esto:
+
+.. code-block:: ceylon
+    
+    Iterator<String> it = ...;
+
+Por otro lado, no necesitamos explícitamente especificar el tipo de los argumentos en 
+muchas invocaciones de métodos o instanciaciones de clases. No necesitamos usualmente
+tener que escribir esto:
+
+.. code-block:: ceylon
+    
+    Array<String> strings = array<String> { "Hello", "World" };
+    {<Integer->String>*} things = entries<String>(strings);
+
+En vez de ello, es posible inferir los tipos de los argumentos desde argumentos 
+ordinarios.
+
+.. code-block:: ceylon
+    
+    value string = array { "Hello", "World" }; //tipo: Array<String>
+    value things = entries(strings); //tipo: Iterable<Entry<Integer,String>>
+
+El algoritmo de inferencia de tipos generic esta ligeramente involucrado, así que
+deberás referirte a las especificaciones del lenguaje para una definición completa.
+Pero esencialmente lo que pasa es que Ceylon infiere el tipo de un argumento
+combinando los tipos de los correspondientes argumentos usando unión en el 
+caso de un parámetro de tipo covariante o intersección en el caso de un parámetro de
+tipo contravariante.
+
+.. code-block:: ceylon
+    
+    value points = array { Polar(pi/4, 0.5), Cartesian(-1.0, 2.5) }; // tipo: Array<Polar|Cartesian>
+    value entries = entries(points); //tipo: Entries<Integer,Polar|Cartesian>
+
+Si un parámetro de tipo tiene un argumentos por defecto, esta permitido dejarlo fuera
+cuando subministremos la lista de argumentos de tipo. Entonces ``Iterable<String>`` significa
+``Iterable<String,Null>``.
+
+
+---------------------------
+Covarianza y contravarianza
+---------------------------
+
+Ceylon elimina una de la partes de los generics de Java que hacían 
+realmente difíciles las cosas: tipos ``wildcard``. Los tipos ``wildcard``
+fueron la solución al problema de covarianza en un sistema de tipos 
+genérico en Java. Conozcamos la idea de covarianza, y entonces podremos
+ver como trabaja la covarianza en Ceylon.
+
+Esto comienza con la intuitiva expectación de que una colección de 
+``geeks`` es una colección de ``Person``. Esta es una intuición razonable,
+pero si la colección mutan, esto cambiara a ser incorrecto.
+Consideremos la siguiente posible definición de ``Collection``:
+
+.. code-block:: ceylon
+    
+    interface Collection<Element> {
+        shared formal Iterator<Element> iterator();
+        shared formal void add(Element x);
+
+Y vamos a suponer que ``Geek`` es un subtipo de ``Person``.
+La expectación intuitiva es que el siguiente código deberá de funcionar:
+
+.. code-block:: ceylon
+    
+    Collection<Geek> geeks = ... ;
+    Collection<Person> people = geeks; //compile error
+    for (person in people) { ... }
+
+Este código es, francamente, perfectamente razonable tomado enserio. Aun en
+ambos Ceylon y Java, este código resulta en un error en tiempo de compilación
+en la segunda linea, donde ``Collection<Geek>`` es asignado a una ``collection<Person>``.
+¿Por qué? Bueno, debido a que si permitimos la asignación, el siguiente código
+deberá también compilar:
+
+.. code-block:: ceylon
+    
+    Collection<Geek> geeks = ...;
+    Collection<Person> people = geeks; //compile error
+    people.add( Person("Fonzie") );
+
+¡No podemos permitir que el código de ``Fonzie`` sea un ``Geek``!
+
+En otras palabras, diremos que ``Collection`` es invariante en ``Element``.
+O, cuando no estemos tratando de impresiones gente con terminología confusa,
+podremos decir que ``Collection`` producen ambos a través del métodos 
+``iterator()`` y consume a través del método ``add()`` el tipo ``Element``.
+
+Aquí es donde Java queda fuera y se dirige a abajo por el agujero del conejo,
+exitosamente usando ``wildcards`` para disputar un tipo covariante o contravariante
+de un tipo invariante, pero también exitosamente dejando confusos a todos.
+No vamos a seguir a Java hasta el fondo del agujero.
+
+En vez, vamos a refactorizar ``Collection`` en una interfaz puramente ``Producer`` y 
+en una puramente ``Consumer``:
+
+.. code-block:: ceylon
+    
+    interface Producer<out Output> {
+        shared formal Iterator<Output> iterator();
+    }
+    interface Consumer<in Input> {
+        shared formal void add(Input x);
+    }
+
+Note que hemos anotado los parámetros de tipo de estas interfaces.
+
+- La anotación ``out`` especifica que ``Producer`` es covariante en ``Output``
+  Esto es que produce una instancia de ``Output``, pero nunca consume
+  instancias de ``Output``.
+- El anotación ``in`` especifica que ``Consumer`` es una contravariante de 
+  ``Input``. Esto es que consume una instancia ``Input``, pero nunca produce una
+  instancia de ``Input``.
+
+El compilador de Ceylon valida el esquemas de la declaración del tipo y se asegura
+que la anotaciones de varianza estén satisfechas. Si tratas de declarar un método
+``add()`` en ``Producer`` dara como resultado un error de compilación. Si tratas
+de declarar  un método ``iterate()`` en ``Consumers`` obtendrás en error de compilación
+similar.
+
+Ahora, veamos que sacamos de esto:
+
+- Desde que ``Producer`` es covariante en su parámetro de tipo ``output``, y desde
+  que ``Geek`` es un subtipo de ``Person``, Ceylon nos permite asignar 
+  ``Producer<Geek>`` a ``Producer<Person>``.
+- Además, desde que ``Consumer`` es una contravariante en su parámetro de tipo 
+  ``Input``, y desde que geek es un subtipo de ``Person``, Ceylon nos permite 
+  asignar ``Consumer<Person>`` a ``Consumer<Geek>``.
+
+Y así poder definir nuestra interfaz ``Collection`` como una mezcla de ``Producer``
+con ``Consumer``.
+
+.. code-block:: ceylon
+    
+    interface Collection<Element>
+            satisfies Producer<Element> & Consumer<Element> {}
+
+
+Note que ``Collection`` permanece invariante en ``Element``. Si tratamos de agregar 
+una anotación de varianza a ``Element`` en ``Collection`` dará como resultado un 
+error de compilación, debido a que la anotación deberá contradecir la anotación
+de varianza de cualquiera ``Producer`` o ``Consumer``.
+
+Ahora el siguiente código finalmente compila:
+
+.. code-block:: ceylon
+    
+    Collection<Geek> geeks = ...;
+    Producer<Person> people = geeks;
+    for (person in people) { ... }
+
+El cual coincide con nuestra intuición original.
+
+El siguiente código también compila:
+
+.. code-block:: ceylon
+    
+    Collection<Person> people = ...;
+    Consumer<Geek> geekConsumer = people;
+    geekConsumer.add( Geek("James") );
+
+Que es también intuitivamente correcto - "James" deberá ser una persona.
+
+Hay dos elementos adicionales a la definición de covarianza y contravariaza:
+
+- ``Producer<Anything>`` es un supertiopo de ``Producer<T>`` para cualquier tipo T, y
+- ``Consumer<Nothing>`` es un supertipo de ``Consumer<T>`` para cualquier tipo T.
+
+Estas invariantes pueden ser útiles si necesitas abstraer todos los ``Producers`` o
+todos los ``Consumers``. (Nota, sin embargo, si ``Producer`` declaro restricciones
+obligatorias para tipos en ``Output``, entonces ``Producer<Anything>`` no deberá ser
+un tipo legal.)
+
+No gastaras mucho tiempo escribiendo tus propias colecciones, desde que Ceylon SDK
+deberá próximamente tener un poderoso framework para construir colecciones. Pero
+aun deberás apreciar el enfoque de Ceylon a la convarianza como un usuario de 
+los tipos colección incorporados.
+
+####################################################
+Covarianza y contravarianza con unión e intersección
+####################################################
+
+Hay un conjunto de relaciones interesantes que surge cuando introducimos 
+los tipos unión e intersección en la pintura.
+
+Primero, consideremos un tipo covariante como ``List<Element>``. Entonces 
+para cualquier tipo ``X`` y ``Y``:
+
+- ``List<X>|List<Y>`` es un subtipo de ``List<X|Y>``, y
+- ``List<X>&List<Y>`` es un supertipo de ``List<X&Y>``.
+
+Después, consideremos un tipo contravariante como ``Consumer<Element>``. 
+Entonces para cualquier tipo ``X`` y ``Y``:
+
+- ``Consumer<X>|Consumer<Y>`` es un subtipo de ``Consumer<X&Y>``, y
+- ``Consumer<X>&Consumer<Y>`` es un supertipo de ``Consumer<X|Y>``.
+
+Esto es valioso volveremos a esta sección mas adelante, y trataremos 
+de desarrollar alguna intuición acerca del por que estas relaciones son
+correctas y que significan. No gastes tu tiempo en esto por ahora. 
+Tenemos cosas mas importantes que hacer.
+
+###################
+Generics y herencia
+###################
+
+Considere las siguientes clases:
+
+.. code-block:: ceylon
+    
+    class LinkedList()
+            satisfies List<Object> { ... }
+
+    class LinkedStringList() 
+            extends LinkedList()
+            satisfies List<String> { ... }
+
+Este tipo de herencia es ilegal en Java. Una clase no puede heredar
+el mismo tipo mas de una vez, con diferentes argumentos de tipo. Podemos
+decir que Java suporta únicamente ``single instantiation inheretance``.
+
+Ceylon es menos restrictivo en este aspecto. El código anterior es 
+perfectamente legal si (y solo si) la interfaz ``List<Element>`` es
+covariante en sus parámetros de tipo ``Element``, que es, declarado
+como esto:
+
+.. code-block:: ceylon
+    
+    inteface List<out Element> { ... }
+
+Diremos que Ceylon cuenta con ``principal instantiation inheritance``. 
+Incluso el siguiente código es legal:
+
+.. code-block:: ceylon
+    
+    interface ListOfSomething satisfies List<Something> { }
+    interface ListOfSomthingElse satisfies List<SomethingElse> {}
+    class MyList() satisfies ListOfSomething & ListOfSomethingElse { ... }
+
+Entonces el siguiente código es lagal y bien tipado:
+
+.. code-block:: ceylon
+    
+    List<Something&SomethingElse> list = MyList()
+
+Por favor hagamos una pausa aquí, y toma tu tiempo para notar que tan 
+ridículamente impresionante es esto. Nosotros nunca mencionamos 
+explícitamente que ``MyList()`` fue una ``List<Something&SomethingElse>``.
+El compilador solo lo dedujo por nosotros.
+
+Note que cuando heredaste el mismo tipo mas de una vez, tu tal vez necesites 
+refinar algunos de sus miembros, en orden para satisfacer todas las firmas
+heredadas. No te preocupes el compilador te lo notificara y te obligara a 
+hacerlo.
+
+##############################
+Restricciones en tipos generic 
+##############################
+
+Es muy común, cuando estamos escribiendo un tipo parametrizado, queremos 
+invocar un método o evaluar un atributo a instancias del parámetro de tipo.
+Por ejemplo, si estamos escribiendo un tipo parametrizado ``Set<Element>``,
+necesitamos ser capaces de comparar instancias de ``Element`` usando ``==``
+para ver si cierta instancia de ``Element`` es contenida en el ``Set``.
+Desde que ``==`` esta definido para expresiones de tipo ``Object`` 
+necesitamos alguna manera de asegurar que ``Element`` es un subtipo de 
+``Object``. Este es un ejemplo de una *restricción de tipo* - de hecho,
+este es un ejemplo del caso mas común de restricción de tipo, un 
+*upper bound*.
+
+.. code-block:: ceylon
+    
+    shared class Set<out Element>(Element* elements)
+            given Element satisifes Object {
+
+        ...
+
+        shared Boolean contains(Object obj) {
+            if (is Element obj){
+                return obj in bucket(obj.hash);
+            }
+            else {
+                return false;
+            }
+
+Un argumento de tipo a ``Element`` deberá ser un subtipo de ``Object``.
+
+.. code-block:: ceylon
+    
+    Set<String> set1 = Set("C", "Java", "Ceylon"); //ok
+    Set<String?> set2 = Set("C", "Java", "Ceylon", null); //compile error
+
+En Ceylon, un parámetro de tipo genérico es considerado un tipo propio, así
+una restricción de tipo luce mas a una declaración de una clase o interfaz.
+Esta es otra forma en la que Ceylon es mas regular que otros lenguajes 
+parecidos a C.
+
+En futuras versiones de Ceylon, después de la 1.0, también introduciremos soporte 
+para varios tipos adicionales de restricciones de tipos genéricos. Podrás encontrar
+mas detalles en las especificaciones del lenguaje.
+
+######################################
+Tipos genéricos totalmente cosificados
+######################################
+
+La causa principal de muchos problemas cuando trabajamos con tipos genéricos en
+Java es el borrado de tipos. Los parámetros y argumentos de tipo son descartados
+por el compilador y simplemente no están disponibles en tiempo de ejecución.
+Así el siguiente, perfectamente sensible, fragmento de código no deberá compilar
+en Java;
+
+.. code-block:: ceylon
+    
+    if (is List<Person> list) { ... }
+    if (is Element obj) { ... }
+
+(Donde elemento es un parámetro de tipo genérico.)
+
+El sistema de tipos de Ceylon ha cosificado los argumentos de tipo genérico, Como
+Java, el compilador de Ceylon lleva acabo limpieza, descartando parámetros de tipo
+desde el esquema  de el tipo genérico. En la plataforma de JavaScript, los tipos 
+son descartados cuando se produce el código de JavaScript. Pero a diferencia
+de Java, los parámetros de tipo son cosificados(disponibles en tiempo de 
+ejecución). Los tipos son incluso cosificados cuando se ejecutan en una
+máquina virtual de Java.
+
+Así los fragmentos de código anteriores compilan y funciones como se esperan
+en ambas plataformas. Una vez hemos terminado de implementar el metamodelo, 
+incluso podrás seras capaz usar reflexión para para descubrir el argumento
+de tipo de una instancia de un tipo genérico.
+
+Ahora por supuesto, argumentos de tipos genéricos no son revisada para seguridad 
+de tipos a nivel de la máquina virtual cuando se esta ejecutando, pero esto 
+no es estrictamente necesario desde que el compilador desde que el compilarlo 
+ya ha revisado la solidez del código.
+
+**Nota de implementación**
+En el release M5 no hemos tenido tiempo de implementar algunas optimizaciones
+importantes relacionadas a genéricos cosificados. Entonces, tal vez 
+experimentes algunos problemas usando tipos genéricos en este release. 
+No te preocupes que resolveremos estos problemas al tiempo en que lancemos 
+la versión de Ceylon 1.0. (¡Por favor haz nos saber tus experiencias!)
+
+###########
+Aun hay mas
+###########
+
+Ahora estamos listos para mirar una característica muy importante de Ceylon:
+``modularidad``.
+
     iterables_secuencias_tuplas
     aliasTipos_inferenciaTipo
     union_interseccion_enumerated
+    generics
 
 Para alguna critica o sugerencia puedes contactarme a 
 `@ilcapitanozoek <https://twitter.com/ilcapitanozoek>`_ en twitter.