=========================== Alias e inferencia de tipos =========================== Esta es la séptima parada en el tour de Ceylon. En la pasada entrega introducimos varios tipos de objetos iterables. Ahora es tiempo de explorar el sistema de tipos de Ceylon con más detalle. En este capitulo, vamos a discutir los alias de tipos y la inferencia local de tipo, dos características que te ayudaran a reducir la verbosidad del código tipado estáticamente. -------------- Alias de tipos -------------- Es frecuentemente útil proveer un nombre mas corto o mas semántico a una clase o interfaz existente, especialmente si la clase o la interfaz es un tipo parametrizado. Para esto, utilizamos los alias de tipo. Para definir un alias para una clase o interfaz, usamos la flecha gorda, por ejemplo: .. code-block:: ceylon interface People => set; El alias de una clase deberá declarar sus parámetros formales: .. code-block:: ceylon class People({Person*} people) => ArrayList(people); Si quieres crear un alias para un tipo unión o intersección tienes que usar la palabra reservada alias: .. code-block:: ceylon alias Num => Float|Integer; Un tipo alias puede ser parametrizado, y tener tipos `constraints`, los cuales veremos más adelante: .. code-block:: ceylon class Named(String name, Value val) given Value satisfies Object => Entry(name,val); Los alias nos ayudan a reducir la verbosidad, por que en vez de repetidamente escribir el mismo tipo generico, por ejemplo `Set` podemos usar un alias, tal como `People`, pero en algunos casos Ceylon nos permite omitir completamente el tipo. Un alias de tipo en el `toplevel` o dentro de una clase o interfaz puede ser `shared`. .. code-block:: ceylon shared interface People => set ------------------- Inferencia de tipos ------------------- Hasta es momento, hemos estado especificando el tipo de cada declaración. Esto en general hace código, especialmente códigos de ejemplo, mucho mas fáciles de leer y entender. Sin embargo, Ceylon tienen la habilidad para inferir el tipo de una variable local o el tipo de retorno de un método local. Solamente colocando la palabra reservada `value` en el caso de una variable local o `function` en el caso de un método local en lugar de el tipo de la declaración. .. code-block:: ceylon value polar = Polar(pi, 2.0); value operators = { "+", "-", "*", "/" }; function add(Integer x, Integer y) => x+y; Existen algunas restricciones al aplicar esta característica. Tu no puedes usar `value` o `function`. - para declaraciones anotadas con `shared`, - para declaraciones anotadas con `formal`, - cuando el valor es especificado después en el bloque de la declaración, or - para declarar un parámetro. Estas restricciones significan que las reglas de inferencia de tipo en Ceylon son algo simples. La inferencia de tipos es puramente "derecha-a-izquierda" y "arriba-a-abajo". El tipo de una expresión ya es conocido sin la necesidad de buscar cualquier tipo declarado a la izquierda del especificar =, o mas adelante el bloque de declaración. - El tipo inferido de una referencia declarada `value` es solo el tipo de la expresión asignada a ella usando =. - El tipo inferido de una referencia declarada `value` es solo la unión de los tipos de la expresión devuelta en la declaración `return` del getter. - El tipo inferido de un método declarado `function` es solo la unión de los tipos de la expresión devuelta que aparecen en la declaración `return` del método. ###################################################### Inferencia de tipos al construir expresiones iterables ###################################################### Que hay acerca de construir expresiones iterables como esta: .. code-block:: ceylon value coords = { Polar(0.0, 0.0) , Cartesian(1.0, 2.0) }; ¿Qué tipo es inferido para `coords`? Tal vez tu respuesta sea: {X+} donde x es el superclase común o super interfaz de los tipos de todos los elementos. Pero esto puedo que no sea correcto, desde que puede haber mas de un supertipo común. La respuesta correcta es que la inferencia de tipos es {X*} donde x es la unión de los tipos de todas la expresiones de los elementos. En este caso, el tipo es `{Polar,Cartesian*}`. Ahora, esto es una buena solución, debido a que `Iterable` es covariante en `T`. Así el siguiente código esta bien tipado. .. code-block:: ceylon value coords = { Polar(0.0, 0.0), Cartesian(1.0,2.0) }; //type {Polar|Cartesian+} {Point*} points = coords; Al igual que el siguiente código: .. code-block:: ceylon value nums = { 12.0, 1, -3 }; //type {Float|Integer+} {Numbers+} numbers = nums; ¿Qué hay acerca de los iterables que producen `null`? Bueno, ¿recuerdas que el tipo de `null` es `Null`? .. code-block:: ceylon value string = { null, "Hello", "world" }; //type: {String?+} String? str = strings.first; El tipo de el atributo `first` de `Iterables` es `Element?`. Aquí, tenemos `Iterable`, substituyendo `String?` a `Element`, y así obtenemos el tipo `String??` la cual es `Null|Null|String` que simplemente es `Null|String`, puede ser escrito como `String?`. Por supuesto el compilador puede figurarse que tipo queremos, y puede ser simplificado: .. code-block:: ceylon value string = { null, "hello", "World" }; //type: {String?+} value str = strings.first; //type: String? El mismo trabajo funciona para secuencias: .. code-block:: ceylon value tuple = [null, "Hello","World"]; //type: [Null,String,String] String?[] strings = tuple; value str = strings[0]; //type String? Es interesante que tan útil los tipos unión pueden ser. Incluso si raramente escribes código con declaraciones unión explicitas, ellos aun estarán hay, en el trasfondo, ayudando al compilador a solucionar algunos descabellados y otras veces ambiguos, problemas de tipos. Note que lo que hemos visto es realmente solo un caso especial de el algoritmo que Ceylon usa para inferencia de argumentos para tipos `generic`, y todo lo anterior también funciona para tipos `generic` escritos por el usuario al igual que lo hace para `Iterable`. ##################################### Clases anónimas e inferencia de tipos ##################################### Desde que una clase anónima no tiene un nombre, Ceylon remplaza clases anónimas con la intersección de sus supertipos cuando lleva acabo la inferencia de tipos. .. code-block:: ceylon interface Foo {} interface Bar {} object foobar satisfies Foo&Bar {} value fb = foobar; //inferred type Basic&Foo&Bar value fbs = { foobar, foobar }; // inferred type {Basic&Foo&bar+} ########### Aun hay mas ########### En lo siguiente, vamos a explorar mas detalles de el sistema de tipos, comenzando con `tipos unión, intersección, enumerated y switching`. Entonces, despues de ello estaremos listos para discutir `tipos generic`.