SlideShare una empresa de Scribd logo
1 de 242
Descargar para leer sin conexión
UNIVERSIDAD DE MÁLAGA
ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA
INFORMÁTICA
INGENIERO EN INFORMÁTICA
PROGRAMACIÓN FUNCIONAL EN SCALA
(FUNCTIONAL PROGRAMMING IN SCALA)
Realizado por
RUBÉN PÉREZ LUJANO
Dirigido por
JOSÉ ENRIQUE GALLARDO RUIZ
Departamento
LENGUAJES Y CIENCIAS DE LA COMPUTACIÓN
MÁLAGA, SEPTIEMBRE 2016
Dedicado a
la memoria de mi padre
Agradecimientos
Después de recorrer un largo camino ha llegado el momento de parar, coger aire, mirar hacia
atrás un instante y dar las gracias a todas esas personas que en algún momento han formado
parte del mismo, que han compartido los mejores momentos, que me han ayudado a mirar hacia
delante a pesar de las adversidades y junto a las que me gustaría continuar recorriendo el camino
de la vida.
Quisiera comenzar dando las gracias a D. José Enrique Gallardo Ruiz, tutor del proyecto,
por su comprensión, dedicación y por el gran esfuerzo realizado, así como a D. Blas Carlos
Ruiz Jiménez, un gran profesor y la persona con la que comencé mi proyecto.
Gracias a Irene, mi mujer, por saber sacarme una sonrisa en esos días grises, por tenderme la
mano y ayudar a levantarme en los días más oscuros y por darme ánimos para continuar cuando
más duro se hacía el camino. Gracias por hacerme el hombre más feliz del mundo. Gracias por
apostar por mí. Tú siempre has sido y serás mi apuesta.
Gracias a Carmen, mi madre, por los valores que me ha transmitido, por todo lo que me ha
enseñado durante la vida y por creer en mí hasta el final. Gracias por esa canción inolvidable.
Gracias a Lidia, mi hermana, por su cariño, su ayuda, sus bromas y por confiar ciegamente
en mí. Gracias por todos y cada uno de los momentos que hemos vivido.
Gracias a mis titos, Juan y Paqui, por ofrecer siempre todo su apoyo y demostrarme que
siempre podré contar con ellos.
Gracias a mis amigos Nacho y Jesús, con los que he compartido algunos de los mejores
momentos de este camino. Gracias por vuestros consejos, por esas tardes de risas en el salón.
Gracias a Dña. Lidia Fuentes por ofrecerme esa beca en el momento que más lo necesitaba
y a Dña. Mariam Cobaleda por todos los buenos consejos que me dio.
Gracias a todos los miembros de los centros de día para personas mayores de Estepona y
Coín por acogerme en vuestra familia y por todos los buenos momentos que compartimos.
Para finalizar, aunque no los mencione de una forma explícita, quiero dar las gracias a mis
compañeros de universidad y a mis compañeros de piso.
A todos, eternamente agradecido.
Introducción
La elección de un lenguaje para introducir la programación a los alumnos de las actuales
ingenierías en informática es una decisión trascendental; esta elección está ligada a la pregunta:
¿qué características se deben exigir a un “primer” lenguaje para describir de forma limpia y
sencilla los conceptos de la programación?
Hoy en día es comúnmente aceptado entre los profesionales de la enseñanza (y entre los alum-
nos) que hay dos paradigmas esenciales que simplifican los conceptos de la programación, y
que debe conocer un futuro informático: el funcional y el orientado a objetos. Sin embargo es
difícil encontrar un lenguaje que integre ambos paradigmas de forma sencilla si se parte de la
base de que tal lenguaje será el primer contacto de un estudiante con la programación. Además
de esto, se quiere una buena elección desde el punto de vista del programador profesional; es
decir, tal lenguaje debe facilitar de forma “natural” el aprendizaje de los principales lenguajes
con los que se enfrentará el futuro informático profesional.
Entre la oferta actual de lenguajes hay uno que cada vez toma más adeptos en el mundo edu-
cativo: el lenguaje Scala. Scala es un lenguaje de programación multiparadigma diseñado para
expresar patrones comunes de programación en forma concisa, elegante y con tipos seguros.
Integra sutilmente características de lenguajes funcionales y orientados a objetos. La imple-
mentación actual corre en la máquina virtual de Java y es compatible con las aplicaciones Java
existentes; por ello el uso de Scala como un primer lenguaje será un puente importante con el
mundo de la programación profesional.
El trabajo en Scala surge a partir de un esfuerzo de investigación para desarrollar un mejor
soporte de los lenguajes de programación para la composición de software. Hay dos hipótesis
que se desea validar con el experimento Scala. Primera, se postula que un lenguaje de progra-
mación para la composición de software necesita ser escalable en el sentido de que los mismos
conceptos pueden describir tanto partes pequeñas como grandes. Por tanto, los autores se han
concentrado en los mecanismos para la abstracción, composición y descomposición en vez de
añadir un conjunto grande de primitivas que pueden ser útiles para los componentes a algún
nivel de escala, pero no a otro nivel. Segundo, se postula, que el soporte escalable para los
componentes puede ser previsto por un lenguaje de programación que unifica y generaliza la
programación orientada a objetos y la funcional. Para los lenguajes con tipos estáticos, de los
que Scala es un ejemplo, estos dos paradigmas estaban hasta ahora en gran medida separados.
El principal objetivo de este PFC es desarrollar un material didáctico a modo de una guía
donde el programador (tanto el alumno como el profesor) pueda ver de forma clara y conci-
sa, tras una primera toma de contacto con el lenguaje, las ventajas de utilizar un lenguaje de
programación multiparadigma como Scala en la resolución de los diferentes problemas que se
puedan plantear.
En el Capítulo 1: Scala « página 1 » se realiza una breve introducción a Scala, se presenta
el lenguaje y se analizan conceptos básicos de los lenguajes de programación como los tipos de
datos básicos, los operadores, las estructuras de control o la evaluación en Scala.
Página I
II
En el Capítulo 2: Programación Orientada a Objetos en Scala « página 31 » se realiza un
análisis de Scala como un lenguaje orientado a objetos puro, se presenta la jerarquía de clases
y conceptos como polimorfismo, genericidad, acotación de tipos y varianza en Scala.
En las primeras secciones del Capítulo 3: Programación Funcional en Scala « página 53 »
se describe el paradigma funcional utilizando Scala y se enseñan conceptos de la programación
a través del estilo funcional. Posteriormente, se detalla la implementación en Scala de las es-
tructuras básicas de la programación, aprovechando dichas estructuras para el aprendizaje de
la programación funcional. Para finalizar el capítulo se hace un repaso por las colecciones que
Scala ofrece al programador.
Es conocido que es posible unificar los estilos imperativos y orientado a objetos con el fun-
cional puro a través de mónadas, pero el uso de éstas no es apropiado para un curso introductorio
a la programación. Después de haber estudiado previamente los aspectos fundamentales de la
programación funcional, en el Capítulo 4: Programación Funcional Avanzada en Scala « página
129 » se analizan conceptos más complejos de este paradigma, como la programación monádica
a través de las mónadas más populares del lenguaje.
En el Capítulo 5: Tests en Scala « página 155 » se presentan brevemente algunas de las
soluciones más populares para realizar pruebas al código realizado en Scala.
Scala propone una solución basada en el paso asíncrono de mensajes inmutables para resol-
ver la problemática de la concurrencia. El modelo de actores de Scala, así como la biblioteca
Akka, se presentan en el Capítulo 6: Concurrencia en Scala. Modelo de actores « página 165 ».
En el Capítulo 7: Conclusiones « página 185 » se justifica razonadamente el uso de Scala
como lenguaje de programación adecuado para ser utilizado dentro del ámbito de la docencia.
Finalmente, en el Capítulo 8: Solución a los ejercicios propuestos « página 189 » se pueden
encontrar las soluciones de los ejercicios propuestos a lo largo de la guía.
Índice general
1. Scala 1
1.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.1. Scala. Un lenguaje escalable . . . . . . . . . . . . . . . . . . . . . . . 2
1.1.2. Paradigmas de la programación . . . . . . . . . . . . . . . . . . . . . 2
1.1.2.1. Scala. Un lenguaje multiparadigma . . . . . . . . . . . . . . 3
1.1.3. Preparación del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.3.1. Descargar Scala . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.3.2. Herramientas de Scala . . . . . . . . . . . . . . . . . . . . . 3
El compilador de Scala: scalac . . . . . . . . . . . . . . . . . . 3
El intérprete de código: scala . . . . . . . . . . . . . . . . . . . 4
Scala como lenguaje compilado . . . . . . . . . . . . . . . . . 4
Scala como lenguaje interpretado desde un script . . . . . . . . 5
Scala como lenguaje interpretado desde un intérprete . . . . . . 5
1.2. Conceptos básicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2.1. Elementos de un lenguaje de programación . . . . . . . . . . . . . . . 6
1.2.2. Elementos básicos en Scala . . . . . . . . . . . . . . . . . . . . . . . 6
1.2.2.1. Tipos de datos básicos en Scala . . . . . . . . . . . . . . . . 6
1.2.2.2. Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Operadores de igualdad. . . . . . . . . . . . . . . . . . . . . . 15
1.2.2.3. Nombrar expresiones . . . . . . . . . . . . . . . . . . . . . 15
1.2.2.4. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.2.3. Uso del carácter punto y coma (;) en Scala . . . . . . . . . . . . . . . . 17
1.3. Bloques en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.3.1. Visibilidad y bloques en Scala . . . . . . . . . . . . . . . . . . . . . . 18
1.4. Evaluación en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.4.1. Evaluación de expresiones . . . . . . . . . . . . . . . . . . . . . . . . 18
1.4.2. Evaluación de funciones . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.4.3. Sistema de Evaluación de Scala . . . . . . . . . . . . . . . . . . . . . 19
1.4.3.1. Valores de las definiciones . . . . . . . . . . . . . . . . . . . 19
1.4.3.2. Evaluación de Booleanos . . . . . . . . . . . . . . . . . . . 19
1.4.4. Ámbito y visibilidad de las variables . . . . . . . . . . . . . . . . . . . 20
1.5. Estructuras de control en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.5.1. Estructuras condicionales . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.5.1.1. La sentencia if . . . . . . . . . . . . . . . . . . . . . . . . . 21
La sentencia if / else . . . . . . . . . . . . . . . . . . . . . . . 22
La sentencia if...else if ...else . . . . . . . . . . . . . . . . . 22
Estructuras condicionales anidadas . . . . . . . . . . . . . . . . 23
1.5.2. Estructuras iterativas . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Página III
IV ÍNDICE GENERAL
1.5.2.1. Bucles while . . . . . . . . . . . . . . . . . . . . . . . . . . 24
1.5.2.2. Bucles do...while . . . . . . . . . . . . . . . . . . . . . . . 24
1.5.2.3. Bucles for . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Bucles for con rangos . . . . . . . . . . . . . . . . . . . . . . . 25
Bucles for con colecciones . . . . . . . . . . . . . . . . . . . . 26
Bucles for con filtros . . . . . . . . . . . . . . . . . . . . . . . 27
Bucles for con yield. . . . . . . . . . . . . . . . . . . . . . . . 28
1.6. Interacción con Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
1.6.1. Ejecución sobre la JVM . . . . . . . . . . . . . . . . . . . . . . . . . 29
1.7. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2. Programación Orientada a Objetos en Scala 31
2.1. Introducción a la programación orientada a objetos en Scala . . . . . . . . . . 31
2.1.1. Características principales de la programación orientada a objetos . . . 31
2.1.2. Scala como lenguaje orientado a objetos . . . . . . . . . . . . . . . . . 31
2.2. Paquetes, clases, objetos y namespaces . . . . . . . . . . . . . . . . . . . . . . 32
2.2.1. Objetos Singleton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.2.2. Módulos, objetos, paquetes y namespaces . . . . . . . . . . . . . . . . 32
2.2.3. Clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.2.4. Objetos funcionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.2.4.1. Constructores . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.2.4.2. Sobrescritura de métodos . . . . . . . . . . . . . . . . . . . 35
2.2.4.3. Precondiciones . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.2.4.4. Atributos y Métodos . . . . . . . . . . . . . . . . . . . . . . 36
2.2.4.5. Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.3. Jerarquía de clases en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.3.1. Herencia en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.3.1.1. Rasgos y herencia múltiple en Scala . . . . . . . . . . . . . 39
2.3.1.2. Funcionamiento de los rasgos . . . . . . . . . . . . . . . . . 39
2.3.1.3. Rasgos como modificaciones apiladas . . . . . . . . . . . . 40
2.3.1.4. ¿Cuándo usar rasgos? . . . . . . . . . . . . . . . . . . . . . 42
2.4. Patrones y clases case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
2.4.1. Clases case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
2.4.2. Patrones: estructuras y tipos . . . . . . . . . . . . . . . . . . . . . . . 43
2.4.2.1. Patrones comodín . . . . . . . . . . . . . . . . . . . . . . . 43
2.4.2.2. Patrones constantes . . . . . . . . . . . . . . . . . . . . . . 43
2.4.2.3. Patrones variables . . . . . . . . . . . . . . . . . . . . . . . 44
2.4.2.4. Patrones constructores . . . . . . . . . . . . . . . . . . . . . 44
2.4.2.5. Patrones de secuencia . . . . . . . . . . . . . . . . . . . . . 44
2.4.2.6. Patrones tipados . . . . . . . . . . . . . . . . . . . . . . . . 45
2.5. Polimorfismo en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.5.1. Acotación de tipos y varianza . . . . . . . . . . . . . . . . . . . . . . 47
2.5.1.1. Acotación de tipos . . . . . . . . . . . . . . . . . . . . . . . 47
2.5.1.2. Varianza . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
ÍNDICE GENERAL V
3. Programación Funcional en Scala 53
3.1. Introducción a la programación funcional . . . . . . . . . . . . . . . . . . . . 53
3.1.1. Características de los Lenguajes de Programación Funcionales . . . . . 53
3.1.2. Scala como lenguaje funcional . . . . . . . . . . . . . . . . . . . . . . 54
3.1.3. ¿Por qué la programación funcional? . . . . . . . . . . . . . . . . . . 54
3.2. Sentido estricto y amplio de la programación funcional . . . . . . . . . . . . . 54
3.2.1. ¿Qué son las funciones puras? . . . . . . . . . . . . . . . . . . . . . . 55
3.3. Funciones y cierres en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.3.1. Definición de funciones . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.3.2. Funciones anidadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
3.3.3. Diferencias entre métodos y funciones . . . . . . . . . . . . . . . . . . 57
3.3.4. Funciones de primera clase . . . . . . . . . . . . . . . . . . . . . . . . 58
3.3.5. Funciones anónimas y funciones valor . . . . . . . . . . . . . . . . . . 58
3.3.6. Cierres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.4. Recursión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.4.1. Importancia de la pila del sistema en recursión. . . . . . . . . . . . . . 60
3.4.1.1. La pila de Java . . . . . . . . . . . . . . . . . . . . . . . . . 61
3.4.1.2. Contexto de pila . . . . . . . . . . . . . . . . . . . . . . . . 61
3.4.2. Recursión de cola . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
3.5. Currificación y Parcialización . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
3.5.1. Currificacion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
3.5.2. Parcialización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
3.6. Orden Superior . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
3.6.1. Funciones de orden superior . . . . . . . . . . . . . . . . . . . . . . . 65
3.7. Funciones polimórficas. Genericidad . . . . . . . . . . . . . . . . . . . . . . . 66
3.8. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
3.8.1. Ejercicio Resuelto . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
3.8.2. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
3.9. Programación funcional estricta y perezosa . . . . . . . . . . . . . . . . . . . 76
3.9.1. Funciones estrictas y no estrictas . . . . . . . . . . . . . . . . . . . . . 76
3.10. Estructuras de Datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
3.10.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
3.10.1.1. ¿Qué es una teoría?. Definición de Estructuras de Datos . . . 78
3.10.1.2. La abstracción en la programación . . . . . . . . . . . . . . 78
3.10.1.3. Datos, Tipos de Datos, Estructuras de Datos y Tipos Abstrac-
tos de Datos . . . . . . . . . . . . . . . . . . . . . . . . . . 79
3.10.2. Definición de Estructuras de Datos en Lenguajes Funcionales . . . . . 80
3.10.2.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . 80
3.10.2.2. Definición . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
3.10.2.3. Los Naturales . . . . . . . . . . . . . . . . . . . . . . . . . 82
Ejercicios resueltos. . . . . . . . . . . . . . . . . . . . . . . . 86
3.10.3. Estructuras de datos lineales. Listas . . . . . . . . . . . . . . . . . . . 89
3.10.3.1. TAD Lista . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
3.10.3.2. Ejercicios sobre el TAD Lista . . . . . . . . . . . . . . . . . 94
3.10.4. Estructuras de datos no lineales . . . . . . . . . . . . . . . . . . . . . 95
3.10.4.1. Árboles . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
3.10.4.2. Arboles Binarios . . . . . . . . . . . . . . . . . . . . . . . . 97
3.10.4.3. Arboles Binarios de Búsqueda . . . . . . . . . . . . . . . . 99
VI ÍNDICE GENERAL
3.10.5. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
3.11. Colecciones en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
3.11.1. El paquete scala.collection . . . . . . . . . . . . . . . . . . . . . . . . 103
3.11.2. Iteradores en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
3.11.2.1. Métodos definidos para el tipo Iterator en Scala. . . . . . . . 105
3.11.3. Colecciones inmutables . . . . . . . . . . . . . . . . . . . . . . . . . 107
3.11.3.1. Definición de rangos en Scala. La clase Range. . . . . . . . . 107
Métodos definidos para el tipo Range en Scala. . . . . . . . . . 108
3.11.3.2. Definición de tuplas en Scala. La clase Tuple . . . . . . . . . 108
3.11.3.3. Listas en Scala. La clase List . . . . . . . . . . . . . . . . . 110
Métodos definidos para el tipo List en Scala. . . . . . . . . . . 112
Ejercicios sobre listas . . . . . . . . . . . . . . . . . . . . . . . 112
3.11.3.4. Vectores en Scala. La clase Vector . . . . . . . . . . . . . . 114
3.11.3.5. Flujos en Scala. La clase Stream . . . . . . . . . . . . . . . 115
3.11.3.6. Conjuntos en Scala. La clase Set . . . . . . . . . . . . . . . 116
Recorriendo conjuntos . . . . . . . . . . . . . . . . . . . . . . 117
Métodos definidos para el tipo Set en Scala. . . . . . . . . . . . 118
3.11.3.7. Asociaciones en Scala. La clase Map . . . . . . . . . . . . . 119
Métodos definidos para el tipo Map en Scala. . . . . . . . . . . 120
3.11.3.8. Selección de una colección . . . . . . . . . . . . . . . . . . 120
3.11.3.9. Colecciones como funciones . . . . . . . . . . . . . . . . . 121
3.11.4. Expresiones for como una combinación elegante de funciones de orden
superior . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
3.11.4.1. Traducción de expresiones for con un generador . . . . . . . 122
3.11.4.2. Traducción de expresiones for con un generador y un filtro . 123
3.11.4.3. Traducción de expresiones for con dos generadores . . . . . 123
3.11.4.4. Traducción de bucles for . . . . . . . . . . . . . . . . . . . 124
3.11.4.5. Definición de map, flatMap y filter con expresiones for . . . 125
3.11.4.6. Uso generalizado de for en estructuras de datos . . . . . . . 125
3.11.5. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
4. Programación Funcional Avanzada en Scala 129
4.1. Implícitos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
4.1.1. Parámetros ímplicitos en funciones . . . . . . . . . . . . . . . . . . . 129
4.1.2. Clases implícitas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
4.2. Tipos en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
4.2.1. Definición de tipos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
4.2.2. Parámetros de tipo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
4.2.2.1. Nombres de los parámetros de tipo. . . . . . . . . . . . . . . 132
4.2.3. Constructores de tipos. . . . . . . . . . . . . . . . . . . . . . . . . . . 133
4.2.4. Tipos compuestos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
4.2.5. Tipos estructurales . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
4.2.6. Tipos de orden superior. . . . . . . . . . . . . . . . . . . . . . . . . . 134
4.2.7. Tipos existenciales . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
4.3. Teoría de categorías . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
4.3.1. El patrón funcional Funtor . . . . . . . . . . . . . . . . . . . . . . . . 137
4.3.2. El patrón funcional Mónada . . . . . . . . . . . . . . . . . . . . . . . 138
4.3.2.1. Reglas que deben satisfacer las mónadas . . . . . . . . . . . 139
ÍNDICE GENERAL VII
4.3.2.2. Importancia de las propiedades de las mónadas en las expre-
siones for . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
4.3.2.3. Map en las mónadas . . . . . . . . . . . . . . . . . . . . . . 141
4.3.2.4. La importancia de las mónadas . . . . . . . . . . . . . . . . 142
4.3.2.5. La mónada Identidad . . . . . . . . . . . . . . . . . . . . . 142
4.3.2.6. Envolviendo el contexto con mónadas. La clase monádica Try 143
La mónada Try. . . . . . . . . . . . . . . . . . . . . . . . . . . 146
4.4. Manejo de errores sin usar excepciones . . . . . . . . . . . . . . . . . . . . . 149
4.4.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
4.4.2. Tipo de datos Option . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
4.4.3. Tipo de datos Either . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
4.5. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
5. Tests en Scala 155
5.1. Afirmaciones Asserts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
5.1.1. Assert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
5.1.2. Ensuring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
5.2. Herramientas específicas para tests . . . . . . . . . . . . . . . . . . . . . . . . 159
5.2.1. ScalaTest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
5.3. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
6. Concurrencia en Scala. Modelo de actores 165
6.1. Programación Concurrente. Problemática . . . . . . . . . . . . . . . . . . . . 165
6.1.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
6.1.1.1. Sistema Reactivo Vs Sistema Transformacional . . . . . . . 165
6.1.2. Speed-Up en programación concurrente . . . . . . . . . . . . . . . . . 166
6.1.3. Problemática . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
6.1.3.1. Propiedades de los programas concurrentes . . . . . . . . . . 166
6.1.3.2. Bloqueos y secciones críticas . . . . . . . . . . . . . . . . . 167
Problemas del uso de bloqueos . . . . . . . . . . . . . . . . . . 167
6.1.3.3. Concurrencia en Java . . . . . . . . . . . . . . . . . . . . . 167
6.2. Modelo de actores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
6.2.1. Origen del Modelo de Actores . . . . . . . . . . . . . . . . . . . . . . 168
6.2.2. Filosofía del Modelo de Actores . . . . . . . . . . . . . . . . . . . . . 169
6.3. Actores en Scala. Librería scala.actors . . . . . . . . . . . . . . . . . . . . . . 170
6.3.1. Definición de actores . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
6.3.2. Estado de los actores . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
6.3.3. Mejora del rendimiento con react . . . . . . . . . . . . . . . . . . . . 173
6.4. Actores en Scala con Akka . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
6.4.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
6.4.1.1. Diferencias entre Akka y la librería Actors de Scala. . . . . . 175
6.4.2. Definición y estado de los actores . . . . . . . . . . . . . . . . . . . . 177
6.5. Buenas prácticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
6.5.1. Ausencia de bloqueos . . . . . . . . . . . . . . . . . . . . . . . . . . 182
6.5.2. Comunicación exclusiva mediante mensajes . . . . . . . . . . . . . . . 182
6.5.3. Mensajes inmutables . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
6.5.4. Mensajes autocontenidos . . . . . . . . . . . . . . . . . . . . . . . . . 182
6.6. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
VIII ÍNDICE GENERAL
7. Conclusiones 185
8. Solución a los ejercicios propuestos 189
8.1. Evaluación en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
8.2. Introducción a la Programación Funcional . . . . . . . . . . . . . . . . . . . . 189
8.3. Estructuras de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
8.3.1. TAD Lista . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
8.3.2. TAD Arbol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
8.4. Colecciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
8.4.1. Tipo List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
8.4.2. Otras colecciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
8.5. Programación Funcional Avanzada . . . . . . . . . . . . . . . . . . . . . . . . 204
8.6. Tests en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
8.7. Concurrencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
Lista de tablas 210
Listados de algoritmos 211
Referencias 219
Glosario 221
Acrónimos 225
Capítulo 1
Scala
1.1. Introducción
El nombre Scala significa: diseñado para crecer con la demanda de sus usuarios (Scala-
ble language). Scala es un lenguaje de programación multi-paradigma diseñado para expresar
patrones comunes de programación de forma concisa, elegante y con tipos estáticos. Integra
elegantemente características de lenguajes funcionales y orientados a objetos, lo cual hace que
la escalabilidad sea una de las principales características del lenguaje. La implementación ac-
tual corre en la máquina virtual de Java y es compatible con las aplicaciones existentes en Java.
[7]
Su creador, Martin Odersky1
, y su equipo comenzaron el desarrollo de este nuevo lengua-
je de código abierto en el año 2001, en el laboratorio de métodos de programación en École
Polytechnique Fédérale de Lausanne (EPFL).
Scala hizo su aparición pública sobre la plataforma Máquina Virtual de Java (JVM) en enero
de 2004 y unos meses después haría lo propio sobre la plataforma .NET.
Aunque se trata de un elemento relativamente novedoso dentro del espacio de los lenguajes
de programación, ha adquirido una notable popularidad y las ventajas que ofrece han hecho ya
que cada vez más empresas apuesten por Scala ( Twitter, Linked-in, Foursquare, The Guardian,
...). Las características de Scala le hacen un lenguaje ideal para ser utilizado en centros de
computación, centros de hosting,..., en los que las aplicaciones se ejecutan de forma parale-
la en los supercomputadores, servidores,...que los conforman. Además, Scala es uno de los
lenguajes de programación que se utilizan para desarrollar aplicaciones para los dispositivos
Android.
“ Si le das a una persona un pescado, comerá un día. Si enseñas a pescar a una
persona, comerá toda su vida. Si das herramientas a una persona, puede construirse
1
Martin Odersky es profesor de la EPFL en Lausanne, Suiza y ha trabajado en los lenguajes de programación
durante la mayor parte de su carrera. Estudió programación estructurada y orientada a objetos como estudiante
de doctorado de Niklaus Wirth. Después, mientras trabajaba en IBM y en la Universidad de Yale se enamoró de
la programación funcional. Cuando apareció Java, comenzó a añadir construcciones de programación funcionales
para la nueva plataforma, lo cual dio lugar a Pizza y GJ y eventualmente a Java 5 con los genéricos. Durante ese
tiempo también desarrolló javac, el compilador de referencia actual para Java.
En los últimos 10 años, Martin ha centrado su trabajo en la unificación de los paradigmas de programación
funcional y programación orientada a objetos en el lenguaje Scala. Scala pasó rápidamente del laboratorio de
investigación a convertirse en una herramienta de código abierto y en un lenguaje industrial. Odersky ahora es el
encargado de supervisar el desarrollo de Scala como jefe del grupo de programación de la EPFL y como presidente
de la empresa Typesafe.
Página 1
una caña de pescar, ¡y muchas otras herramientas!. Incluso podrá construir una
máquina para fabricar más cañas y así podrá ayudar a otras personas a pescar.
Ahora debemos conceptualizar el diseño de un lenguaje de programación co-
mo un patrón para diseñar lenguajes de programación, como una herramienta para
hacer más herramientas del mismo tipo.
El diseño de un lenguaje de programación debe ser un patrón, un patrón de
crecimiento, un patrón para desarrollar el patrón para la definición de patrones que
los programadores puedan usar en su trabajo y para alcanzar su objetivo.” [1]
1.1.1. Scala. Un lenguaje escalable
Uno de los principales objetivos del diseño de Scala es la construcción de un lenguaje que
permita el crecimiento y la escalabilidad en función de las exigencias del desarrollador. Scala
puede ser utilizado como lenguaje de scripting, así como también se puede adoptar en el proceso
de construcción de aplicaciones empresariales. La conjunción de su abstracción de componen-
tes, su sintaxis reducida, el soporte para la programación orientada a objetos y la programación
funcional han contribuido a que el lenguaje sea más escalable y haga de la escalabilidad una de
sus principales características.
1.1.2. Paradigmas de la programación
Dentro de la programación se pueden distinguir tres paradigmas:
Programación imperativa.
Programación lógica.
Programación funcional.
La programación imperativa pura está limitada por “el cuello de botella de Von Neuman”,
término que fue acuñado por John Backus en su conferencia de la concesión del Premio Turing
por la Asociación para la Maquinaria Computacional (ACM) en 1977. Según Backus:
“Seguramente debe haber una manera menos primitiva de realizar grandes cam-
bios en la memoria, que empujando tantas palabras hacia un lado y otro del cuello
de botella de Von Neumann. No sólo es un cuello de botella para el tráfico de datos,
sino que, más importante, es un cuello de botella intelectual que nos ha mantenido
atados al pensamiento de “una palabra a la vez” en lugar de fomentarnos el pensar
en unidades conceptuales mayores. Entonces la programación es básicamente la
planificación del enorme tráfico de palabras que cruzan el cuello de botella de Von
Neumann, y gran parte de ese tráfico no concierne a los propios datos, sino a dónde
encontrar éstos”
En la actualidad, la programación funcional y la programación orientada a objetos (POO)2
se preocupan mucho menos de “empujar un gran número de palabras hacia un lado y otro” que
otros lenguajes anteriores (como por ejemplo Fortran), pero internamente, esto sigue siendo
2
El paradigma de la POO se considera ortogonal a los paradigmas de programación funcional, programación
imperativa y programación lógica.
Página 2
lo que hacen durante gran parte del tiempo los computadores, incluso los supercomputadores
altamente paralelos3
.[26]
Del cuello de botella intelectual criticado por Backus subyace la necesidad de disponer de
otras técnicas para definir abstracciones de alto nivel como son los conjuntos, los polinomios,
las formas geométricas, las cadenas de caracteres, los documentos,...para lo que idealmente se
deberían de desarrollar teorías de conjuntos, formas, cadenas de caracteres, etc.
1.1.2.1. Scala. Un lenguaje multiparadigma
Scala ha sido el primero en incorporar y unificar la programación funcional y la programa-
ción orientada a objetos en un lenguaje estáticamente tipado, donde la clase de cada instancia
se conoce en tiempo de compilación, así como la disponibilidad de cualquier método de una
instancia dada. La pregunta es: ¿por qué se necesita más de un estilo de programación?.
El objetivo principal de la computación multiparadigma es ofrecer un determinado conjunto
de mecanismos de resolución de problemas de modo que los desarrolladores puedan seleccionar
la técnica que mejor se adapte a las características del problema que se está tratando de resolver.
1.1.3. Preparación del sistema
1.1.3.1. Descargar Scala
Para disponer de la última versión de Scala sólo habrá que acceder a la sección “Download”
en la web oficial: http://www.scala-lang.org/download/
Para trabajar con Scala, sólo será necesario un editor de texto y un terminal.
También es posible trabajar con un Entorno de Desarrollo Integrado (IDE), ya que existen
plugins para Eclipse, IntelliJ IDEA o Netbeans.4
1.1.3.2. Herramientas de Scala
Las herramientas de línea de comandos de Scala se encuentran dentro de la carpeta bin de
la instalación.
El compilador de Scala: scalac
El compilador scalac transforma un programa de Scala en archivos .class que podrán ser
utilizados por la JVM. El nombre del archivo no tiene que corresponder con el nombre de la
clase.
Sintaxis: scalac [opciones ...] [archivos fuente ...]
Opciones:
La opción -classpath (-cp) indica al compilador donde encontrar los archivos fuente
La opción -d indica al compilador donde colocar los archivos .class
La opción -target indica al compilador qué versión de máquina virtual usar
3
Un estudio de referencia de base de datos, realizado a partir de 1996, encontró que tres de cada cuatro ciclos
de la unidad central de procesamiento (CPU) se dedican a la espera de memoria.
4
En los próximos capítulos se usará tanto el intérprete de Scala como el IDE Eclipse junto con el plugin
ScalaIDE disponible en la web http://scala-ide.org/.
Página 3
El intérprete de código: scala
La instrucción scala puede operar en tres modos:
Puede ejecutar clases compiladas
Puede ejecutar archivos fuente (scripts – Secuencia de instrucciones almacenadas en un
fichero que se ejecutan normalmente de forma interpretada. –)
Puede operar en forma interactiva, es decir como intérprete
Sintaxis: scala [opciones ...] [script u objeto] [argumentos]
Si no se especifica un script o un objeto, la herramienta funciona como un evaluador de
expresiones interactivo. En cambio, si se especifica un script, el comando scala lo compilará y
lo ejecutará. Si se especifica el nombre de una clase, scala la ejecuta.
Algunos comandos importantes:
:help muestra mensaje de ayuda
:quit termina la ejecución del intérprete
:load carga comandos de un archivo
Scala como lenguaje compilado
Como primer ejemplo, se puede observar la definición del programa estándar “Hola mun-
do”.
1 object HolaMundo {
2 def main(args: Array[String]) {
3 println("Hola, mundo!")
4 }
5 }
Algoritmo 1.1: Hola Mundo
La estructura de este programa debería resultar familiar para los lectores que ya conozcan Java.
Consiste de un método llamado main que toma los argumentos de la línea de comandos (un
vector, array en inglés, de objetos del tipo String) como parámetro. El cuerpo de este método
presenta una sola llamada al método predefinido println con el saludo como argumento. El
método main no devuelve un valor, por lo tanto no es necesario que se declare un tipo retorno.
Lo que es menos familiar es la declaración de objetos que contienen al método main. Esta
declaración introduce lo que es comúnmente conocido como singleton objects (objeto single-
ton), que es una clase con una sola instancia. Por lo tanto, dicha construcción declara tanto una
clase llamada HolaMundo, como una instancia de esa clase también llamada HolaMundo. Esta
instancia es creada bajo demanda, es decir, la primera vez que es utilizada.
Se puede advertir que el método main no está declarado como estático. Esto es así porque
los miembros estáticos (métodos o campos) no existen en Scala. En vez de definir miembros
estáticos, en Scala se declararán estos miembros en un objeto singleton.
Compilando el ejemplo
Para compilar el ejemplo utilizaremos scalac, el compilador de Scala. El comando scalac
funciona como la mayoría de los compiladores: toma un archivo fuente como argumento, al-
gunas opciones y produce uno o varios archivos objeto. Los archivos objeto que produce son
archivos class para la JVM.
Página 4
Si guardamos el programa anterior en un archivo llamado HolaMundo.scala, podemos com-
pilarlo ejecutando el siguiente comando:
$ scalac HolaMundo.scala
Esto generará algunos archivos class en el directorio actual. Uno de ellos se llamará Hola-
Mundo.class y contiene una clase que puede ser directamente ejecutada utilizando el comando
scala, como mostramos en la siguiente sección.
Ejecutando el ejemplo
Una vez compilado, un programa Scala puede ser ejecutado utilizando el comando scala. Su
uso es muy similar al comando java utilizado para ejecutar programas Java, y acepta las mismas
opciones. El ejemplo de arriba puede ser ejecutado utilizando el siguiente comando que, como
se puede comprobar, produce la salida esperada:
$ scala HolaMundo
Hola, mundo!
Scala como lenguaje interpretado desde un script
Como ya se ha introducido anteriormente, es posible ejecutar archivos fuente haciendo uso
del comando scala. A continuación se muestra como crear un script básico llamado hola.scala:
1 println("Hola mundo, desde un script!")
Ejecutando el ejemplo desde la línea de comandos se comprueba que el resultado obtenido
es el esperado:
$>scala hola.scala
Hola mundo, desde un script!
Desde la línea de comandos se le pueden pasar argumentos a los scripts mediante el vector
de argumentos args. En Scala, el acceso a los elementos de un vector se realiza especificando
el índice entre paréntesis (no entre corchetes como en Java)5
. Se ha definido el siguiente script,
llamado holaarg.scala, con el objetivo de probar el funcionamiento del paso de argumentos
desde la línea de comandos:
1 println("Hola, "+ args(0) +"!")
Si se ejecuta:
$> scala holaarg.scala pepe
En este comando, “pepe” se pasa como argumento desde la línea de comandos, el cual se
accede desde el script mediante args(0). La salida obtenida es la que se esperaba:
Hola, pepe!
Scala como lenguaje interpretado desde un intérprete
Para lanzar el intérprete de Scala, se tiene que abrir una ventana de terminal y teclear scala.
Aparecerá el prompt del intérprete de Scala a la espera de recibir expresiones. Funciona, al igual
que el intérprete de Scheme o Haskell, mediante el Bucle Leer-Evaluar-Imprimir (REPL).
El intérprete de Scala será muy utilizado durante el capítulo dedicado a la programación
funcional, por lo que se recomienda familiarizarse con el mismo.
5
Notación que es homogénea para las demás estructuras, incluso las estructuras definidas por el usuario.
Página 5
1.2. Conceptos básicos
1.2.1. Elementos de un lenguaje de programación
Un lenguaje de programación debe proveer:
expresiones primitivas que representen los elementos más simples
maneras de combinar expresiones
maneras de abstraer expresiones, lo cual introducirá un nombre para esta expresión y nos
permitirá hacer referencia a la expresión.
1.2.2. Elementos básicos en Scala
1.2.2.1. Tipos de datos básicos en Scala
En Scala se pueden encontrar los mismos tipos de datos básicos que en Java. El tamaño que
ocupa cada uno de ellos en memoria, así como la precisión de los tipos primitivos de Scala,
también se corresponden con los de Java. Aunque se hable de tipos de datos, en Scala todos los
tipos de datos son clases. En la tabla 1.1 se muestran los tipos básicos en Scala.
Tipo de dato Tamaño Rango Ejemplo
Byte 8 bits con signo [−128, 127] 38
Short 16 bits con signo [−32768, 32767] 23
Int 32 bits con signo [−231
, 231
− 1] 45
Long 64 bits con signo [−263
, 263
− 1] 3434115
Float 32 bits con signo [−3,4028 ∗ 1038
, 3,4028 ∗ 1038
1.38
Double 64 bits con signo [−1,7977 ∗ 10308
, 1,7977 ∗ 10308
] 54.37
Boolean true o false true
Char 16 bits con signo [0, 216
− 1] ’F’
String secuencia de caracteres Cadena de caracteres "hola mundo!"
Tabla 1.1: Tipos de datos primitivos y tamaño en Scala
Los tipos Byte, Short, Int y Long reciben el nombre de tipos enteros. Los tipos enteros junto
con los tipos Float y Double son llamados tipos numéricos.
Excepto String, que es del paquete java.lang, el resto se encuentran en el paquete Scala.
Todos se importan automáticamente.
Literales de tipos básicos en Scala
Las reglas sobre literales que usa Scala son bastante simples e intuitivas. A continuación se
verán las principales características de los literales básicos en Scala.
Literales enteros
Los mayoría de literales enteros que utilicemos serán de tipo Int. Los literales enteros
también podrán ser de tipo Long añadiendo el sufijo L o l al final del literal. A continuación se
muestran algunos ejemplos:
Página 6
scala> 5
res0: Int = 5
scala> 777L
res3: Long = 777
scala> 0xFFAFAFA5
res2: Int = -5263451
scala> 0777L
<console>:1: error: Non-zero integral values may not have a leading zero.
0777L
^
Los literales enteros no podrán tener el cero como primer dígito6
, excepto el entero 0 y
aquellos representados en notación hexadecimal. Los enteros representados en notación hexa-
decimal comienzan por 0x o 0X, pudiendo estar seguido por dígitos entre 0 y 9 o letras entre A
y F (en mayúscula o minúscula).
Literales en punto flotante
Los literales en punto flotante serán del tipo de datos Float cuando se añada el sufijo F o
f. En otro caso serán de tipo Double. Estos literales sí podrán contener uno o varios ceros como
primeros dígitos.
scala> 0.0
res0: Double = 0.0
scala> 01.2
res1: Double = 1.2
scala> 01.2F
res2: Float = 1.2
scala> 00045.34
res3: Double = 45.34
Literales lógicos
Los literales lógicos o booleanos son aquellos que pertenecen a la clase Boolean y que sólo
pueden tener uno de los dos valores booleanos: true o false.
Literales de tipo símbolo
Aunque el tipo de datos Symbol (scala.Symbol) no se considera un tipo básico de Scala,
merece la pena tenerlo en cuenta. Los símbolos serán cadenas de caracteres no vacías precedidas
del prefijo ’. La clase case Symbol está definida de la siguiente forma:
1 package scala
2 final case class Symbol private (name: String) {
3 override def toString: String = "’" + name
4 }
Ejemplo:
6
En versiones anteriores de Scala, cuando un entero comenzaba por cero era porque el número estaba en base
8 y, por tanto, sólo podía estar seguido de los dígitos comprendidos entre 0 y 7.
Página 7
scala> ’miSimbolo
res4: Symbol = ’miSimbolo
Literales de tipo carácter
Un literal de tipo carácter consiste en un carácter encerrado entre comillas simples que
representará un carácter representable o un carácter de escape7
. El tipo de datos de los literales
de tipo carácter es Char. El estándar de codificación de caracteres utilizado para representar los
caracteres es Unicode. También podremos indicar el código unicode del carácter que queramos
representar encerrado entre comillas simples. Veamos algunos ejemplos:
scala> ’u0050’
res5: Char = P
scala> ’S’
res6: Char = S
scala> ’n’
res7: Char =
Literales de tipo cadena de caracteres
Un literal del tipo cadena de caracteres será una secuencia de caracteres encerradas entre
comillas dobles cuyo tipo de datos será String. Los caracteres que conformen la cadena de
caracteres podrán ser tanto caracteres representables, como caracteres de escape. Por ejemplo:
scala> "Hola Mundo!"
res8: String = Hola Mundo!
scala> "Hola " Mundo!"
res9: String = Hola " Mundo!
Los literales de tipo cadena de caracteres también pueden ser multilínea en cuyo ca-
so estarán encerrados entre tres comillas dobles. En este caso, la cadena de caracteres podrá
estar formada por los caracteres representables, caracteres de escape o por caracteres no repre-
sentables como salto de línea o cualquier otro carácter especial como comillas dobles, barra
inversa,...con la única salvedad de que sólo podrá haber tres o más comillas dobles al final.
Veamos algún ejemplo:
scala> """y dijo:
| "mi nombre es Bond, James Bond"
| ///"""
res10: String =
y dijo:
"mi nombre es Bond, James Bond"
///
Caracteres de escape
Los caracteres de escape que se muestran en la tabla 1.2 son reconocidos tanto en los
literales de caracteres como en los literales de cadenas de caracteres.
7
También conocidos como secuencia de escape
Página 8
Carácter de escape Unicode Descripción
b u0008 Retroceso BS
t u0009 Tabulador horizontal HT
n u000A Salto de línea LF
f u000C Salto de página FF
r u000D Retorno de carro CR
" u0022 Comillas dobles
’ u0027 Comilla simple
 u005c Barra inversa
Tabla 1.2: Caracteres de escape reconocidos por Char y String
En versiones anteriores de Scala era posible definir cualquier carácter que tuviera un código
unicode entre 0 y 255, indicando el código en base octal. Esto se indicaba precediendo al código
octal de . Por ejemplo 150 representaría el carácter h. Esta representación está depreciada en
la actualidad, siendo sustituida por la representación en base hexadecimal de los caracteres.
scala> "150"
<console>:1: warning: Octal escape literals are deprecated, use u0068 instead.
"150"
^
res11: String = h
scala> "u0068"
res12: String = h
Expresiones de tipos básicos en Scala
Las expresiones en Scala pueden estar formadas por:
Un literal válido de cada uno de los tipo de datos básicos vistos en la tabla 1.1. Por
ejemplo: 1, 2.5, 3.74E10f, true, false, ’a’...
Un operador 8
junto con dos operandos (operadores binarios) o un operando (operadores
unarios) del tipo de datos compatible con el operador.
El último valor evaluado en un bloque (en la llamada a una función o método).
Expresiones aritméticas en Scala
Las expresiones aritméticas son aquellas que, tras ser evaluadas, devuelven un valor nu-
mérico. Los tipos de datos Byte, Short, Int y Long serán utilizados para representar valores
numéricos de tipo entero. Con los tipos de datos Float y Double se representarán valores de
tipo real.
El tipo de datos devuelto por defecto al evaluar una expresión que represente un número
entero es Int mientras que Double será el tipo de datos por defecto devuelto al evaluar una
expresión que represente un número real.
scala> 1.2
res0: Double = 1.2
scala> 5
res1: Int = 5
8
Los operadores se verán en la Subsubsección 1.2.2.2: Operadores « página 10 »
Página 9
Expresiones booleanas
Una expresión booleana puede estar compuesta por un literal, por uno de los operadores
lógicos mostrados en la tabla 1.6 o ser el valor devuelto por las operaciones usuales de com-
paración que se muestran en la tabla 1.5 de operadores relacionales en Scala. Una expresión
booleana también puede ser el resultado de la evaluación de cualquier expresión booleana co-
mo, por ejemplo, el resultado de una función o método.
1.2.2.2. Operadores
Los operadores se utilizarán para combinar expresiones de los tipos de datos básicos. En
Scala los operadores son en realidad métodos y, por tanto, se reducen a la llamada a un método
de un objeto de una de clases de los tipos de datos básicos. Es decir, 1 + 2 realmente invoca
al método + del objeto 1 con el parámetro 2: (1).+(2). Es más, en la clase Int hay diferentes
definiciones del método + (método sobrecargado) que difieren las unas de las otras en el tipo
del parámetro con el que se invocan y que nos permiten realizar la suma de objetos de diferentes
tipos enteros. Por ejemplo, existe otro método + en la clase Int que recibe como parámetro un
objeto de tipo Long y devuelve otro objeto de tipo Long.
Los operadores se pueden clasificar, atendiendo a su notación en:
Operadores infijos. Son operadores binarios en los que el método que se va a invocar
se ubica entre sus dos operandos. Un ejemplo de operador infijo podría ser el operador
aritmético + de los tipos numéricos.
Operadores prefijos. Son operadores unarios en los que el nombre del método se sitúa
delante del objeto que invocará al método. Como por ejemplo: !b, -5,...
Operadores postfijos9
. Son aquellos operadores unarios en los que el nombre del método
se sitúa detrás del objeto que invocará al método. Por ejemplo: 5 abs, 2345 toLong.
Infijos, Prefijos y Postfijos
En Scala los operadores no tienen una sintaxis especial10
, como ocurre en otros lenguajes
de programación como Haskell, por lo que cualquier método puede ser un operador. Lo que
convertirá un método en un operador será la forma de usar el mismo. Por ejemplo, si se escribe
1.+(2), + no será un operador. Pero si se escribe 1 + 2, + sí será un operador.
Existe la posibilidad de definir nuevos operadores prefijos definiendo métodos que comien-
cen por unary_ y estén seguidos por un identificador válido. Por ejemplo, si en un tipo de datos
se define un método unary_!, una instancia de este tipo de datos podrá invocar ! en notación
prefija. Scala transforma las llamadas a operadores prefijos en llamadas al método unary_. Por
ejemplo, la expresión !p es transformada por Scala en la invocación de p.unary_!.
Los operadores postfijos son métodos que no reciben parámetros y, por convenio, no es
necesario el uso de paréntesis11
9
Los operadores postfijos tienen que ser habilitados -visibles- importando scala.language.postfixOps o indican-
do al compilador la opción -language:postfixOps
10
Excepto los operadores prefijos en los que los identificadores que pueden ser usados en este tipo de operadores
son +, -, ! y ~.
11
Excepto si el método presenta efectos colaterales, como println(), en cuyo caso sí habrá que poner los parén-
tesis.
Página 10
Los operadores infijos son aquellos métodos que reciben un argumento. Los operadores
infijos se ubican entre sus dos operandos (como el operador +, en el que el primer operando
será el objeto que invoca al método y el segundo operando es el argumento que recibe).
Prioridad y Asociatividad de los operadores
Cuando en una expresión aparecen varios operadores, la prioridad de los operadores nos
indicará el orden de evaluación de las diferentes partes que componen la expresión, es decir, qué
partes de la expresión son evaluadas antes. Por ejemplo, el resultado de evaluar la expresión 100
- 40 * 2 es 20, no 120, ya que el operador * tiene mayor prioridad que el operador +. El resultado
de la anterior expresión es el mismo que si se evalúa la expresión 100 - (40 * 2). Si se quisiera
cambiar el orden de evaluación anterior se debería escribir la expresión: (100 - 40) * 2, cuyo
resultado sería 120.
Scala determina la prioridad de los operadores basándose en el primer carácter de los méto-
dos usados con notación de operador. La excepción a esta regla es el operador de menor priori-
dad en Scala: el operador de asignación (los operadores que terminan con el carácter ’=’). Así,
con la excepción ya comentada de los operadores de asignación, la precedencia de operadores
se determinará según el primer carácter, tal como se muestra en la tabla 1.3, donde encontramos
la prioridad de los operadores básicos, de forma que los operadores de mayor prioridad se en-
cuentran en la parte superior de la tabla y los operadores de menor prioridad en la parte inferior.
La prioridad de cualquier operador definido por el usuario se definirá por el primer carácter
empleando según esta misma tabla.
Tipo Operador Asociatividad
Postfijos (), [],... Izquierda
Unarios ! ˆ Derecha
Multiplicativos * / % Izquierda
Aditivos + - Izquierda
Binario : Izquierda
Binario = ! Izquierda
Desplazamiento >> >>> << Izquierda
Relación <><= >= Izquierda
Igualdad == != Izquierda
Bit a bit AND & Izquierda
Bit a bit XOR ˆ Izquierda
Bit a bit OR | Izquierda
AND lógico && Izquierda
OR lógico || Izquierda
Todas las letras Izquierda
Asignación = += -= *= /= %= >>= <<= &= ˆ= |= Derecha
Coma , Izquierda
Tabla 1.3: Prioridad y asociatividad de los operadores
Cuando se encuentran en una expresión varios operadores con la misma prioridad, será
otra de las características de los operadores, la asociatividad, la encargada de indicar cómo
se agrupan los operadores y, por tanto, como se evaluará la expresión. La asociatividad de un
operador en Scala se determina por el último carácter del operador. Si el último carácter de
Página 11
un operador binario es :, el operador tendrá asociatividad derecha, lo que quiere decir que
los métodos que terminen en : serán invocados por el operando situado en el lado derecho del
operador y se les pasará como parámetro el operando izquierdo. Es decir, si se tiene la expresión
x /: y, será equivalente a y./:(x). En cualquier otro caso, el operador presentará asociatividad
izquierda.
La asociatividad de un operador no influye en el orden de evaluación de los operandos,
que siempre será de izquierda a derecha. En la anterior expresión, x/:y, primero se evaluará el
operando x y después el operando y. Es decir, la expresión es tratada como el siguiente bloque:
1 {val t=x;y./:(x)}
Como se ha dicho anteriormente, la asociatividad determinará como se agrupan los opera-
dores en caso de que aparezcan en una expresión varios operadores con la misma prioridad. Si
los métodos terminan en ’:’, se agruparán de derecha a izquierda, mientras que en cualquier otro
caso se agruparán de izquierda a derecha. Si se tienen las expresiones a * b * c y x /: y /: z, se
evaluarán como (a * b) * c y x /: (y /: z), respectivamente. A pesar de conocer las reglas que
determinan la prioridad de los operadores y la asociatividad de los mismos, es aconsejable usar
paréntesis para aclarar cuales son los operadores que actúan sobre cada una de las expresiones.
Operadores aritméticos
Los operadores aritméticos son aquellos que operan con expresiones enteras o reales, es
decir, con objetos de tipo Short, Int, Double o Long. En Scala se pueden distinguir dos tipos de
operadores aritméticos: unarios y binarios. En la tabla 1.4 se muestran los diferentes operadores
aritméticos presentes en Scala.
Descripción Operador Tipo
Signo positivo + unario
Signo negativo - unario
Suma + binario
Resta - binario
División / binario
Producto * binario
Resto % binario
Tabla 1.4: Operadores aritméticos en Scala
Todos los operadores admiten expresiones enteras y reales. En el caso de los operadores
binarios, si los dos operandos son enteros o reales el resultado de la operación será un valor
entero o real respectivamente. Si uno de sus operandos es entero y el otro real entonces el
resultado devuelto será de tipo real.
Los operadores aritméticos también servirán para combinar expresiones de tipo carácter. En
este caso, Scala tomará el valor decimal del código unicode que represente el carácter de cada
uno de los operadores.
scala> 1.5 * 3
res2: Double = 4.5
scala> 5 * 3
res3: Int = 15
Página 12
Operadores relacionales
El uso de operadores relacionales permite comparar expresiones de tipos de datos com-
patibles, devolviendo un resultado de tipo booleano: true o false. Scala soporta los operadores
relacionales que se muestran en la tabla 1.5. Los operadores relacionales son binarios.
Descripción Operador
Menor <
Menor/Igual <=
Mayor >
Mayor/Igual >=
Distinto !=
Igual ==
Tabla 1.5: Operadores relacionales en Scala
Como se puede apreciar en el siguiente ejemplo, los resultados obtenidos después de com-
parar diferentes expresiones numéricas (reales o enteras) son los esperados:
scala> 12.0 * 3 == 3 * 12
res4: Boolean = true
scala> 3.0f < 7
res5: Boolean = true
scala> "cadena" == "cadena"
res6: Boolean = true
En cambio, si lo que pretende es comparar dos expresiones booleanas se deberá tener en
cuenta que el valor false se considera menor que el valor true.
scala> 13<10 < (11==11.0)
res7: Boolean = true
Cuando se comparen expresiones del tipo de datos Char o String se deberá tener en cuenta
que se basan en el valor decimal del código unicode de cada carácter12
. En el caso de expresiones
del tipo de datos String, cuando los caracteres situados en la primera posición de las cadenas que
se estén comparando sean iguales (mismo código unicode) se comparará el segundo carácter de
ambas cadenas y así sucesivamente hasta que se encuentre el primer par de caracteres distintos,
ubicados en la misma posición en ambas cadenas, o hasta que la cadena cuya longitud sea
menor se termine, en cuyo caso la cadena de menor longitud será menor que la cadena de mayor
longitud. A continuación se muestran unos ejemplos en el intérprete de Scala que pueden ayudar
a aclarar estos conceptos:
scala> "hola mundo" < "hola mundo scala!"
res8: Boolean = true
scala> ’a’<’b’
res9: Boolean = true
scala> "hola"<"hola"
res10: Boolean = false
scala> "hola"<="hola"
res11: Boolean = true
scala> "hola" <= "Hola"
res12: Boolean = false
12
En Scala, una expresión de tipo String y otra de tipo Char no se pueden comparar.
Página 13
En otros lenguajes de programación como Java, el operador relacional == se utiliza pa-
ra comparar tipos primitivos (comparando si los valores son iguales, como en Scala) y tipos
referenciados (comparando igualdad referencial). En Scala se puede comparar la igualdad refe-
rencial de tipos referenciados usando eq y neq.
Operadores lógicos
Los operadores lógicos permitirán combinar expresiones lógicas. Las expresiones lógicas
son todas aquellas expresiones que tras ser evaluadas se obtiene: verdadero o falso.
Scala soporta los operadores lógicos que se muestran en la tabla 1.6.
Descripción Operador Tipo
AND lógico && binario
OR lógico || binario
NOT lógico ! unario
Tabla 1.6: Operadores lógicos en Scala
Operadores bit a bit.
Los operadores bit a bit trabajan sobre cadenas de bits aplicando el operador en cada uno
de los bits de los operadores. Las tablas de verdad para los operadores &, | y ˆ son las que se
muestran en la tabla 1.7
p q p& q p ˆ q p | q
0 0 0 0 0
0 1 0 1 1
1 0 1 1 0
1 1 0 1 1
Tabla 1.7: Tabla de verdad de los operadores bit a bit &, | y ˆ .
Los operadores bit a bit soportados por Scala se muestran en la tabla 1.8.
Operador Descripción Ejemplo
& AND binario a & b
| OR binario a | b
ˆ XOR binario 45
˜ Complemento a uno (intercambio de bits) ˜a
<< Desplazamiento binario a la izquierda a <<2
>> Desplazamiento binario a la derecho a >>2
>>> Desplazamiento a la derecha con relleno de ceros a >>>2
Tabla 1.8: Operadores bit a bit.
Página 14
Operadores de igualdad.
Scala soporta los operadores de asignación mostrados en la tabla 1.9.
Operador Descripción Ejemplo
= Operador de asignación simple C = A + B
+= Suma y asignación A += B equivalente a A = A + B
-= Resta y asignación A -= B equivalente a A = A - B
*= Multiplicación y asignación A *= B equivalente a A = A * B
/= División y asignación A /= B equivalente a A = A / B
%= Módulo y asignación A %=B equivalente a A = A % B
<<= Desplazamiento a la izquierda y asignación A <<= 2 equivalente a A = A <<2
&= Bit a bit AND y asignación A &= 2 equivalente a A = A & 2
ˆ= Bit a bit XOR y asignación A ˆ= 2 equivalente a A = A ˆ 2
|= Bit a bit OR y asignación A |= 2 es equivalente a A = A | 2
Tabla 1.9: Operadores de asignación.
1.2.2.3. Nombrar expresiones
Es posible nombrar una expresión con la palabra reservada def y utilizar su nombre (identi-
ficador) en lugar de la expresión:
scala> def scale = 5
scale: Int
scala> 7 * scale
res4: Int = 35
scala> def pi = 3.141592653589793
pi: Double
scala> def radius = 10
radius: Int
scala> 2 * pi * radius
res5: Double = 62.83185307179586
def es una primitiva declarativa: le da un nombre a una expresión, pero no la evalúa.
scala> def r=8/0
r: Int
scala> r
java.lang.ArithmeticException: / by zero
at .r(<console>:7)
... 33 elided
Se puede observar que la definición de r no da error. El error se produce en el momento que
se evalúa por primera vez r.
Es lo contrario que la forma especial define de Scheme, que se utiliza para asociar nombres
con expresiones, y en primer lugar se evalúa la expresión. En realidad, nombrar una expresión
sólo aportará azúcar sintáctico, permitiendo crear funciones y evitando escribir la expresión
lambda asociada. Es decir, es lo mismo que crear una función en Scheme sin argumentos.
Alternativamente, se verá como Scala permite una definición de variables utilizando val o
var.
1.2.2.4. Variables
Las variables están encapsuladas en objetos, es decir, no pueden existir por si mismas. Las
variables son referencias a instancias de clases.
Página 15
Para definir una variable se requiere:
Definir su mutabilidad
Definir el identificador
Definir el tipo (opcional)
Definir un valor inicial
Sintaxis para definir una nueva variable:
<var | val> < identificador> : < Tipo> = < _ | Valor Inicial>
La nueva variable podrá ser un literal o el resultado de la evaluación de una expresión, una
función o el valor devuelto por un método. Pero también se podrán definir funciones, métodos,
bloques, etc.
Por ejemplo:
1 val x : Int = 1
Se usa la palabra reservada val para indicar que la referencia no puede reasignarse, mientras
que se utiliza var para indicar que sí puede reasignarse. La variable val es similar a una variable
final en Java: una vez inicializada, no se podrá reasignar. Una variable var, por el contrario, se
puede reasignar múltiples veces.
Todas las variables o referencias en Scala tienen un tipo, debido a que el lenguaje es estric-
tamente tipado. Sin embargo, los tipos pueden en muchos casos omitirse, porque el compilador
de Scala tiene inferencia de tipos. Por ejemplo, las siguientes definiciones son equivalentes:
scala> var x : Int = 1
scala> var x = 1
Otros ejemplos:
val msg = "Hola mundo!"
msg: java.lang-String = Hola mundo!
En este ejemplo se aprecia que Scala tiene inferencia de tipos, es decir, al haber inicializado
la variable msg con una cadena, Scala asocia el tipo de msg al tipo de datos String. Si se intenta
modificar el valor de msg, no se podrá y obteniéndose un error ya que se ha definido como val:
scala> msg = "Hasta luego!"
<console>:5: error: reassignment to val
msg = "Hasta luego!"
Si se imprime por pantalla el valor de msg:
scala> println(msg)
Hola mundo!
Si se quisiera reasignar el valor de una variable habría que utilizar la palabra reservada var:
scala> var saludo = "Hola mundo!"
saludo: java.lang.String = Hola mundo!
scala> saludo = "Hasta luego!"
saludo: java.lang.String = Hasta luego!
Aunque todas las declaraciones de variables podrían realizarse utilizando var, se recomienda
encarecidamente usar val cuando la variable no vaya a mutar.
Página 16
1.2.3. Uso del carácter punto y coma (;) en Scala
En Scala, el uso del punto y coma al final de una línea de programa es opcional en la
mayoría de las ocasiones, siendo recomendado omitir el mismo siempre y cuando su uso no sea
obligatorio. Se podría escribir:
1 def pi = 3.14159;
aunque muchos programadores omitirán el uso del (;) y simplemente escribirán:
1 def pi = 3.14159
¿Cuándo es obligatorio el uso del punto y coma?
El uso del punto y coma es obligatorio para separar instrucciones escritas en la misma línea.
1 def y = x - 1; y + y
Uso del punto y coma y operadores infijos en Scala.
Uno de los problemas derivados de omitir el uso del punto y coma en Scala es cómo escribir
expresiones que ocupen varias líneas. Si se escribiera:
Expresión larga
+ otra expresión larga
sería interpretado por Scala como dos expresiones. Si lo que se quiere es que se interprete como
una única expresión, se podría hacer de dos formas:
Se podría encerrar una expresión de varias líneas entre paréntesis, dando por hecho que
no se usará el punto y coma en éstas líneas:
(Expresión larga
+ otra expresión larga)
Se podría escribir el operador al final de la línea, indicándole así a Scala que la expresión
no está finalizada:
Expresión larga +
otra expresión larga
Por tanto, por norma general, los saltos de línea serán tratados como puntos y coma, salvo
que algunas de las siguientes condiciones sea cierta:
La línea en cuestión finaliza con una palabra que no puede actuar como final de sentencia,
como por ejemplo un espacio (“ ”) o los operadores infijos.
La siguiente línea comienza con una palabra que no puede actuar como inicio de sentencia
La línea termina dentro de paréntesis (...) o corchetes [...], puesto que éstos últimos no
pueden contener múltiples sentencias
Página 17
1.3. Bloques en Scala
Un bloque en Scala estará encerrado entre llaves { ...}. Dentro de un bloque se podrán
encontrar definiciones o expresiones. El último elemento de un bloque será una expresión que
definirá el valor del bloque, la cual podrá estar precedida por definiciones auxiliares.
Los bloques también son expresiones en sí mismos, por lo que un bloque podrá aparecer en
cualquier lugar en el que pueda aparecer una expresión. Ejemplo:
1 { val x = f(3)
2 x * x
3 }
1.3.1. Visibilidad y bloques en Scala
Scala sigue las reglas de ámbito habituales en lenguajes como C o Java. Las definiciones
realizadas dentro de un bloque sólo serán visibles dentro del mismo y ocultan las definiciones
realizadas fuera del bloque que tengan el mismo nombre.
Las definiciones realizadas en bloques externos estarán visibles dentro de un bloque siempre
y cuando no sean ocultadas por otras definiciones con el mismo nombre en el bloque.
Ejemplo:
1 val x = 10
2 def f(y: Int)=y+1
3 val result = {
4 val x = f(3)
5 x * x
6 } + x
El resultado de la ejecución de este código será 26
1.4. Evaluación en Scala
1.4.1. Evaluación de expresiones
Para evaluar una expresión no primitiva:
1. Se toma el operador con mayor prioridad (ver la Sección 1.2.2.2: Prioridad y Asociati-
vidad de los operadores « página 11 »), o en caso de que todos los operadores tenga la
misma prioridad se toma el operador situado más a la izquierda,
2. Se evalúa sus operandos (de izquierda a derecha),
3. Se aplica el operador a los operandos.
Un nombre se evalúa reemplazando el mismo por su definición. El proceso de evaluación fina-
liza cuando se obtiene un valor.
Ejemplo:
(2 ∗ pi) ∗ radius
(2 ∗ 3,14159) ∗ radius
6,28318 ∗ radius
Página 18
6,28318 ∗ 10
62,8318
1.4.2. Evaluación de funciones
La evaluación de funciones parametrizadas es similar a la de operadores:
1. Se evalúan los parámetros de la función de izquierda a derecha,
2. Se reemplaza la llamada a la función por la definición de la misma y, al mismo tiempo,
3. Se reemplazan los parámetros formales de la función por el valor de sus argumentos.
Este sistema de evaluación de expresiones es llamado modelo de sustitución, cuya idea
gira en torno a que lo que hace una evaluación es reducir una expresión a su valor y puede ser
aplicado a todas las expresiones (siempre y cuando no tengan efectos laterales). El modelo de
sustitución está formalizado en el λ-cálculo. Esta estrategia de evaluación es conocida como
evaluación estricta o Call by Value.
Existe otra alternativa: no evaluar los argumentos antes de reemplazar la función por la
definición de la misma, llamada evaluación no estricta o Call by Name.
1.4.3. Sistema de Evaluación de Scala
Scala usa evaluación estricta normalmente, pero se puede indicar explícitamente que se
desea que algún argumento de una función use evaluación no estricta. Para esto último, se
pondrá => delante del tipo del parámetro que se desee evaluar siguiendo esta estrategia.
Ejemplo:
1 def miConst (x : Int, y: =>Int) = x
Algoritmo 1.2: Función con dos parámetros. El primero es evaluado por valor y el segundo por
nombre
1.4.3.1. Valores de las definiciones
Anteriormente se ha dicho que los parámetros de las funciones pueden ser pasados por valor
(evaluación estricta) o por nombre (evaluación no estricta).
La misma distinción se puede aplicar a las definiciones. La forma def es por nombre (eva-
luación no estricta) y la forma val es por valor (evaluación estricta).
1.4.3.2. Evaluación de Booleanos
En la tabla 1.10 aparecen representadas las reglas de reducción para expresiones booleanas.
Página 19
!true → false
!false → true
true && e → e
false && e → false
true || e → true
false || e → e
Tabla 1.10: Reglas de reducción para expresiones booleanas
En la tabla 1.10 se puede apreciar que && y || no siempre necesitan que el operando derecho
sea evaluado. En estos casos, se dirá que estas expresiones usan una evaluación en cortocir-
cuito.
1.4.4. Ámbito y visibilidad de las variables
El funcionamiento de los ámbitos en Scala es el siguiente:
Una invocación a una función crea un nuevo ámbito en el que se evalúa el cuerpo de la
función. Es el ámbito de evaluación de la función.
El ámbito de evaluación se crea dentro del ámbito en el que se definió la función a la que
se invoca.
Los argumentos de la función son variables no mutables locales de este nuevo ámbito que
quedan ligadas a los parámetros que se utilizan en la llamada.
En el nuevo ámbito se pueden definir variables locales.
En el nuevo ámbito se pueden obtener el valor de variables del ámbito padre.
Primer ejemplo:
Supongamos el siguiente código en Scala:
1 def f(x: Int, y: Int): Int = {
2 val z = 5
3 x+y+z
4 }
5 def g(z: Int): Int = {
6 val x = 10
7 z+x
8 }
9 f(g(3),g(5))
Se definen dos funciones f y g, dentro de cada una de las cuales hay declaradas distintas
variables locales. Las funciones f y g devuelven una suma de los parámetros con la variable
local. En la última línea de código se realiza una invocación a f con los resultados devueltos por
dos invocaciones a g .
¿Cuántos ámbitos locales se crean? ¿En qué orden?
1. En primer lugar se realizan las invocaciones a g . Cada una crea un ámbito local en el que
se evalúa la función. Las invocaciones devuelven 13 y 15, respectivamente.
Página 20
2. Después se realiza la invocación a f con esos valores 13 y 15. Esta invocación vuelve a
crear un ámbito local en el que se evalúa la expresión x+y+z , devolviendo 33.
Por ahora, todo es bastante normal. La diversión empezará cuando se construyan funciones
anónimas durante la ejecución de otras funciones, lo cual permitirá la definición de cierres.
1.5. Estructuras de control en Scala
Las estructuras de control permiten controlar el flujo de ejecución de las sentencias de un
programa, método, bloque, etc. Siguiendo el flujo natural, las sentencias que componen un pro-
grama se ejecutan secuencialmente una tras de otra según estén definidas13
(primero se ejecutará
la primera sentencia, después la segunda ..., y así hasta la última sentencia). Las sentencias de
control de flujo se emplean en los programas para ejecutar sentencias condicionalmente, repetir
un conjunto de sentencias o, en general, cambiar el flujo secuencial de ejecución. Las estructuras
de control se dividen en tres grandes categorías en función del flujo de ejecución:
Estructuras secuenciales
Estructuras condicionales
Estructuras iterativas
Hasta el momento se ha visto el flujo secuencial. Cada una de la sentencias que se utilizan
en Scala están separadas por el carácter punto y coma (véase el uso del carácter punto y coma
en Scala en la Subsección 1.2.3: Uso del carácter punto y coma (;) en Scala « página 17 »). El
uso de estructuras secuenciales puede ser suficiente para la resolución de programas sencillos,
pero para la resolución de programas de tipo general se necesitará controlar las sentencias que
se ejecutan haciendo uso de estructuras condicionales e iterativas.
1.5.1. Estructuras condicionales
Se utilizarán las estructuras condicionales para determinar si un bloque debe de ser ejecu-
tado o no, en función de una condición lógica o booleana.
1.5.1.1. La sentencia if
Una sentencia if consiste en una expresión booleana seguida de un bloque de expresiones.
Si la expresión booleana se evalúa a cierta, entonces se ejecutará el bloque de expresiones. En
caso contrario no se ejecutará el bloque de expresiones. La sintaxis de una sentencia if es:
1 if (expresion_booleana) {
2 // Sentencias que se ejecutaran si la
3 // expresion booleana se evalua a true
4 }
Algoritmo 1.3: Sintaxis sentencia if
Ejemplo:
13
Cuando se escribe un programa, se introduce la secuencia de sentencias dentro de un archivo. Sin sentencias
de control del flujo, el intérprete ejecuta las sentencias conforme aparecen en el programa de principio a fin.
Página 21
1 def mayorQueCinco(x: Int) = if (x > 5) { println(" El argumento
",x " es mayor que 5");}
Algoritmo 1.4: Expresión condicional if
La sentencia if / else
La sentencia if puede ir seguida de la una declaración else y un bloque de expresiones.
El bloque de expresiones que sigue a la declaración else se ejecutará en el caso de que la
expresión booleana del if sea falsa. Scala tiene la expresión condicional if-else para expresar la
elección entre dos alternativas (parecida a if-else de Java pero usada para expresiones, no para
instrucciones).
La sintaxis de una sentencia if / else es:
1 if (expresion_booleana) {
2 // Sentencias que se ejecutaran si la
3 // expresion booleana se evalua a true
4 } else {
5 // Sentencias que se ejecutaran si la
6 // expresion booleana se evalua a false
7 }
Algoritmo 1.5: Sintaxis sentencia if / else
Ejemplo:
1 def abs(x: Int) = if (x > 0) x else -x
Algoritmo 1.6: Expresiones condicionales. Función valor absoluto
La sentencia if...else if ...else
La expresión if puede ir seguida por una declaración else if, lo cual nos será de gran ayuda
para testear varias opciones haciendo uso de una única sentencia if.
Cuando se haga uso de la sentencia if...else if...else habrá que tener en cuenta algunos
puntos importantes:
Una sentencia if podrá tener cero o una declaración else, la cual estará declarada siempre
al final de una sentencia if.
Una sentencia if podrá tener cero o varias declaraciones else if, y siempre deberán prece-
der a la declaración else (si hubiera).
Si la evaluación de la expresión booleana de la sentencia if es true, ninguna de las otras
expresiones booleanas de las declaraciones else if serán evaluadas, y tampoco se ejecutará
el bloque de la declaración else.
En el algoritmo Algoritmo 1.7: Sintaxis sentencia if / else if / else « página 23 » se muestra
la sintaxis de una sentencia if / else if / else.
Página 22
1 if (expresion_booleana1) {
2 // Sentencias que se ejecutaran si la
3 // expresion booleana 1 se evalua a true
4 } else if (expresion_boolena2){
5 // Sentencias que se ejecutaran si la
6 // expresion booleana 2 se evalua a true
7 } else if (expresion_boolena3){
8 // Sentencias que se ejecutaran si la
9 // expresion booleana 3 se evalua a true
10 }
11 else {
12 // Sentencias que se ejecutaran si ninguna
13 // de las anteriores expresiones booleanas
14 // se evaluan a true
15 }
Algoritmo 1.7: Sintaxis sentencia if / else if / else
Estructuras condicionales anidadas
Scala permite que las sentencias if, if/else, if/else if/else se puedan anidar. Ejemplo:
1 var x = 30;
2 var y = 10;
3
4 if( x == 30 ){
5 if( y == 10 ){
6 println("X = 30 and Y = 10");
7 }
8 }
Algoritmo 1.8: Sentencias if anidadas
1.5.2. Estructuras iterativas
Hasta el momento se ha visto como el uso de la sentencia condicional permite dejar de
ejecutar algunas sentencias dispuestas en un programa, en función del resultado de la evaluación
de una expresión booleana. Pero el flujo del programa, en cualquier caso, siempre avanza hacia
adelante y nunca se vuelve a ejecutar una sentencia ejecutada anteriormente.
Las sentencias iterativas permitirán iterar un bloque de sentencias, es decir, ejecutar un
bloque de sentencias mientras la condición especificada sea cierta. A este tipo de sentencias se
les denomina bucles y al bloque de sentencias se les denominará cuerpo del bucle.
En la tabla 1.11 se puede observar los diferentes tipos de bucles que presenta Scala para dar
respuesta a las necesidades de iteración de los programadores.
Página 23
Bucle Descripción
while Repite una sentencia o un bloque de sentencias siempre que la condición
sea verdadera. Se evalúa la condición antes de ejecutar el cuerpo del bucle
do...while Igual que el bucle while pero la evaluación de la condición se produce
después de la ejecución del bucle.
for El cuerpo del bucle se ejecuta un número determinado de veces. Presenta
un sintaxis que abrevia el código que maneja la variable del bucle.
Tabla 1.11: Tipos de bucles en Scala
1.5.2.1. Bucles while
Un bucle while repetirá sucesivamente un bloque de sentencias mientras la condición da-
da sea cierta. Un bucle while tiene una condición de control o expresión lógica que ha de ir
encerrada entre paréntesis y es la encargada de controlar la secuencia de repetición.
El punto clave de los bucles while es el hecho de que la evaluación de la condición se realiza
antes de que se ejecute el cuerpo del bucle, por lo que si la condición se evalúa a falsa, el cuerpo
del bucle no se ejecutaría ninguna vez.
La sintaxis de un bucle while es:
1 while (condicion) {
2 // Sentencias que se ejecutaran mientras
3 // la condicion sea cierta
4 }
Algoritmo 1.9: Sintaxis bucles while
Hay que hacer notar que si la condición es cierta inicialmente, la sentencia while no termi-
nará nunca (bucle infinito) a menos que en el cuerpo de la misma se modifique de alguna forma
la condición de control del bucle.
Ejemplo:
1 def printUntil(x: Int) = {
2 //variable local
3 var s = 0;
4 while (s <= x){
5 println("Valor: " + s);
6 s += 1;
7 }
8 }
Algoritmo 1.10: Ejemplo de bucle while.
1.5.2.2. Bucles do...while
Al igual que los bucles while, los bucles do...while repetirán un bloque de sentencias hasta
que la condición de control del bucle sea falsa. Por tanto, al igual que en el bucle while, el cuerpo
del bucle se ejecuta mientras la expresión lógica sea cierta. Los bucles do...while también se
denominan post-prueba ya que, a diferencia de los bucles while, los bucles do...while evalúan
Página 24
la condición después de ejecutar el cuerpo del bucle, motivo por el cual este tipo de bucles
garantizan la ejecución del cuerpo del bucle al menos una vez.
La sintaxis de un bucle while es:
1 do {
2 // Sentencias que se ejecutaran mientras
3 // la condicion sea cierta
4 } while (condicion);
Algoritmo 1.11: Sintaxis bucles do... while.
Se puede observar que la expresión lógica o booleana aparece al final del bucle por lo que
el cuerpo del bucle se ejecutará al menos una vez. Si la condición se evalúa a cierta, el flujo
de control saltará hasta la declaración do y el cuerpo del bucle se ejecutará nuevamente. Este
proceso se repetirá hasta que la condición se evalúe a falsa.
Ejemplo:
1 def printUntil2(x: Int) = {
2 //variable local
3 var s = 0;
4 do {
5 println("Valor: " + s);
6 s += 1;
7 }while (s <= x)
8 }
Algoritmo 1.12: Ejemplo de bucle do... while.
1.5.2.3. Bucles for
Los bucles for son sentencias que nos permitirán escribir eficientemente bucles que ten-
gan que repetirse un número determinado de veces. En Scala podemos encontrar las siguientes
variantes de bucles for:
Bucles for con rangos
Bucles for con colecciones
Bucles for con filtros
A continuación se verán los conceptos básicos de este tipo de bucles aunque, la especificidad
de su implementación en Scala y su utilidad harán, al contrario de lo que se podría imaginar,
que este tipo de bucles sean también muy útiles en programación funcional, capítulo en el cual
se estudiarán con mayor profundidad (véase el Capítulo 3: Programación Funcional en Scala «
página 53 »).
Bucles for con rangos
La forma más fácil de definir bucles for es haciendo uso del tipo de datos Range definido
en Scala. El bucle se repetirá tantas veces como valores contenga el tipo de datos Range dado
(véase la Subsubsección 3.11.3.1: Definición de rangos en Scala. La clase Range « página 107
»).
La sintaxis más simple de bucles for con rangos es:
Página 25
1 for (a <- Range) {
2 // Sentencias que se ejecutaran tantas veces
3 // como valores contenga el tipo de datos
4 // Range dado
5 }
Algoritmo 1.13: Sintaxis bucles for con rangos.
Range puede ser un rango de números, normalmente representado de la forma i to j o i until j.
Más adelante veremos este tipo de datos en mayor profundidad.
El operador flecha izquierda (<-) recibe el nombre de generador, ya que es el encargado de
generar los diferentes valores del rango.
La variable de control de bucle (a) se inicializará con el primer valor del rango e irá
tomando, en cada iteración, los diferentes valores del mismo.
Ejemplo:
1 def printUntil3(x: Int) = {
2 //variable local
3 for (a <- 0 to x) {
4 println("Valor: " + a);
5 }
6 }
Algoritmo 1.14: Ejemplo de bucle for con rangos.
Dentro de los bucles for se pueden utilizar varios rangos, separando cada uno de ellos por el
carácter punto y coma (;). En este caso el bucle iterará sobre todas las posibles combinaciones
de los mismos. Ejemplo:
scala> for (x<- 1 to 3;y<- 4 to 6) {println("Valor x: "+x+" Valor y: "+y)}
Valor x: 1 Valor y: 4
Valor x: 1 Valor y: 5
Valor x: 1 Valor y: 6
Valor x: 2 Valor y: 4
Valor x: 2 Valor y: 5
Valor x: 2 Valor y: 6
Valor x: 3 Valor y: 4
Valor x: 3 Valor y: 5
Valor x: 3 Valor y: 6
Bucles for con colecciones
Los bucles for sirven para recorrer fácilmente los elementos de una colección.
La sintaxis de los bucles for con colecciones es:
1 for (a <- Collection) {
2 // Sentencias que se ejecutaran tantas veces
3 // como valores contenga el tipo de datos
4 // Collection dado
5 }
Algoritmo 1.15: Sintaxis bucles for con colecciones
Se puede iterar sobre los elementos de las distintas estructuras de datos definidas en la
librería Scala.collection de Scala como listas, conjuntos,etc., así como sobre los tipos de datos
Página 26
creados por el usuario14
La variable de control de bucle (a) se inicializará con el primer valor de la colección e
irá tomando el valor de los distintos elementos que componen la colección en las sucesivas
iteraciones del bucle.
Ejemplo:
1 def forLista = {
2 val numList = List(0,1,2,3,4,5,6,7,8,9,10);
3 for( a <- numList ){
4 println( "Valor de a: " + a );
5 }
6 }
Algoritmo 1.16: Ejemplo bucle for con colecciones.
En el ejemplo del algoritmo 1.16 se puede apreciar un bucle for recorriendo una colección
de números. Se ha creado esta colección usando el tipo de datos List de Scala. Las colecciones
en Scala se estudiarán en mayor profundidad en la Sección 3.11: Colecciones en Scala « página
102 ».
Bucles for con filtros
Los bucles for en Scala permiten emplear filtros para descartar la iteración sobre algunos
elementos de la colección que se desea iterar (rangos, listas, conjuntos,...) que no cumplan con
alguna propiedad. Para filtrar elementos se empleará una o más sentencias if.
La sintaxis de los bucles for con filtros es:
1 for (a <- Collection|Range...
2 if condicion1; if condicion2...) {
3 // Sentencias que se ejecutaran tantas veces
4 // como valores del tipo de datos
5 // Collection|Range dado satisfagan los filtros
6 }
Algoritmo 1.17: Sintaxis bucles for con filtros.
La variable de control de bucle (a) se inicializará con el primer valor de la colección/rango
que satisfaga las condiciones: condicion1 y condicion2, e irá tomando el valor de los distintos
elementos que componen la colección y que también satisfagan las condiciones impuestas como
filtros.
1 def forListaconFiltros = {
2 val numList = List(0,1,2,3,4,5,6,7,8,9,10);
3 for( a <- numList
4 if a <= 5;
5 if a != 3 ){
6 println( "Valor de a: " + a );
7 }
8 }
Algoritmo 1.18: Ejemplo bucle for con filtros
14
Estos tipos de datos creados por el usuario deberán presentar unas características especiales que serán estudia-
das con mayor detalle.
Página 27
En el ejemplo del algoritmo 1.18 se recorren los elementos de la lista numList que sean
menores o iguales a 5 y distintos de 3.
Bucles for con yield.
Los bucles for en Scala permiten almacenar los resultados de un bucle for en una variable o
que éstos sean el valor devuelto por una función. Para hacer esto se añadirá la palabra reservada
yield al final del cuerpo del bucle.
La sintaxis general de los bucles for con yield es:
1 for (a <- Collection|Range...
2 if condicion1; if condicion2...) {
3 // Sentencias que se ejecutaran tantas veces
4 // como valores del tipo de datos
5 // Collection dado satisfagan los filtros
6 }yield a
Algoritmo 1.19: Sintaxis bucles for con yield.
Yield generará un valor en cada iteración del bucle que será recordado por el bucle for15
.
Cuando el bucle finalice, devolverá una colección del mismo tipo de datos que la colección que
estamos iterando con los valores recordados. En los bucles for con yield se podrá también hacer
uso de filtros, haciendo de estos una herramienta mucho más potente.
En el algoritmo 1.20 podemos ver un ejemplo del uso de bucles for con yield y filtros.
1 def forListaconYield = {
2 val numList = List(0,1,2,3,4,5,6,7,8,9,10);
3 val lista= for( a <- numList
4 if a <= 5;
5 if a != 3 )yield a * 2
6 for (b<-lista){println ("Valor recordado por yield: "+b)}
7 }
Algoritmo 1.20: Ejemplo bucle for con yield
En el algoritmo 1.20 se utilizan dos bucles for. El primero de ellos generará una lista con el
doble de los números de la lista numList que satisfagan los filtros del bucle. El segundo bucle
for iterará sobre la lista resultante del primer bucle, imprimiendo los valores por pantalla.
1.6. Interacción con Java
Una de las características más importantes de Scala es que hace muy fácil la interacción
con el código escrito en Java. Todas las clases de la librería java.lang son importadas por de-
fecto, mientras que las otras necesitan ser importadas explícitamente. Scala hace muy fácil la
interactuación con código Java.
Como se verá a lo largo de los próximos capítulos, Scala es un lenguaje de programación
que ha sabido integrar algunas de las principales características presentes en los lenguajes de
programación más populares. Java no es la excepción, y comparte muchas cosas con éste. La
diferencia que se puede ver es que para cada uno de los conceptos de Java, Scala los aumenta,
15
Como si el bucle for tuviera un buffer que no se puede ver y al que en cada iteración se le añade un elemento
Página 28
refina y mejora. Poder aprender todas las características de Scala nos equipa con más y mejores
herramientas a la hora de escribir nuestros programas.
También es posible heredar de clases Java e implementar interfaces Java directamente en
Scala.
A continuación se presenta un ejemplo que demuestra esto. Se quiere obtener y formatear
la fecha actual de acuerdo a convenciones utilizadas en un país específico, por ejemplo Francia.
Las librerías de clases de Java definen clases de utilidades interesantes, como Date y Da-
teFormat. Ya que Scala interacciona fácilmente con Java, no es necesario implementar estas
clases equivalentes en las librerías de Scala, se pueden simplemente importar las clases de los
correspondientes paquetes de Java:
1 import java.util.{Date, Locale}
2 import java.text.DateFormat._
3 object FrenchDate {
4 def main(args: Array[String]) {
5 val ahora = new Date
6 val df = getDateInstance(LONG, Locale.FRANCE)
7 println(df format ahora)
8 }
9 }
Algoritmo 1.21: Fecha actual formateada.
Las declaraciones de importación de Scala parecen muy similares a las de Java, sin embargo,
las primeras son bastante más potentes. Se pueden importar múltiples clases desde el mismo
paquete al encerrarlas entre llaves, como se muestra en la primer linea. Otra diferencia es que
se pueden importar todos los nombres de un paquete o clase, utilizando el carácter guión bajo
(_) en lugar del asterisco (*). Eso es porque el asterisco es un identificador válido en Scala (por
ejemplo se puede nombrar a un método).
Por tanto, la declaración import en la segunda línea, importa todos los miembros de la clase
DateFormat. Esto hace que el método estático getDateInstance y el campo estático LONG sean
directamente visibles.
Dentro del método main, primero se crea una instancia de la clase Date que por defecto con-
tiene la fecha actual. A continuación se define un formateador de fechas, utilizando el método
estático getDateInstance que ha sido importado previamente. Finalmente, se imprime la fecha
actual formateada de acuerdo a la instancia de DateFormat que fue “localizada”. Esta última
línea muestra un ejemplo de un método que toma un solo argumento y que ha sido utilizado
como operador infijo (véase la Subsubsección 1.2.2.2: Operadores « página 10 »). Es decir, la
expresión:
df format ahora
es solamente otra manera más corta de escribir la expresión:
df.format(ahora)
1.6.1. Ejecución sobre la JVM
Una de las características más relevantes de Java no es el lenguaje, sino su máquina virtual
(JVM). Una pulida maquinaria que el equipo de HotSpot ha ido mejorando a lo largo de los años.
Puesto que Scala es un lenguaje basado en la JVM, se integra a la perfección dentro de Java y
Página 29
su ecosistema (herramientas, librerías, IDE,...), por lo que no será necesario desprenderse de
todas las inversiones hechas en el pasado.
El compilador de Scala genera bytecode, siendo indistinguible a este nivel el código escrito
en Java y el escrito en Scala. Adicionalmente, puesto que se ejecuta sobre la JVM, se beneficia
del rendimiento y estabilidad de dicha plataforma. Y siendo un lenguaje de tipado estático, los
programas construidos con Scala se ejecutan tan rápido como los programas Java.
El hecho de ser un lenguaje basado en la JVM también es la consecuencia por la que al-
gunos tipos de la JVM (como vectores) acaban siendo poco elegantes en Scala o que ciertas
características, como la recursividad de cola, no están implementadas en la JVM y hay que
simularlas.
1.7. Ejercicios
Ejercicio 1. Supongamos el siguiente código en Scala:
1 val x = 10
2 val y = 20
3 def g(y: Int): Int = {
4 x+y
5 }
6 def prueba(z: Int): Int = {
7 val x = 0
8 g(x+y+z)
9 }
10 prueba(3)
¿Qué devuelve prueba(3)? ¿En qué ámbito se evalúa la expresión x+y+z ? ¿Y la expresión
x+y ? ¿Qué valores tienen esas variables en el momento de la evaluación?
Página 30
Capítulo 2
Programación Orientada a Objetos en
Scala
2.1. Introducción a la programación orientada a objetos en
Scala
2.1.1. Características principales de la programación orientada a objetos
La popularidad de lenguajes como Java, C# o Ruby han hecho que la POO sea un paradigma
ampliamente aceptado entre la mayoría de desarrolladores. Aunque existen numerosos lengua-
jes orientados a objetos en el ecosistema actual, únicamente podríamos encajar unos pocos si
nos ceñimos a una definición estricta de orientación a objetos. Un lenguaje orientado a objetos
“puro” debería presentar las siguientes características:
Encapsulamiento/ocultación de información.
Herencia.
Polimorfismo/Enlace dinámico.
Todos los tipos predefinidos son objetos.
Todas las operaciones son llevadas a cabo mediante el envío de mensajes a objetos.
Todos los tipos definidos por el usuario son objetos.
2.1.2. Scala como lenguaje orientado a objetos
Scala da soporte a todas las características anteriores mediante la utilización de un modelo
puro de orientación a objetos muy similar al presentado por Smalltalk (lenguaje creado por Alan
Kay sobre el año 1980)1
.
De manera adicional a todas las características puras de un lenguaje orientado a objetos
presentadas anteriormente, Scala añade algunas innovaciones en el espacio de los lenguajes
orientados a objetos:
1
http://en.wikipedia.org/wiki/Smalltalk
Página 31
Composición modular de los elementos mezclados (mixin). Mecanismo que permite
la composición de clases para el diseño de componentes reutilizables, evitando los pro-
blemas presentados por la herencia múltiple. Similar a los interfaces Java y las clases
abstractas. Por una parte se pueden definir múltiples “contratos”(del mismo modo que los
interfaces). Por otro lado, se podrían tener implementaciones concretas de los métodos.
Self-type. Los rasgos mezclados no dependen de ningún método y/o atributo de aque-
llas clases con las que se está entremezclando, aunque en determinadas ocasiones será
necesario hacer uso de las mismas. Esta capacidad es conocida en Scala como self-type.
Abstracción de tipos. Existen dos mecanismos principales de abstracción en los lengua-
jes de programación: la parametrización y los miembros abstractos. Scala soporta ambos
estilos de abstracción de manera uniforme para tipos y valores.
2.2. Paquetes, clases, objetos y namespaces
2.2.1. Objetos Singleton
Scala no soporta la definición de atributos estáticos en las clases, incorporando en su lugar
el concepto de objeto singleton. La definición de objetos de este tipo es muy similar a la de
las clases, salvo que se utiliza la palabra reservada object en lugar de class. Cuando un objeto
singleton comparte el mismo nombre de una clase, el primero de ellos es conocido como com-
panion object (objeto acompañante), mientras que la clase se denomina companion class (clase
acompañante) del objeto singleton. Inicialmente, sobre todo aquellos desarrolladores provenien-
tes del mundo Java, podrían ver este tipo de objetos como un contenedor en el que se podrían
definir tantos métodos estáticos como quisiéramos.
Una de las principales diferencias entre los objetos singleton y las clases es que los pri-
meros no aceptan parámetros (no podemos instanciar un objeto singleton mediante la palabra
reservada new), mientras que las clases sí lo permiten. Cada uno de los objetos singleton es
implementado mediante una instancia de una clase synthetic referenciada desde una variable
estática, por lo que presentan la misma semántica de inicialización que los estáticos de Java. Un
objeto singleton es inicializado la primera vez que es accedido por algún código.
2.2.2. Módulos, objetos, paquetes y namespaces
Si se quiere hacer referencia a un método declarado dentro de un objeto (object) se tendrá
que hacer una llamada del tipo nombreObjeto.nombreMétodo(parámetros) ya que el método
nombreMétodo habrá sido definido dentro del objeto nombreObjeto.
En realidad, en Scala todos los valores serán objetos. El principal objetivo de un objeto es
el de otorgar un namespace a sus miembros, algunas veces llamado módulo.
Un objeto puede constar de cero o más miembros. Un miembro puede ser un método decla-
rado con la palabra reservada def, o puede ser otro objeto declarado con las palabras reservadas
val o object2
Para hacer referencia a los miembros de un objeto en Scala se utilizará la notación típica de
la POO (se indicará el namespace del objeto seguido de un puto y del nombre del miembro).
Ejemplo: MyModule.abs(42)
2
Los objetos también pueden contener otros tipos de objetos, que no se mencionarán en este capítulo.
Página 32
Si se observa la expresión 3 * 2, lo que se está haciendo realmente es llamar al miembro
(método) * del objeto 3, es decir (3.*(2)). En general, los métodos que toman un solo argumento
pueden ser usados con una sintaxis de infijo3
. De este modo, se puede comprobar como la
llamada a MyModule abs 42 devolvería la misma salida que la llamada MyModule.abs(42).
Es posible importar miembros de un namespace haciendo uso de la palabra reservada import.
Ejemplo: import MyModule.abs
Para importar todos los miembros de un namespace se usará el guión bajo. Ejemplo: import
MyModule._4
En Scala, al igual que en Java, se puede utilizar la palabra reservada package, la cual creará
un paquete (en realidad se crea un namespace). Por tanto, se puede crear un namespace tanto
con object como con package pero si se utiliza package no se creará un objeto, por lo que,
obviamente, no se podrá pasar como tal en otras llamadas. Además en un package no podrá
haber definiciones de miembros usando las palabras reservadas val o def.
2.2.3. Clases
Del mismo modo que en todos los lenguajes orientados a objetos, Scala permite la defini-
ción de clases en las que se pueden añadir métodos y atributos. A continuación se muestra la
definición de la clase MiPrimeraClase.
1 class MiPrimeraClase{
2 val a = 1
3 }
Algoritmo 2.1: Mi primera clase
Si se quiere instanciar un objeto de la clase anterior habrá que hacer uso de la palabra
reservada new.
1 val v = new MiPrimeraClase
Cuando se defina una variable en Scala se tendrá que especificar la mutabilidad de la misma,
pudiendo escoger entre:
Utilizar la palabra reservada val para indicar que es inmutable. Una variable de este tipo
es similar al uso de final en Java. Una vez inicializada, no se podrá reasignar jamás.
De manera contraria, se podrá indicar que una variable es de tipo var, consiguiendo con
esto que su valor pueda ser modificado durante todo su ciclo de vida.
Uno de los principales mecanismos utilizados para garantizar la robustez de un objeto es
la afirmación de que su conjunto de atributos (variables de instancia) permanece constante a
lo largo de todo el ciclo de vida del mismo. El primer paso para evitar que agentes externos
tengan acceso a los campos de una clase es declarar los mismos como private. Puesto que
los campos privados sólo podrán ser accedidos desde métodos que se encuentran definidos en
la misma clase, todo el código que podría modificar el estado del mismo estará localizado en
dicha clase.5
3
En estos casos, en Scala podremos omitir el uso del punto y los paréntesis. Para más información, véase la
Subsubsección 1.2.2.2: Operadores « página 10 »
4
Véase el algoritmo 1.21 (página 29) donde se podrán ver otros ejemplos de las declaraciones de importación
en Scala
5
Por defecto, si no se especifica en el momento de la definición, los atributos y/o métodos de una clase tienen
acceso público. Es decir, public es el cualificador por defecto en Scala.
Página 33
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala
Programación funcional en Scala

Más contenido relacionado

La actualidad más candente

3. conceptos de calidad del software
3. conceptos de calidad del software3. conceptos de calidad del software
3. conceptos de calidad del softwareJuan Pablo Carvallo
 
Introducción a C++
Introducción a C++Introducción a C++
Introducción a C++Areli1215
 
Componentes y Librerías - Tópicos avanzados de programación.
Componentes y Librerías - Tópicos avanzados de programación.Componentes y Librerías - Tópicos avanzados de programación.
Componentes y Librerías - Tópicos avanzados de programación.Giancarlo Aguilar
 
Interfaz con el sistema de archivos
Interfaz con el sistema de archivosInterfaz con el sistema de archivos
Interfaz con el sistema de archivosUTPL UTPL
 
Diagramas clases presentacion
Diagramas clases presentacionDiagramas clases presentacion
Diagramas clases presentacionjosebrandon24
 
Identificadores en Lógia de Programación
Identificadores en Lógia de ProgramaciónIdentificadores en Lógia de Programación
Identificadores en Lógia de Programaciónnormaroldano
 
Desarrollo de aplicaciones web con casos de uso
Desarrollo de aplicaciones web  con casos de usoDesarrollo de aplicaciones web  con casos de uso
Desarrollo de aplicaciones web con casos de usoJosafat Mtz
 
Diagramas UML: Componentes y despliegue
Diagramas UML: Componentes y despliegueDiagramas UML: Componentes y despliegue
Diagramas UML: Componentes y desplieguejoshell
 
PPT Git GitHub
PPT Git GitHubPPT Git GitHub
PPT Git GitHubdrsevilla
 
Las 4 P en el desarrollo de software
Las 4 P en el desarrollo de softwareLas 4 P en el desarrollo de software
Las 4 P en el desarrollo de softwareSofylutqm
 
Calidad del software
Calidad del softwareCalidad del software
Calidad del softwareUTPL UTPL
 
Capitulo 16- Excepciones en Java
Capitulo 16- Excepciones en JavaCapitulo 16- Excepciones en Java
Capitulo 16- Excepciones en JavaJonnathan Cuvi
 

La actualidad más candente (20)

Prolog
PrologProlog
Prolog
 
3. conceptos de calidad del software
3. conceptos de calidad del software3. conceptos de calidad del software
3. conceptos de calidad del software
 
Unidad 2 Sintaxis en java
Unidad 2 Sintaxis en javaUnidad 2 Sintaxis en java
Unidad 2 Sintaxis en java
 
Introducción a C++
Introducción a C++Introducción a C++
Introducción a C++
 
Componentes y Librerías - Tópicos avanzados de programación.
Componentes y Librerías - Tópicos avanzados de programación.Componentes y Librerías - Tópicos avanzados de programación.
Componentes y Librerías - Tópicos avanzados de programación.
 
Interfaz con el sistema de archivos
Interfaz con el sistema de archivosInterfaz con el sistema de archivos
Interfaz con el sistema de archivos
 
5 polimorfismo
5 polimorfismo5 polimorfismo
5 polimorfismo
 
6 Curso de POO en Java - clases y objetos
6  Curso de POO en Java - clases y objetos6  Curso de POO en Java - clases y objetos
6 Curso de POO en Java - clases y objetos
 
Diagramas clases presentacion
Diagramas clases presentacionDiagramas clases presentacion
Diagramas clases presentacion
 
Identificadores en Lógia de Programación
Identificadores en Lógia de ProgramaciónIdentificadores en Lógia de Programación
Identificadores en Lógia de Programación
 
Desarrollo de aplicaciones web con casos de uso
Desarrollo de aplicaciones web  con casos de usoDesarrollo de aplicaciones web  con casos de uso
Desarrollo de aplicaciones web con casos de uso
 
Diagramas UML: Componentes y despliegue
Diagramas UML: Componentes y despliegueDiagramas UML: Componentes y despliegue
Diagramas UML: Componentes y despliegue
 
PPT Git GitHub
PPT Git GitHubPPT Git GitHub
PPT Git GitHub
 
Las 4 P en el desarrollo de software
Las 4 P en el desarrollo de softwareLas 4 P en el desarrollo de software
Las 4 P en el desarrollo de software
 
Unidad 2 expresiones regulares
Unidad 2 expresiones regularesUnidad 2 expresiones regulares
Unidad 2 expresiones regulares
 
Modelo V
Modelo VModelo V
Modelo V
 
BIBLIOTECAS PARA C++
BIBLIOTECAS PARA C++BIBLIOTECAS PARA C++
BIBLIOTECAS PARA C++
 
Ciclos
CiclosCiclos
Ciclos
 
Calidad del software
Calidad del softwareCalidad del software
Calidad del software
 
Capitulo 16- Excepciones en Java
Capitulo 16- Excepciones en JavaCapitulo 16- Excepciones en Java
Capitulo 16- Excepciones en Java
 

Destacado (14)

Scala
ScalaScala
Scala
 
Scala en la Practica
Scala en la PracticaScala en la Practica
Scala en la Practica
 
Pf con scala
Pf con scalaPf con scala
Pf con scala
 
Curso de Scala: Trabajando con variables
Curso de Scala: Trabajando con variablesCurso de Scala: Trabajando con variables
Curso de Scala: Trabajando con variables
 
Scala - just good for Java shops?
Scala - just good for Java shops?Scala - just good for Java shops?
Scala - just good for Java shops?
 
JavaFX and Scala - Like Milk and Cookies
JavaFX and Scala - Like Milk and CookiesJavaFX and Scala - Like Milk and Cookies
JavaFX and Scala - Like Milk and Cookies
 
Baño
BañoBaño
Baño
 
Introducción a scala
Introducción a scalaIntroducción a scala
Introducción a scala
 
Koreference
KoreferenceKoreference
Koreference
 
Scala+swing
Scala+swingScala+swing
Scala+swing
 
Scala vs Ruby
Scala vs RubyScala vs Ruby
Scala vs Ruby
 
JavaFX 2 and Scala - Like Milk and Cookies (33rd Degrees)
JavaFX 2 and Scala - Like Milk and Cookies (33rd Degrees)JavaFX 2 and Scala - Like Milk and Cookies (33rd Degrees)
JavaFX 2 and Scala - Like Milk and Cookies (33rd Degrees)
 
Seven ways to kill your presentation
Seven ways to kill your presentationSeven ways to kill your presentation
Seven ways to kill your presentation
 
Scala
ScalaScala
Scala
 

Similar a Programación funcional en Scala

Apuntes de introduccion a la programación
Apuntes de introduccion a la programaciónApuntes de introduccion a la programación
Apuntes de introduccion a la programaciónvictdiazm
 
Introducción a JAVA
Introducción a JAVAIntroducción a JAVA
Introducción a JAVAjohitafresh
 
Informe teórico-getchars-1
Informe teórico-getchars-1Informe teórico-getchars-1
Informe teórico-getchars-1Brenda Jazmin
 
Analisis semantico
Analisis semanticoAnalisis semantico
Analisis semanticoAreli Gómez
 
Java jedi pre
Java jedi preJava jedi pre
Java jedi prejtk1
 
Java jedi prev
Java jedi prevJava jedi prev
Java jedi prevjtk1
 
Fundamentos de Lenguaje de programacion
Fundamentos de Lenguaje de programacionFundamentos de Lenguaje de programacion
Fundamentos de Lenguaje de programacionGermán Sailema
 
10 conceptos basicos_procesadores_lenguaje
10 conceptos basicos_procesadores_lenguaje10 conceptos basicos_procesadores_lenguaje
10 conceptos basicos_procesadores_lenguajeAreli Gómez
 
Cartilla de-java-basico-actualizado
Cartilla de-java-basico-actualizadoCartilla de-java-basico-actualizado
Cartilla de-java-basico-actualizadoRobert Wolf
 
Aspect Oriented Programming introduction
Aspect Oriented Programming introductionAspect Oriented Programming introduction
Aspect Oriented Programming introductionMiguel Pastor
 
Lenguajes de Programación
Lenguajes de Programación Lenguajes de Programación
Lenguajes de Programación lobi7o
 
Lenguajes de-programacion-clase
Lenguajes de-programacion-claseLenguajes de-programacion-clase
Lenguajes de-programacion-claseBeatriz Moreyra
 
Lenguajes de-programacion-clase
Lenguajes de-programacion-claseLenguajes de-programacion-clase
Lenguajes de-programacion-claseBeatriz Moreyra
 

Similar a Programación funcional en Scala (20)

Apuntes de introduccion a la programación
Apuntes de introduccion a la programaciónApuntes de introduccion a la programación
Apuntes de introduccion a la programación
 
Introducción a JAVA
Introducción a JAVAIntroducción a JAVA
Introducción a JAVA
 
Las getchar
Las getcharLas getchar
Las getchar
 
Informe teórico-getchars-1
Informe teórico-getchars-1Informe teórico-getchars-1
Informe teórico-getchars-1
 
Analisis semantico
Analisis semanticoAnalisis semantico
Analisis semantico
 
Manual Teórico - Práctico C++
Manual Teórico -  Práctico C++Manual Teórico -  Práctico C++
Manual Teórico - Práctico C++
 
Lenguaje objective c
Lenguaje objective cLenguaje objective c
Lenguaje objective c
 
LOS C++
LOS C++LOS C++
LOS C++
 
Java jedi pre
Java jedi preJava jedi pre
Java jedi pre
 
Java jedi prev
Java jedi prevJava jedi prev
Java jedi prev
 
Fundamentos de Lenguaje de programacion
Fundamentos de Lenguaje de programacionFundamentos de Lenguaje de programacion
Fundamentos de Lenguaje de programacion
 
10 conceptos basicos_procesadores_lenguaje
10 conceptos basicos_procesadores_lenguaje10 conceptos basicos_procesadores_lenguaje
10 conceptos basicos_procesadores_lenguaje
 
Cartilla de-java-basico-actualizado
Cartilla de-java-basico-actualizadoCartilla de-java-basico-actualizado
Cartilla de-java-basico-actualizado
 
Aspect Oriented Programming introduction
Aspect Oriented Programming introductionAspect Oriented Programming introduction
Aspect Oriented Programming introduction
 
Lenguajes de Programación
Lenguajes de Programación Lenguajes de Programación
Lenguajes de Programación
 
Lenguajes de-programacion-clase
Lenguajes de-programacion-claseLenguajes de-programacion-clase
Lenguajes de-programacion-clase
 
Lenguajes de-programacion-clase
Lenguajes de-programacion-claseLenguajes de-programacion-clase
Lenguajes de-programacion-clase
 
Laboratorio 4
Laboratorio 4Laboratorio 4
Laboratorio 4
 
Tarea 05 OP.docx
Tarea 05 OP.docxTarea 05 OP.docx
Tarea 05 OP.docx
 
Introducción a la algoritmia
Introducción a la algoritmiaIntroducción a la algoritmia
Introducción a la algoritmia
 

Último

TAREA 8 CORREDOR INTEROCEÁNICO DEL PAÍS.pdf
TAREA 8 CORREDOR INTEROCEÁNICO DEL PAÍS.pdfTAREA 8 CORREDOR INTEROCEÁNICO DEL PAÍS.pdf
TAREA 8 CORREDOR INTEROCEÁNICO DEL PAÍS.pdfAntonioGonzalezIzqui
 
VIRUS FITOPATÓGENOS (GENERALIDADES EN PLANTAS)
VIRUS FITOPATÓGENOS (GENERALIDADES EN PLANTAS)VIRUS FITOPATÓGENOS (GENERALIDADES EN PLANTAS)
VIRUS FITOPATÓGENOS (GENERALIDADES EN PLANTAS)ssuser6958b11
 
SEGURIDAD EN CONSTRUCCION PPT PARA EL CIP
SEGURIDAD EN CONSTRUCCION PPT PARA EL CIPSEGURIDAD EN CONSTRUCCION PPT PARA EL CIP
SEGURIDAD EN CONSTRUCCION PPT PARA EL CIPJosLuisFrancoCaldern
 
IPERC Y ATS - SEGURIDAD INDUSTRIAL PARA TODA EMPRESA
IPERC Y ATS - SEGURIDAD INDUSTRIAL PARA TODA EMPRESAIPERC Y ATS - SEGURIDAD INDUSTRIAL PARA TODA EMPRESA
IPERC Y ATS - SEGURIDAD INDUSTRIAL PARA TODA EMPRESAJAMESDIAZ55
 
Sesion 02 Patentes REGISTRO EN INDECOPI PERU
Sesion 02 Patentes REGISTRO EN INDECOPI PERUSesion 02 Patentes REGISTRO EN INDECOPI PERU
Sesion 02 Patentes REGISTRO EN INDECOPI PERUMarcosAlvarezSalinas
 
Propositos del comportamiento de fases y aplicaciones
Propositos del comportamiento de fases y aplicacionesPropositos del comportamiento de fases y aplicaciones
Propositos del comportamiento de fases y aplicaciones025ca20
 
estadisticasII Metodo-de-la-gran-M.pdf
estadisticasII   Metodo-de-la-gran-M.pdfestadisticasII   Metodo-de-la-gran-M.pdf
estadisticasII Metodo-de-la-gran-M.pdfFlorenciopeaortiz
 
CICLO DE DEMING que se encarga en como mejorar una empresa
CICLO DE DEMING que se encarga en como mejorar una empresaCICLO DE DEMING que se encarga en como mejorar una empresa
CICLO DE DEMING que se encarga en como mejorar una empresaSHERELYNSAMANTHAPALO1
 
Manual de Usuario Estacion total Sokkia SERIE SET10K.pdf
Manual de Usuario Estacion total Sokkia SERIE SET10K.pdfManual de Usuario Estacion total Sokkia SERIE SET10K.pdf
Manual de Usuario Estacion total Sokkia SERIE SET10K.pdfSandXmovex
 
Hanns Recabarren Diaz (2024), Implementación de una herramienta de realidad v...
Hanns Recabarren Diaz (2024), Implementación de una herramienta de realidad v...Hanns Recabarren Diaz (2024), Implementación de una herramienta de realidad v...
Hanns Recabarren Diaz (2024), Implementación de una herramienta de realidad v...Francisco Javier Mora Serrano
 
AMBIENTES SEDIMENTARIOS GEOLOGIA TIPOS .pptx
AMBIENTES SEDIMENTARIOS GEOLOGIA TIPOS .pptxAMBIENTES SEDIMENTARIOS GEOLOGIA TIPOS .pptx
AMBIENTES SEDIMENTARIOS GEOLOGIA TIPOS .pptxLuisvila35
 
183045401-Terminal-Terrestre-de-Trujillo.pdf
183045401-Terminal-Terrestre-de-Trujillo.pdf183045401-Terminal-Terrestre-de-Trujillo.pdf
183045401-Terminal-Terrestre-de-Trujillo.pdfEdwinAlexanderSnchez2
 
4.6 DEFINICION DEL PROBLEMA DE ASIGNACION.pptx
4.6 DEFINICION DEL PROBLEMA DE ASIGNACION.pptx4.6 DEFINICION DEL PROBLEMA DE ASIGNACION.pptx
4.6 DEFINICION DEL PROBLEMA DE ASIGNACION.pptxGARCIARAMIREZCESAR
 
3039_ftg_01Entregable 003_Matematica.pptx
3039_ftg_01Entregable 003_Matematica.pptx3039_ftg_01Entregable 003_Matematica.pptx
3039_ftg_01Entregable 003_Matematica.pptxJhordanGonzalo
 
Fe_C_Tratamientos termicos_uap _3_.ppt
Fe_C_Tratamientos termicos_uap   _3_.pptFe_C_Tratamientos termicos_uap   _3_.ppt
Fe_C_Tratamientos termicos_uap _3_.pptVitobailon
 
Topografía 1 Nivelación y Carretera en la Ingenierías
Topografía 1 Nivelación y Carretera en la IngenieríasTopografía 1 Nivelación y Carretera en la Ingenierías
Topografía 1 Nivelación y Carretera en la IngenieríasSegundo Silva Maguiña
 
Historia de la Arquitectura II, 1era actividad..pdf
Historia de la Arquitectura II, 1era actividad..pdfHistoria de la Arquitectura II, 1era actividad..pdf
Historia de la Arquitectura II, 1era actividad..pdfIsbelRodrguez
 
CHARLA DE INDUCCIÓN SEGURIDAD Y SALUD OCUPACIONAL
CHARLA DE INDUCCIÓN SEGURIDAD Y SALUD OCUPACIONALCHARLA DE INDUCCIÓN SEGURIDAD Y SALUD OCUPACIONAL
CHARLA DE INDUCCIÓN SEGURIDAD Y SALUD OCUPACIONALKATHIAMILAGRITOSSANC
 
Tiempos Predeterminados MOST para Estudio del Trabajo II
Tiempos Predeterminados MOST para Estudio del Trabajo IITiempos Predeterminados MOST para Estudio del Trabajo II
Tiempos Predeterminados MOST para Estudio del Trabajo IILauraFernandaValdovi
 
Parámetros de Perforación y Voladura. para Plataformas
Parámetros de  Perforación y Voladura. para PlataformasParámetros de  Perforación y Voladura. para Plataformas
Parámetros de Perforación y Voladura. para PlataformasSegundo Silva Maguiña
 

Último (20)

TAREA 8 CORREDOR INTEROCEÁNICO DEL PAÍS.pdf
TAREA 8 CORREDOR INTEROCEÁNICO DEL PAÍS.pdfTAREA 8 CORREDOR INTEROCEÁNICO DEL PAÍS.pdf
TAREA 8 CORREDOR INTEROCEÁNICO DEL PAÍS.pdf
 
VIRUS FITOPATÓGENOS (GENERALIDADES EN PLANTAS)
VIRUS FITOPATÓGENOS (GENERALIDADES EN PLANTAS)VIRUS FITOPATÓGENOS (GENERALIDADES EN PLANTAS)
VIRUS FITOPATÓGENOS (GENERALIDADES EN PLANTAS)
 
SEGURIDAD EN CONSTRUCCION PPT PARA EL CIP
SEGURIDAD EN CONSTRUCCION PPT PARA EL CIPSEGURIDAD EN CONSTRUCCION PPT PARA EL CIP
SEGURIDAD EN CONSTRUCCION PPT PARA EL CIP
 
IPERC Y ATS - SEGURIDAD INDUSTRIAL PARA TODA EMPRESA
IPERC Y ATS - SEGURIDAD INDUSTRIAL PARA TODA EMPRESAIPERC Y ATS - SEGURIDAD INDUSTRIAL PARA TODA EMPRESA
IPERC Y ATS - SEGURIDAD INDUSTRIAL PARA TODA EMPRESA
 
Sesion 02 Patentes REGISTRO EN INDECOPI PERU
Sesion 02 Patentes REGISTRO EN INDECOPI PERUSesion 02 Patentes REGISTRO EN INDECOPI PERU
Sesion 02 Patentes REGISTRO EN INDECOPI PERU
 
Propositos del comportamiento de fases y aplicaciones
Propositos del comportamiento de fases y aplicacionesPropositos del comportamiento de fases y aplicaciones
Propositos del comportamiento de fases y aplicaciones
 
estadisticasII Metodo-de-la-gran-M.pdf
estadisticasII   Metodo-de-la-gran-M.pdfestadisticasII   Metodo-de-la-gran-M.pdf
estadisticasII Metodo-de-la-gran-M.pdf
 
CICLO DE DEMING que se encarga en como mejorar una empresa
CICLO DE DEMING que se encarga en como mejorar una empresaCICLO DE DEMING que se encarga en como mejorar una empresa
CICLO DE DEMING que se encarga en como mejorar una empresa
 
Manual de Usuario Estacion total Sokkia SERIE SET10K.pdf
Manual de Usuario Estacion total Sokkia SERIE SET10K.pdfManual de Usuario Estacion total Sokkia SERIE SET10K.pdf
Manual de Usuario Estacion total Sokkia SERIE SET10K.pdf
 
Hanns Recabarren Diaz (2024), Implementación de una herramienta de realidad v...
Hanns Recabarren Diaz (2024), Implementación de una herramienta de realidad v...Hanns Recabarren Diaz (2024), Implementación de una herramienta de realidad v...
Hanns Recabarren Diaz (2024), Implementación de una herramienta de realidad v...
 
AMBIENTES SEDIMENTARIOS GEOLOGIA TIPOS .pptx
AMBIENTES SEDIMENTARIOS GEOLOGIA TIPOS .pptxAMBIENTES SEDIMENTARIOS GEOLOGIA TIPOS .pptx
AMBIENTES SEDIMENTARIOS GEOLOGIA TIPOS .pptx
 
183045401-Terminal-Terrestre-de-Trujillo.pdf
183045401-Terminal-Terrestre-de-Trujillo.pdf183045401-Terminal-Terrestre-de-Trujillo.pdf
183045401-Terminal-Terrestre-de-Trujillo.pdf
 
4.6 DEFINICION DEL PROBLEMA DE ASIGNACION.pptx
4.6 DEFINICION DEL PROBLEMA DE ASIGNACION.pptx4.6 DEFINICION DEL PROBLEMA DE ASIGNACION.pptx
4.6 DEFINICION DEL PROBLEMA DE ASIGNACION.pptx
 
3039_ftg_01Entregable 003_Matematica.pptx
3039_ftg_01Entregable 003_Matematica.pptx3039_ftg_01Entregable 003_Matematica.pptx
3039_ftg_01Entregable 003_Matematica.pptx
 
Fe_C_Tratamientos termicos_uap _3_.ppt
Fe_C_Tratamientos termicos_uap   _3_.pptFe_C_Tratamientos termicos_uap   _3_.ppt
Fe_C_Tratamientos termicos_uap _3_.ppt
 
Topografía 1 Nivelación y Carretera en la Ingenierías
Topografía 1 Nivelación y Carretera en la IngenieríasTopografía 1 Nivelación y Carretera en la Ingenierías
Topografía 1 Nivelación y Carretera en la Ingenierías
 
Historia de la Arquitectura II, 1era actividad..pdf
Historia de la Arquitectura II, 1era actividad..pdfHistoria de la Arquitectura II, 1era actividad..pdf
Historia de la Arquitectura II, 1era actividad..pdf
 
CHARLA DE INDUCCIÓN SEGURIDAD Y SALUD OCUPACIONAL
CHARLA DE INDUCCIÓN SEGURIDAD Y SALUD OCUPACIONALCHARLA DE INDUCCIÓN SEGURIDAD Y SALUD OCUPACIONAL
CHARLA DE INDUCCIÓN SEGURIDAD Y SALUD OCUPACIONAL
 
Tiempos Predeterminados MOST para Estudio del Trabajo II
Tiempos Predeterminados MOST para Estudio del Trabajo IITiempos Predeterminados MOST para Estudio del Trabajo II
Tiempos Predeterminados MOST para Estudio del Trabajo II
 
Parámetros de Perforación y Voladura. para Plataformas
Parámetros de  Perforación y Voladura. para PlataformasParámetros de  Perforación y Voladura. para Plataformas
Parámetros de Perforación y Voladura. para Plataformas
 

Programación funcional en Scala

  • 1.
  • 2.
  • 3. UNIVERSIDAD DE MÁLAGA ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA INFORMÁTICA INGENIERO EN INFORMÁTICA PROGRAMACIÓN FUNCIONAL EN SCALA (FUNCTIONAL PROGRAMMING IN SCALA) Realizado por RUBÉN PÉREZ LUJANO Dirigido por JOSÉ ENRIQUE GALLARDO RUIZ Departamento LENGUAJES Y CIENCIAS DE LA COMPUTACIÓN MÁLAGA, SEPTIEMBRE 2016
  • 4.
  • 5. Dedicado a la memoria de mi padre
  • 6.
  • 7. Agradecimientos Después de recorrer un largo camino ha llegado el momento de parar, coger aire, mirar hacia atrás un instante y dar las gracias a todas esas personas que en algún momento han formado parte del mismo, que han compartido los mejores momentos, que me han ayudado a mirar hacia delante a pesar de las adversidades y junto a las que me gustaría continuar recorriendo el camino de la vida. Quisiera comenzar dando las gracias a D. José Enrique Gallardo Ruiz, tutor del proyecto, por su comprensión, dedicación y por el gran esfuerzo realizado, así como a D. Blas Carlos Ruiz Jiménez, un gran profesor y la persona con la que comencé mi proyecto. Gracias a Irene, mi mujer, por saber sacarme una sonrisa en esos días grises, por tenderme la mano y ayudar a levantarme en los días más oscuros y por darme ánimos para continuar cuando más duro se hacía el camino. Gracias por hacerme el hombre más feliz del mundo. Gracias por apostar por mí. Tú siempre has sido y serás mi apuesta. Gracias a Carmen, mi madre, por los valores que me ha transmitido, por todo lo que me ha enseñado durante la vida y por creer en mí hasta el final. Gracias por esa canción inolvidable. Gracias a Lidia, mi hermana, por su cariño, su ayuda, sus bromas y por confiar ciegamente en mí. Gracias por todos y cada uno de los momentos que hemos vivido. Gracias a mis titos, Juan y Paqui, por ofrecer siempre todo su apoyo y demostrarme que siempre podré contar con ellos. Gracias a mis amigos Nacho y Jesús, con los que he compartido algunos de los mejores momentos de este camino. Gracias por vuestros consejos, por esas tardes de risas en el salón. Gracias a Dña. Lidia Fuentes por ofrecerme esa beca en el momento que más lo necesitaba y a Dña. Mariam Cobaleda por todos los buenos consejos que me dio. Gracias a todos los miembros de los centros de día para personas mayores de Estepona y Coín por acogerme en vuestra familia y por todos los buenos momentos que compartimos. Para finalizar, aunque no los mencione de una forma explícita, quiero dar las gracias a mis compañeros de universidad y a mis compañeros de piso. A todos, eternamente agradecido.
  • 8.
  • 9. Introducción La elección de un lenguaje para introducir la programación a los alumnos de las actuales ingenierías en informática es una decisión trascendental; esta elección está ligada a la pregunta: ¿qué características se deben exigir a un “primer” lenguaje para describir de forma limpia y sencilla los conceptos de la programación? Hoy en día es comúnmente aceptado entre los profesionales de la enseñanza (y entre los alum- nos) que hay dos paradigmas esenciales que simplifican los conceptos de la programación, y que debe conocer un futuro informático: el funcional y el orientado a objetos. Sin embargo es difícil encontrar un lenguaje que integre ambos paradigmas de forma sencilla si se parte de la base de que tal lenguaje será el primer contacto de un estudiante con la programación. Además de esto, se quiere una buena elección desde el punto de vista del programador profesional; es decir, tal lenguaje debe facilitar de forma “natural” el aprendizaje de los principales lenguajes con los que se enfrentará el futuro informático profesional. Entre la oferta actual de lenguajes hay uno que cada vez toma más adeptos en el mundo edu- cativo: el lenguaje Scala. Scala es un lenguaje de programación multiparadigma diseñado para expresar patrones comunes de programación en forma concisa, elegante y con tipos seguros. Integra sutilmente características de lenguajes funcionales y orientados a objetos. La imple- mentación actual corre en la máquina virtual de Java y es compatible con las aplicaciones Java existentes; por ello el uso de Scala como un primer lenguaje será un puente importante con el mundo de la programación profesional. El trabajo en Scala surge a partir de un esfuerzo de investigación para desarrollar un mejor soporte de los lenguajes de programación para la composición de software. Hay dos hipótesis que se desea validar con el experimento Scala. Primera, se postula que un lenguaje de progra- mación para la composición de software necesita ser escalable en el sentido de que los mismos conceptos pueden describir tanto partes pequeñas como grandes. Por tanto, los autores se han concentrado en los mecanismos para la abstracción, composición y descomposición en vez de añadir un conjunto grande de primitivas que pueden ser útiles para los componentes a algún nivel de escala, pero no a otro nivel. Segundo, se postula, que el soporte escalable para los componentes puede ser previsto por un lenguaje de programación que unifica y generaliza la programación orientada a objetos y la funcional. Para los lenguajes con tipos estáticos, de los que Scala es un ejemplo, estos dos paradigmas estaban hasta ahora en gran medida separados. El principal objetivo de este PFC es desarrollar un material didáctico a modo de una guía donde el programador (tanto el alumno como el profesor) pueda ver de forma clara y conci- sa, tras una primera toma de contacto con el lenguaje, las ventajas de utilizar un lenguaje de programación multiparadigma como Scala en la resolución de los diferentes problemas que se puedan plantear. En el Capítulo 1: Scala « página 1 » se realiza una breve introducción a Scala, se presenta el lenguaje y se analizan conceptos básicos de los lenguajes de programación como los tipos de datos básicos, los operadores, las estructuras de control o la evaluación en Scala. Página I
  • 10. II En el Capítulo 2: Programación Orientada a Objetos en Scala « página 31 » se realiza un análisis de Scala como un lenguaje orientado a objetos puro, se presenta la jerarquía de clases y conceptos como polimorfismo, genericidad, acotación de tipos y varianza en Scala. En las primeras secciones del Capítulo 3: Programación Funcional en Scala « página 53 » se describe el paradigma funcional utilizando Scala y se enseñan conceptos de la programación a través del estilo funcional. Posteriormente, se detalla la implementación en Scala de las es- tructuras básicas de la programación, aprovechando dichas estructuras para el aprendizaje de la programación funcional. Para finalizar el capítulo se hace un repaso por las colecciones que Scala ofrece al programador. Es conocido que es posible unificar los estilos imperativos y orientado a objetos con el fun- cional puro a través de mónadas, pero el uso de éstas no es apropiado para un curso introductorio a la programación. Después de haber estudiado previamente los aspectos fundamentales de la programación funcional, en el Capítulo 4: Programación Funcional Avanzada en Scala « página 129 » se analizan conceptos más complejos de este paradigma, como la programación monádica a través de las mónadas más populares del lenguaje. En el Capítulo 5: Tests en Scala « página 155 » se presentan brevemente algunas de las soluciones más populares para realizar pruebas al código realizado en Scala. Scala propone una solución basada en el paso asíncrono de mensajes inmutables para resol- ver la problemática de la concurrencia. El modelo de actores de Scala, así como la biblioteca Akka, se presentan en el Capítulo 6: Concurrencia en Scala. Modelo de actores « página 165 ». En el Capítulo 7: Conclusiones « página 185 » se justifica razonadamente el uso de Scala como lenguaje de programación adecuado para ser utilizado dentro del ámbito de la docencia. Finalmente, en el Capítulo 8: Solución a los ejercicios propuestos « página 189 » se pueden encontrar las soluciones de los ejercicios propuestos a lo largo de la guía.
  • 11. Índice general 1. Scala 1 1.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1.1. Scala. Un lenguaje escalable . . . . . . . . . . . . . . . . . . . . . . . 2 1.1.2. Paradigmas de la programación . . . . . . . . . . . . . . . . . . . . . 2 1.1.2.1. Scala. Un lenguaje multiparadigma . . . . . . . . . . . . . . 3 1.1.3. Preparación del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.1.3.1. Descargar Scala . . . . . . . . . . . . . . . . . . . . . . . . 3 1.1.3.2. Herramientas de Scala . . . . . . . . . . . . . . . . . . . . . 3 El compilador de Scala: scalac . . . . . . . . . . . . . . . . . . 3 El intérprete de código: scala . . . . . . . . . . . . . . . . . . . 4 Scala como lenguaje compilado . . . . . . . . . . . . . . . . . 4 Scala como lenguaje interpretado desde un script . . . . . . . . 5 Scala como lenguaje interpretado desde un intérprete . . . . . . 5 1.2. Conceptos básicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.2.1. Elementos de un lenguaje de programación . . . . . . . . . . . . . . . 6 1.2.2. Elementos básicos en Scala . . . . . . . . . . . . . . . . . . . . . . . 6 1.2.2.1. Tipos de datos básicos en Scala . . . . . . . . . . . . . . . . 6 1.2.2.2. Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 Operadores de igualdad. . . . . . . . . . . . . . . . . . . . . . 15 1.2.2.3. Nombrar expresiones . . . . . . . . . . . . . . . . . . . . . 15 1.2.2.4. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.2.3. Uso del carácter punto y coma (;) en Scala . . . . . . . . . . . . . . . . 17 1.3. Bloques en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 1.3.1. Visibilidad y bloques en Scala . . . . . . . . . . . . . . . . . . . . . . 18 1.4. Evaluación en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 1.4.1. Evaluación de expresiones . . . . . . . . . . . . . . . . . . . . . . . . 18 1.4.2. Evaluación de funciones . . . . . . . . . . . . . . . . . . . . . . . . . 19 1.4.3. Sistema de Evaluación de Scala . . . . . . . . . . . . . . . . . . . . . 19 1.4.3.1. Valores de las definiciones . . . . . . . . . . . . . . . . . . . 19 1.4.3.2. Evaluación de Booleanos . . . . . . . . . . . . . . . . . . . 19 1.4.4. Ámbito y visibilidad de las variables . . . . . . . . . . . . . . . . . . . 20 1.5. Estructuras de control en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . 21 1.5.1. Estructuras condicionales . . . . . . . . . . . . . . . . . . . . . . . . . 21 1.5.1.1. La sentencia if . . . . . . . . . . . . . . . . . . . . . . . . . 21 La sentencia if / else . . . . . . . . . . . . . . . . . . . . . . . 22 La sentencia if...else if ...else . . . . . . . . . . . . . . . . . 22 Estructuras condicionales anidadas . . . . . . . . . . . . . . . . 23 1.5.2. Estructuras iterativas . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 Página III
  • 12. IV ÍNDICE GENERAL 1.5.2.1. Bucles while . . . . . . . . . . . . . . . . . . . . . . . . . . 24 1.5.2.2. Bucles do...while . . . . . . . . . . . . . . . . . . . . . . . 24 1.5.2.3. Bucles for . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 Bucles for con rangos . . . . . . . . . . . . . . . . . . . . . . . 25 Bucles for con colecciones . . . . . . . . . . . . . . . . . . . . 26 Bucles for con filtros . . . . . . . . . . . . . . . . . . . . . . . 27 Bucles for con yield. . . . . . . . . . . . . . . . . . . . . . . . 28 1.6. Interacción con Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 1.6.1. Ejecución sobre la JVM . . . . . . . . . . . . . . . . . . . . . . . . . 29 1.7. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 2. Programación Orientada a Objetos en Scala 31 2.1. Introducción a la programación orientada a objetos en Scala . . . . . . . . . . 31 2.1.1. Características principales de la programación orientada a objetos . . . 31 2.1.2. Scala como lenguaje orientado a objetos . . . . . . . . . . . . . . . . . 31 2.2. Paquetes, clases, objetos y namespaces . . . . . . . . . . . . . . . . . . . . . . 32 2.2.1. Objetos Singleton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.2.2. Módulos, objetos, paquetes y namespaces . . . . . . . . . . . . . . . . 32 2.2.3. Clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 2.2.4. Objetos funcionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 2.2.4.1. Constructores . . . . . . . . . . . . . . . . . . . . . . . . . 35 2.2.4.2. Sobrescritura de métodos . . . . . . . . . . . . . . . . . . . 35 2.2.4.3. Precondiciones . . . . . . . . . . . . . . . . . . . . . . . . . 35 2.2.4.4. Atributos y Métodos . . . . . . . . . . . . . . . . . . . . . . 36 2.2.4.5. Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 2.3. Jerarquía de clases en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 2.3.1. Herencia en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 2.3.1.1. Rasgos y herencia múltiple en Scala . . . . . . . . . . . . . 39 2.3.1.2. Funcionamiento de los rasgos . . . . . . . . . . . . . . . . . 39 2.3.1.3. Rasgos como modificaciones apiladas . . . . . . . . . . . . 40 2.3.1.4. ¿Cuándo usar rasgos? . . . . . . . . . . . . . . . . . . . . . 42 2.4. Patrones y clases case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 2.4.1. Clases case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 2.4.2. Patrones: estructuras y tipos . . . . . . . . . . . . . . . . . . . . . . . 43 2.4.2.1. Patrones comodín . . . . . . . . . . . . . . . . . . . . . . . 43 2.4.2.2. Patrones constantes . . . . . . . . . . . . . . . . . . . . . . 43 2.4.2.3. Patrones variables . . . . . . . . . . . . . . . . . . . . . . . 44 2.4.2.4. Patrones constructores . . . . . . . . . . . . . . . . . . . . . 44 2.4.2.5. Patrones de secuencia . . . . . . . . . . . . . . . . . . . . . 44 2.4.2.6. Patrones tipados . . . . . . . . . . . . . . . . . . . . . . . . 45 2.5. Polimorfismo en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 2.5.1. Acotación de tipos y varianza . . . . . . . . . . . . . . . . . . . . . . 47 2.5.1.1. Acotación de tipos . . . . . . . . . . . . . . . . . . . . . . . 47 2.5.1.2. Varianza . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
  • 13. ÍNDICE GENERAL V 3. Programación Funcional en Scala 53 3.1. Introducción a la programación funcional . . . . . . . . . . . . . . . . . . . . 53 3.1.1. Características de los Lenguajes de Programación Funcionales . . . . . 53 3.1.2. Scala como lenguaje funcional . . . . . . . . . . . . . . . . . . . . . . 54 3.1.3. ¿Por qué la programación funcional? . . . . . . . . . . . . . . . . . . 54 3.2. Sentido estricto y amplio de la programación funcional . . . . . . . . . . . . . 54 3.2.1. ¿Qué son las funciones puras? . . . . . . . . . . . . . . . . . . . . . . 55 3.3. Funciones y cierres en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 3.3.1. Definición de funciones . . . . . . . . . . . . . . . . . . . . . . . . . 55 3.3.2. Funciones anidadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 3.3.3. Diferencias entre métodos y funciones . . . . . . . . . . . . . . . . . . 57 3.3.4. Funciones de primera clase . . . . . . . . . . . . . . . . . . . . . . . . 58 3.3.5. Funciones anónimas y funciones valor . . . . . . . . . . . . . . . . . . 58 3.3.6. Cierres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 3.4. Recursión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 3.4.1. Importancia de la pila del sistema en recursión. . . . . . . . . . . . . . 60 3.4.1.1. La pila de Java . . . . . . . . . . . . . . . . . . . . . . . . . 61 3.4.1.2. Contexto de pila . . . . . . . . . . . . . . . . . . . . . . . . 61 3.4.2. Recursión de cola . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 3.5. Currificación y Parcialización . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 3.5.1. Currificacion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 3.5.2. Parcialización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 3.6. Orden Superior . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 3.6.1. Funciones de orden superior . . . . . . . . . . . . . . . . . . . . . . . 65 3.7. Funciones polimórficas. Genericidad . . . . . . . . . . . . . . . . . . . . . . . 66 3.8. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 3.8.1. Ejercicio Resuelto . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 3.8.2. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 3.9. Programación funcional estricta y perezosa . . . . . . . . . . . . . . . . . . . 76 3.9.1. Funciones estrictas y no estrictas . . . . . . . . . . . . . . . . . . . . . 76 3.10. Estructuras de Datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 3.10.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 3.10.1.1. ¿Qué es una teoría?. Definición de Estructuras de Datos . . . 78 3.10.1.2. La abstracción en la programación . . . . . . . . . . . . . . 78 3.10.1.3. Datos, Tipos de Datos, Estructuras de Datos y Tipos Abstrac- tos de Datos . . . . . . . . . . . . . . . . . . . . . . . . . . 79 3.10.2. Definición de Estructuras de Datos en Lenguajes Funcionales . . . . . 80 3.10.2.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . 80 3.10.2.2. Definición . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 3.10.2.3. Los Naturales . . . . . . . . . . . . . . . . . . . . . . . . . 82 Ejercicios resueltos. . . . . . . . . . . . . . . . . . . . . . . . 86 3.10.3. Estructuras de datos lineales. Listas . . . . . . . . . . . . . . . . . . . 89 3.10.3.1. TAD Lista . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 3.10.3.2. Ejercicios sobre el TAD Lista . . . . . . . . . . . . . . . . . 94 3.10.4. Estructuras de datos no lineales . . . . . . . . . . . . . . . . . . . . . 95 3.10.4.1. Árboles . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 3.10.4.2. Arboles Binarios . . . . . . . . . . . . . . . . . . . . . . . . 97 3.10.4.3. Arboles Binarios de Búsqueda . . . . . . . . . . . . . . . . 99
  • 14. VI ÍNDICE GENERAL 3.10.5. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 3.11. Colecciones en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 3.11.1. El paquete scala.collection . . . . . . . . . . . . . . . . . . . . . . . . 103 3.11.2. Iteradores en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 3.11.2.1. Métodos definidos para el tipo Iterator en Scala. . . . . . . . 105 3.11.3. Colecciones inmutables . . . . . . . . . . . . . . . . . . . . . . . . . 107 3.11.3.1. Definición de rangos en Scala. La clase Range. . . . . . . . . 107 Métodos definidos para el tipo Range en Scala. . . . . . . . . . 108 3.11.3.2. Definición de tuplas en Scala. La clase Tuple . . . . . . . . . 108 3.11.3.3. Listas en Scala. La clase List . . . . . . . . . . . . . . . . . 110 Métodos definidos para el tipo List en Scala. . . . . . . . . . . 112 Ejercicios sobre listas . . . . . . . . . . . . . . . . . . . . . . . 112 3.11.3.4. Vectores en Scala. La clase Vector . . . . . . . . . . . . . . 114 3.11.3.5. Flujos en Scala. La clase Stream . . . . . . . . . . . . . . . 115 3.11.3.6. Conjuntos en Scala. La clase Set . . . . . . . . . . . . . . . 116 Recorriendo conjuntos . . . . . . . . . . . . . . . . . . . . . . 117 Métodos definidos para el tipo Set en Scala. . . . . . . . . . . . 118 3.11.3.7. Asociaciones en Scala. La clase Map . . . . . . . . . . . . . 119 Métodos definidos para el tipo Map en Scala. . . . . . . . . . . 120 3.11.3.8. Selección de una colección . . . . . . . . . . . . . . . . . . 120 3.11.3.9. Colecciones como funciones . . . . . . . . . . . . . . . . . 121 3.11.4. Expresiones for como una combinación elegante de funciones de orden superior . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 3.11.4.1. Traducción de expresiones for con un generador . . . . . . . 122 3.11.4.2. Traducción de expresiones for con un generador y un filtro . 123 3.11.4.3. Traducción de expresiones for con dos generadores . . . . . 123 3.11.4.4. Traducción de bucles for . . . . . . . . . . . . . . . . . . . 124 3.11.4.5. Definición de map, flatMap y filter con expresiones for . . . 125 3.11.4.6. Uso generalizado de for en estructuras de datos . . . . . . . 125 3.11.5. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 4. Programación Funcional Avanzada en Scala 129 4.1. Implícitos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 4.1.1. Parámetros ímplicitos en funciones . . . . . . . . . . . . . . . . . . . 129 4.1.2. Clases implícitas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 4.2. Tipos en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 4.2.1. Definición de tipos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 4.2.2. Parámetros de tipo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 4.2.2.1. Nombres de los parámetros de tipo. . . . . . . . . . . . . . . 132 4.2.3. Constructores de tipos. . . . . . . . . . . . . . . . . . . . . . . . . . . 133 4.2.4. Tipos compuestos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 4.2.5. Tipos estructurales . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 4.2.6. Tipos de orden superior. . . . . . . . . . . . . . . . . . . . . . . . . . 134 4.2.7. Tipos existenciales . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 4.3. Teoría de categorías . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 4.3.1. El patrón funcional Funtor . . . . . . . . . . . . . . . . . . . . . . . . 137 4.3.2. El patrón funcional Mónada . . . . . . . . . . . . . . . . . . . . . . . 138 4.3.2.1. Reglas que deben satisfacer las mónadas . . . . . . . . . . . 139
  • 15. ÍNDICE GENERAL VII 4.3.2.2. Importancia de las propiedades de las mónadas en las expre- siones for . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 4.3.2.3. Map en las mónadas . . . . . . . . . . . . . . . . . . . . . . 141 4.3.2.4. La importancia de las mónadas . . . . . . . . . . . . . . . . 142 4.3.2.5. La mónada Identidad . . . . . . . . . . . . . . . . . . . . . 142 4.3.2.6. Envolviendo el contexto con mónadas. La clase monádica Try 143 La mónada Try. . . . . . . . . . . . . . . . . . . . . . . . . . . 146 4.4. Manejo de errores sin usar excepciones . . . . . . . . . . . . . . . . . . . . . 149 4.4.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 4.4.2. Tipo de datos Option . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 4.4.3. Tipo de datos Either . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 4.5. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 5. Tests en Scala 155 5.1. Afirmaciones Asserts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 5.1.1. Assert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 5.1.2. Ensuring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 5.2. Herramientas específicas para tests . . . . . . . . . . . . . . . . . . . . . . . . 159 5.2.1. ScalaTest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 5.3. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 6. Concurrencia en Scala. Modelo de actores 165 6.1. Programación Concurrente. Problemática . . . . . . . . . . . . . . . . . . . . 165 6.1.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 6.1.1.1. Sistema Reactivo Vs Sistema Transformacional . . . . . . . 165 6.1.2. Speed-Up en programación concurrente . . . . . . . . . . . . . . . . . 166 6.1.3. Problemática . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166 6.1.3.1. Propiedades de los programas concurrentes . . . . . . . . . . 166 6.1.3.2. Bloqueos y secciones críticas . . . . . . . . . . . . . . . . . 167 Problemas del uso de bloqueos . . . . . . . . . . . . . . . . . . 167 6.1.3.3. Concurrencia en Java . . . . . . . . . . . . . . . . . . . . . 167 6.2. Modelo de actores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 6.2.1. Origen del Modelo de Actores . . . . . . . . . . . . . . . . . . . . . . 168 6.2.2. Filosofía del Modelo de Actores . . . . . . . . . . . . . . . . . . . . . 169 6.3. Actores en Scala. Librería scala.actors . . . . . . . . . . . . . . . . . . . . . . 170 6.3.1. Definición de actores . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 6.3.2. Estado de los actores . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 6.3.3. Mejora del rendimiento con react . . . . . . . . . . . . . . . . . . . . 173 6.4. Actores en Scala con Akka . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175 6.4.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175 6.4.1.1. Diferencias entre Akka y la librería Actors de Scala. . . . . . 175 6.4.2. Definición y estado de los actores . . . . . . . . . . . . . . . . . . . . 177 6.5. Buenas prácticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 6.5.1. Ausencia de bloqueos . . . . . . . . . . . . . . . . . . . . . . . . . . 182 6.5.2. Comunicación exclusiva mediante mensajes . . . . . . . . . . . . . . . 182 6.5.3. Mensajes inmutables . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 6.5.4. Mensajes autocontenidos . . . . . . . . . . . . . . . . . . . . . . . . . 182 6.6. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
  • 16. VIII ÍNDICE GENERAL 7. Conclusiones 185 8. Solución a los ejercicios propuestos 189 8.1. Evaluación en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 8.2. Introducción a la Programación Funcional . . . . . . . . . . . . . . . . . . . . 189 8.3. Estructuras de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 8.3.1. TAD Lista . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 8.3.2. TAD Arbol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 8.4. Colecciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 8.4.1. Tipo List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 8.4.2. Otras colecciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 8.5. Programación Funcional Avanzada . . . . . . . . . . . . . . . . . . . . . . . . 204 8.6. Tests en Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206 8.7. Concurrencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 Lista de tablas 210 Listados de algoritmos 211 Referencias 219 Glosario 221 Acrónimos 225
  • 17. Capítulo 1 Scala 1.1. Introducción El nombre Scala significa: diseñado para crecer con la demanda de sus usuarios (Scala- ble language). Scala es un lenguaje de programación multi-paradigma diseñado para expresar patrones comunes de programación de forma concisa, elegante y con tipos estáticos. Integra elegantemente características de lenguajes funcionales y orientados a objetos, lo cual hace que la escalabilidad sea una de las principales características del lenguaje. La implementación ac- tual corre en la máquina virtual de Java y es compatible con las aplicaciones existentes en Java. [7] Su creador, Martin Odersky1 , y su equipo comenzaron el desarrollo de este nuevo lengua- je de código abierto en el año 2001, en el laboratorio de métodos de programación en École Polytechnique Fédérale de Lausanne (EPFL). Scala hizo su aparición pública sobre la plataforma Máquina Virtual de Java (JVM) en enero de 2004 y unos meses después haría lo propio sobre la plataforma .NET. Aunque se trata de un elemento relativamente novedoso dentro del espacio de los lenguajes de programación, ha adquirido una notable popularidad y las ventajas que ofrece han hecho ya que cada vez más empresas apuesten por Scala ( Twitter, Linked-in, Foursquare, The Guardian, ...). Las características de Scala le hacen un lenguaje ideal para ser utilizado en centros de computación, centros de hosting,..., en los que las aplicaciones se ejecutan de forma parale- la en los supercomputadores, servidores,...que los conforman. Además, Scala es uno de los lenguajes de programación que se utilizan para desarrollar aplicaciones para los dispositivos Android. “ Si le das a una persona un pescado, comerá un día. Si enseñas a pescar a una persona, comerá toda su vida. Si das herramientas a una persona, puede construirse 1 Martin Odersky es profesor de la EPFL en Lausanne, Suiza y ha trabajado en los lenguajes de programación durante la mayor parte de su carrera. Estudió programación estructurada y orientada a objetos como estudiante de doctorado de Niklaus Wirth. Después, mientras trabajaba en IBM y en la Universidad de Yale se enamoró de la programación funcional. Cuando apareció Java, comenzó a añadir construcciones de programación funcionales para la nueva plataforma, lo cual dio lugar a Pizza y GJ y eventualmente a Java 5 con los genéricos. Durante ese tiempo también desarrolló javac, el compilador de referencia actual para Java. En los últimos 10 años, Martin ha centrado su trabajo en la unificación de los paradigmas de programación funcional y programación orientada a objetos en el lenguaje Scala. Scala pasó rápidamente del laboratorio de investigación a convertirse en una herramienta de código abierto y en un lenguaje industrial. Odersky ahora es el encargado de supervisar el desarrollo de Scala como jefe del grupo de programación de la EPFL y como presidente de la empresa Typesafe. Página 1
  • 18. una caña de pescar, ¡y muchas otras herramientas!. Incluso podrá construir una máquina para fabricar más cañas y así podrá ayudar a otras personas a pescar. Ahora debemos conceptualizar el diseño de un lenguaje de programación co- mo un patrón para diseñar lenguajes de programación, como una herramienta para hacer más herramientas del mismo tipo. El diseño de un lenguaje de programación debe ser un patrón, un patrón de crecimiento, un patrón para desarrollar el patrón para la definición de patrones que los programadores puedan usar en su trabajo y para alcanzar su objetivo.” [1] 1.1.1. Scala. Un lenguaje escalable Uno de los principales objetivos del diseño de Scala es la construcción de un lenguaje que permita el crecimiento y la escalabilidad en función de las exigencias del desarrollador. Scala puede ser utilizado como lenguaje de scripting, así como también se puede adoptar en el proceso de construcción de aplicaciones empresariales. La conjunción de su abstracción de componen- tes, su sintaxis reducida, el soporte para la programación orientada a objetos y la programación funcional han contribuido a que el lenguaje sea más escalable y haga de la escalabilidad una de sus principales características. 1.1.2. Paradigmas de la programación Dentro de la programación se pueden distinguir tres paradigmas: Programación imperativa. Programación lógica. Programación funcional. La programación imperativa pura está limitada por “el cuello de botella de Von Neuman”, término que fue acuñado por John Backus en su conferencia de la concesión del Premio Turing por la Asociación para la Maquinaria Computacional (ACM) en 1977. Según Backus: “Seguramente debe haber una manera menos primitiva de realizar grandes cam- bios en la memoria, que empujando tantas palabras hacia un lado y otro del cuello de botella de Von Neumann. No sólo es un cuello de botella para el tráfico de datos, sino que, más importante, es un cuello de botella intelectual que nos ha mantenido atados al pensamiento de “una palabra a la vez” en lugar de fomentarnos el pensar en unidades conceptuales mayores. Entonces la programación es básicamente la planificación del enorme tráfico de palabras que cruzan el cuello de botella de Von Neumann, y gran parte de ese tráfico no concierne a los propios datos, sino a dónde encontrar éstos” En la actualidad, la programación funcional y la programación orientada a objetos (POO)2 se preocupan mucho menos de “empujar un gran número de palabras hacia un lado y otro” que otros lenguajes anteriores (como por ejemplo Fortran), pero internamente, esto sigue siendo 2 El paradigma de la POO se considera ortogonal a los paradigmas de programación funcional, programación imperativa y programación lógica. Página 2
  • 19. lo que hacen durante gran parte del tiempo los computadores, incluso los supercomputadores altamente paralelos3 .[26] Del cuello de botella intelectual criticado por Backus subyace la necesidad de disponer de otras técnicas para definir abstracciones de alto nivel como son los conjuntos, los polinomios, las formas geométricas, las cadenas de caracteres, los documentos,...para lo que idealmente se deberían de desarrollar teorías de conjuntos, formas, cadenas de caracteres, etc. 1.1.2.1. Scala. Un lenguaje multiparadigma Scala ha sido el primero en incorporar y unificar la programación funcional y la programa- ción orientada a objetos en un lenguaje estáticamente tipado, donde la clase de cada instancia se conoce en tiempo de compilación, así como la disponibilidad de cualquier método de una instancia dada. La pregunta es: ¿por qué se necesita más de un estilo de programación?. El objetivo principal de la computación multiparadigma es ofrecer un determinado conjunto de mecanismos de resolución de problemas de modo que los desarrolladores puedan seleccionar la técnica que mejor se adapte a las características del problema que se está tratando de resolver. 1.1.3. Preparación del sistema 1.1.3.1. Descargar Scala Para disponer de la última versión de Scala sólo habrá que acceder a la sección “Download” en la web oficial: http://www.scala-lang.org/download/ Para trabajar con Scala, sólo será necesario un editor de texto y un terminal. También es posible trabajar con un Entorno de Desarrollo Integrado (IDE), ya que existen plugins para Eclipse, IntelliJ IDEA o Netbeans.4 1.1.3.2. Herramientas de Scala Las herramientas de línea de comandos de Scala se encuentran dentro de la carpeta bin de la instalación. El compilador de Scala: scalac El compilador scalac transforma un programa de Scala en archivos .class que podrán ser utilizados por la JVM. El nombre del archivo no tiene que corresponder con el nombre de la clase. Sintaxis: scalac [opciones ...] [archivos fuente ...] Opciones: La opción -classpath (-cp) indica al compilador donde encontrar los archivos fuente La opción -d indica al compilador donde colocar los archivos .class La opción -target indica al compilador qué versión de máquina virtual usar 3 Un estudio de referencia de base de datos, realizado a partir de 1996, encontró que tres de cada cuatro ciclos de la unidad central de procesamiento (CPU) se dedican a la espera de memoria. 4 En los próximos capítulos se usará tanto el intérprete de Scala como el IDE Eclipse junto con el plugin ScalaIDE disponible en la web http://scala-ide.org/. Página 3
  • 20. El intérprete de código: scala La instrucción scala puede operar en tres modos: Puede ejecutar clases compiladas Puede ejecutar archivos fuente (scripts – Secuencia de instrucciones almacenadas en un fichero que se ejecutan normalmente de forma interpretada. –) Puede operar en forma interactiva, es decir como intérprete Sintaxis: scala [opciones ...] [script u objeto] [argumentos] Si no se especifica un script o un objeto, la herramienta funciona como un evaluador de expresiones interactivo. En cambio, si se especifica un script, el comando scala lo compilará y lo ejecutará. Si se especifica el nombre de una clase, scala la ejecuta. Algunos comandos importantes: :help muestra mensaje de ayuda :quit termina la ejecución del intérprete :load carga comandos de un archivo Scala como lenguaje compilado Como primer ejemplo, se puede observar la definición del programa estándar “Hola mun- do”. 1 object HolaMundo { 2 def main(args: Array[String]) { 3 println("Hola, mundo!") 4 } 5 } Algoritmo 1.1: Hola Mundo La estructura de este programa debería resultar familiar para los lectores que ya conozcan Java. Consiste de un método llamado main que toma los argumentos de la línea de comandos (un vector, array en inglés, de objetos del tipo String) como parámetro. El cuerpo de este método presenta una sola llamada al método predefinido println con el saludo como argumento. El método main no devuelve un valor, por lo tanto no es necesario que se declare un tipo retorno. Lo que es menos familiar es la declaración de objetos que contienen al método main. Esta declaración introduce lo que es comúnmente conocido como singleton objects (objeto single- ton), que es una clase con una sola instancia. Por lo tanto, dicha construcción declara tanto una clase llamada HolaMundo, como una instancia de esa clase también llamada HolaMundo. Esta instancia es creada bajo demanda, es decir, la primera vez que es utilizada. Se puede advertir que el método main no está declarado como estático. Esto es así porque los miembros estáticos (métodos o campos) no existen en Scala. En vez de definir miembros estáticos, en Scala se declararán estos miembros en un objeto singleton. Compilando el ejemplo Para compilar el ejemplo utilizaremos scalac, el compilador de Scala. El comando scalac funciona como la mayoría de los compiladores: toma un archivo fuente como argumento, al- gunas opciones y produce uno o varios archivos objeto. Los archivos objeto que produce son archivos class para la JVM. Página 4
  • 21. Si guardamos el programa anterior en un archivo llamado HolaMundo.scala, podemos com- pilarlo ejecutando el siguiente comando: $ scalac HolaMundo.scala Esto generará algunos archivos class en el directorio actual. Uno de ellos se llamará Hola- Mundo.class y contiene una clase que puede ser directamente ejecutada utilizando el comando scala, como mostramos en la siguiente sección. Ejecutando el ejemplo Una vez compilado, un programa Scala puede ser ejecutado utilizando el comando scala. Su uso es muy similar al comando java utilizado para ejecutar programas Java, y acepta las mismas opciones. El ejemplo de arriba puede ser ejecutado utilizando el siguiente comando que, como se puede comprobar, produce la salida esperada: $ scala HolaMundo Hola, mundo! Scala como lenguaje interpretado desde un script Como ya se ha introducido anteriormente, es posible ejecutar archivos fuente haciendo uso del comando scala. A continuación se muestra como crear un script básico llamado hola.scala: 1 println("Hola mundo, desde un script!") Ejecutando el ejemplo desde la línea de comandos se comprueba que el resultado obtenido es el esperado: $>scala hola.scala Hola mundo, desde un script! Desde la línea de comandos se le pueden pasar argumentos a los scripts mediante el vector de argumentos args. En Scala, el acceso a los elementos de un vector se realiza especificando el índice entre paréntesis (no entre corchetes como en Java)5 . Se ha definido el siguiente script, llamado holaarg.scala, con el objetivo de probar el funcionamiento del paso de argumentos desde la línea de comandos: 1 println("Hola, "+ args(0) +"!") Si se ejecuta: $> scala holaarg.scala pepe En este comando, “pepe” se pasa como argumento desde la línea de comandos, el cual se accede desde el script mediante args(0). La salida obtenida es la que se esperaba: Hola, pepe! Scala como lenguaje interpretado desde un intérprete Para lanzar el intérprete de Scala, se tiene que abrir una ventana de terminal y teclear scala. Aparecerá el prompt del intérprete de Scala a la espera de recibir expresiones. Funciona, al igual que el intérprete de Scheme o Haskell, mediante el Bucle Leer-Evaluar-Imprimir (REPL). El intérprete de Scala será muy utilizado durante el capítulo dedicado a la programación funcional, por lo que se recomienda familiarizarse con el mismo. 5 Notación que es homogénea para las demás estructuras, incluso las estructuras definidas por el usuario. Página 5
  • 22. 1.2. Conceptos básicos 1.2.1. Elementos de un lenguaje de programación Un lenguaje de programación debe proveer: expresiones primitivas que representen los elementos más simples maneras de combinar expresiones maneras de abstraer expresiones, lo cual introducirá un nombre para esta expresión y nos permitirá hacer referencia a la expresión. 1.2.2. Elementos básicos en Scala 1.2.2.1. Tipos de datos básicos en Scala En Scala se pueden encontrar los mismos tipos de datos básicos que en Java. El tamaño que ocupa cada uno de ellos en memoria, así como la precisión de los tipos primitivos de Scala, también se corresponden con los de Java. Aunque se hable de tipos de datos, en Scala todos los tipos de datos son clases. En la tabla 1.1 se muestran los tipos básicos en Scala. Tipo de dato Tamaño Rango Ejemplo Byte 8 bits con signo [−128, 127] 38 Short 16 bits con signo [−32768, 32767] 23 Int 32 bits con signo [−231 , 231 − 1] 45 Long 64 bits con signo [−263 , 263 − 1] 3434115 Float 32 bits con signo [−3,4028 ∗ 1038 , 3,4028 ∗ 1038 1.38 Double 64 bits con signo [−1,7977 ∗ 10308 , 1,7977 ∗ 10308 ] 54.37 Boolean true o false true Char 16 bits con signo [0, 216 − 1] ’F’ String secuencia de caracteres Cadena de caracteres "hola mundo!" Tabla 1.1: Tipos de datos primitivos y tamaño en Scala Los tipos Byte, Short, Int y Long reciben el nombre de tipos enteros. Los tipos enteros junto con los tipos Float y Double son llamados tipos numéricos. Excepto String, que es del paquete java.lang, el resto se encuentran en el paquete Scala. Todos se importan automáticamente. Literales de tipos básicos en Scala Las reglas sobre literales que usa Scala son bastante simples e intuitivas. A continuación se verán las principales características de los literales básicos en Scala. Literales enteros Los mayoría de literales enteros que utilicemos serán de tipo Int. Los literales enteros también podrán ser de tipo Long añadiendo el sufijo L o l al final del literal. A continuación se muestran algunos ejemplos: Página 6
  • 23. scala> 5 res0: Int = 5 scala> 777L res3: Long = 777 scala> 0xFFAFAFA5 res2: Int = -5263451 scala> 0777L <console>:1: error: Non-zero integral values may not have a leading zero. 0777L ^ Los literales enteros no podrán tener el cero como primer dígito6 , excepto el entero 0 y aquellos representados en notación hexadecimal. Los enteros representados en notación hexa- decimal comienzan por 0x o 0X, pudiendo estar seguido por dígitos entre 0 y 9 o letras entre A y F (en mayúscula o minúscula). Literales en punto flotante Los literales en punto flotante serán del tipo de datos Float cuando se añada el sufijo F o f. En otro caso serán de tipo Double. Estos literales sí podrán contener uno o varios ceros como primeros dígitos. scala> 0.0 res0: Double = 0.0 scala> 01.2 res1: Double = 1.2 scala> 01.2F res2: Float = 1.2 scala> 00045.34 res3: Double = 45.34 Literales lógicos Los literales lógicos o booleanos son aquellos que pertenecen a la clase Boolean y que sólo pueden tener uno de los dos valores booleanos: true o false. Literales de tipo símbolo Aunque el tipo de datos Symbol (scala.Symbol) no se considera un tipo básico de Scala, merece la pena tenerlo en cuenta. Los símbolos serán cadenas de caracteres no vacías precedidas del prefijo ’. La clase case Symbol está definida de la siguiente forma: 1 package scala 2 final case class Symbol private (name: String) { 3 override def toString: String = "’" + name 4 } Ejemplo: 6 En versiones anteriores de Scala, cuando un entero comenzaba por cero era porque el número estaba en base 8 y, por tanto, sólo podía estar seguido de los dígitos comprendidos entre 0 y 7. Página 7
  • 24. scala> ’miSimbolo res4: Symbol = ’miSimbolo Literales de tipo carácter Un literal de tipo carácter consiste en un carácter encerrado entre comillas simples que representará un carácter representable o un carácter de escape7 . El tipo de datos de los literales de tipo carácter es Char. El estándar de codificación de caracteres utilizado para representar los caracteres es Unicode. También podremos indicar el código unicode del carácter que queramos representar encerrado entre comillas simples. Veamos algunos ejemplos: scala> ’u0050’ res5: Char = P scala> ’S’ res6: Char = S scala> ’n’ res7: Char = Literales de tipo cadena de caracteres Un literal del tipo cadena de caracteres será una secuencia de caracteres encerradas entre comillas dobles cuyo tipo de datos será String. Los caracteres que conformen la cadena de caracteres podrán ser tanto caracteres representables, como caracteres de escape. Por ejemplo: scala> "Hola Mundo!" res8: String = Hola Mundo! scala> "Hola " Mundo!" res9: String = Hola " Mundo! Los literales de tipo cadena de caracteres también pueden ser multilínea en cuyo ca- so estarán encerrados entre tres comillas dobles. En este caso, la cadena de caracteres podrá estar formada por los caracteres representables, caracteres de escape o por caracteres no repre- sentables como salto de línea o cualquier otro carácter especial como comillas dobles, barra inversa,...con la única salvedad de que sólo podrá haber tres o más comillas dobles al final. Veamos algún ejemplo: scala> """y dijo: | "mi nombre es Bond, James Bond" | ///""" res10: String = y dijo: "mi nombre es Bond, James Bond" /// Caracteres de escape Los caracteres de escape que se muestran en la tabla 1.2 son reconocidos tanto en los literales de caracteres como en los literales de cadenas de caracteres. 7 También conocidos como secuencia de escape Página 8
  • 25. Carácter de escape Unicode Descripción b u0008 Retroceso BS t u0009 Tabulador horizontal HT n u000A Salto de línea LF f u000C Salto de página FF r u000D Retorno de carro CR " u0022 Comillas dobles ’ u0027 Comilla simple u005c Barra inversa Tabla 1.2: Caracteres de escape reconocidos por Char y String En versiones anteriores de Scala era posible definir cualquier carácter que tuviera un código unicode entre 0 y 255, indicando el código en base octal. Esto se indicaba precediendo al código octal de . Por ejemplo 150 representaría el carácter h. Esta representación está depreciada en la actualidad, siendo sustituida por la representación en base hexadecimal de los caracteres. scala> "150" <console>:1: warning: Octal escape literals are deprecated, use u0068 instead. "150" ^ res11: String = h scala> "u0068" res12: String = h Expresiones de tipos básicos en Scala Las expresiones en Scala pueden estar formadas por: Un literal válido de cada uno de los tipo de datos básicos vistos en la tabla 1.1. Por ejemplo: 1, 2.5, 3.74E10f, true, false, ’a’... Un operador 8 junto con dos operandos (operadores binarios) o un operando (operadores unarios) del tipo de datos compatible con el operador. El último valor evaluado en un bloque (en la llamada a una función o método). Expresiones aritméticas en Scala Las expresiones aritméticas son aquellas que, tras ser evaluadas, devuelven un valor nu- mérico. Los tipos de datos Byte, Short, Int y Long serán utilizados para representar valores numéricos de tipo entero. Con los tipos de datos Float y Double se representarán valores de tipo real. El tipo de datos devuelto por defecto al evaluar una expresión que represente un número entero es Int mientras que Double será el tipo de datos por defecto devuelto al evaluar una expresión que represente un número real. scala> 1.2 res0: Double = 1.2 scala> 5 res1: Int = 5 8 Los operadores se verán en la Subsubsección 1.2.2.2: Operadores « página 10 » Página 9
  • 26. Expresiones booleanas Una expresión booleana puede estar compuesta por un literal, por uno de los operadores lógicos mostrados en la tabla 1.6 o ser el valor devuelto por las operaciones usuales de com- paración que se muestran en la tabla 1.5 de operadores relacionales en Scala. Una expresión booleana también puede ser el resultado de la evaluación de cualquier expresión booleana co- mo, por ejemplo, el resultado de una función o método. 1.2.2.2. Operadores Los operadores se utilizarán para combinar expresiones de los tipos de datos básicos. En Scala los operadores son en realidad métodos y, por tanto, se reducen a la llamada a un método de un objeto de una de clases de los tipos de datos básicos. Es decir, 1 + 2 realmente invoca al método + del objeto 1 con el parámetro 2: (1).+(2). Es más, en la clase Int hay diferentes definiciones del método + (método sobrecargado) que difieren las unas de las otras en el tipo del parámetro con el que se invocan y que nos permiten realizar la suma de objetos de diferentes tipos enteros. Por ejemplo, existe otro método + en la clase Int que recibe como parámetro un objeto de tipo Long y devuelve otro objeto de tipo Long. Los operadores se pueden clasificar, atendiendo a su notación en: Operadores infijos. Son operadores binarios en los que el método que se va a invocar se ubica entre sus dos operandos. Un ejemplo de operador infijo podría ser el operador aritmético + de los tipos numéricos. Operadores prefijos. Son operadores unarios en los que el nombre del método se sitúa delante del objeto que invocará al método. Como por ejemplo: !b, -5,... Operadores postfijos9 . Son aquellos operadores unarios en los que el nombre del método se sitúa detrás del objeto que invocará al método. Por ejemplo: 5 abs, 2345 toLong. Infijos, Prefijos y Postfijos En Scala los operadores no tienen una sintaxis especial10 , como ocurre en otros lenguajes de programación como Haskell, por lo que cualquier método puede ser un operador. Lo que convertirá un método en un operador será la forma de usar el mismo. Por ejemplo, si se escribe 1.+(2), + no será un operador. Pero si se escribe 1 + 2, + sí será un operador. Existe la posibilidad de definir nuevos operadores prefijos definiendo métodos que comien- cen por unary_ y estén seguidos por un identificador válido. Por ejemplo, si en un tipo de datos se define un método unary_!, una instancia de este tipo de datos podrá invocar ! en notación prefija. Scala transforma las llamadas a operadores prefijos en llamadas al método unary_. Por ejemplo, la expresión !p es transformada por Scala en la invocación de p.unary_!. Los operadores postfijos son métodos que no reciben parámetros y, por convenio, no es necesario el uso de paréntesis11 9 Los operadores postfijos tienen que ser habilitados -visibles- importando scala.language.postfixOps o indican- do al compilador la opción -language:postfixOps 10 Excepto los operadores prefijos en los que los identificadores que pueden ser usados en este tipo de operadores son +, -, ! y ~. 11 Excepto si el método presenta efectos colaterales, como println(), en cuyo caso sí habrá que poner los parén- tesis. Página 10
  • 27. Los operadores infijos son aquellos métodos que reciben un argumento. Los operadores infijos se ubican entre sus dos operandos (como el operador +, en el que el primer operando será el objeto que invoca al método y el segundo operando es el argumento que recibe). Prioridad y Asociatividad de los operadores Cuando en una expresión aparecen varios operadores, la prioridad de los operadores nos indicará el orden de evaluación de las diferentes partes que componen la expresión, es decir, qué partes de la expresión son evaluadas antes. Por ejemplo, el resultado de evaluar la expresión 100 - 40 * 2 es 20, no 120, ya que el operador * tiene mayor prioridad que el operador +. El resultado de la anterior expresión es el mismo que si se evalúa la expresión 100 - (40 * 2). Si se quisiera cambiar el orden de evaluación anterior se debería escribir la expresión: (100 - 40) * 2, cuyo resultado sería 120. Scala determina la prioridad de los operadores basándose en el primer carácter de los méto- dos usados con notación de operador. La excepción a esta regla es el operador de menor priori- dad en Scala: el operador de asignación (los operadores que terminan con el carácter ’=’). Así, con la excepción ya comentada de los operadores de asignación, la precedencia de operadores se determinará según el primer carácter, tal como se muestra en la tabla 1.3, donde encontramos la prioridad de los operadores básicos, de forma que los operadores de mayor prioridad se en- cuentran en la parte superior de la tabla y los operadores de menor prioridad en la parte inferior. La prioridad de cualquier operador definido por el usuario se definirá por el primer carácter empleando según esta misma tabla. Tipo Operador Asociatividad Postfijos (), [],... Izquierda Unarios ! ˆ Derecha Multiplicativos * / % Izquierda Aditivos + - Izquierda Binario : Izquierda Binario = ! Izquierda Desplazamiento >> >>> << Izquierda Relación <><= >= Izquierda Igualdad == != Izquierda Bit a bit AND & Izquierda Bit a bit XOR ˆ Izquierda Bit a bit OR | Izquierda AND lógico && Izquierda OR lógico || Izquierda Todas las letras Izquierda Asignación = += -= *= /= %= >>= <<= &= ˆ= |= Derecha Coma , Izquierda Tabla 1.3: Prioridad y asociatividad de los operadores Cuando se encuentran en una expresión varios operadores con la misma prioridad, será otra de las características de los operadores, la asociatividad, la encargada de indicar cómo se agrupan los operadores y, por tanto, como se evaluará la expresión. La asociatividad de un operador en Scala se determina por el último carácter del operador. Si el último carácter de Página 11
  • 28. un operador binario es :, el operador tendrá asociatividad derecha, lo que quiere decir que los métodos que terminen en : serán invocados por el operando situado en el lado derecho del operador y se les pasará como parámetro el operando izquierdo. Es decir, si se tiene la expresión x /: y, será equivalente a y./:(x). En cualquier otro caso, el operador presentará asociatividad izquierda. La asociatividad de un operador no influye en el orden de evaluación de los operandos, que siempre será de izquierda a derecha. En la anterior expresión, x/:y, primero se evaluará el operando x y después el operando y. Es decir, la expresión es tratada como el siguiente bloque: 1 {val t=x;y./:(x)} Como se ha dicho anteriormente, la asociatividad determinará como se agrupan los opera- dores en caso de que aparezcan en una expresión varios operadores con la misma prioridad. Si los métodos terminan en ’:’, se agruparán de derecha a izquierda, mientras que en cualquier otro caso se agruparán de izquierda a derecha. Si se tienen las expresiones a * b * c y x /: y /: z, se evaluarán como (a * b) * c y x /: (y /: z), respectivamente. A pesar de conocer las reglas que determinan la prioridad de los operadores y la asociatividad de los mismos, es aconsejable usar paréntesis para aclarar cuales son los operadores que actúan sobre cada una de las expresiones. Operadores aritméticos Los operadores aritméticos son aquellos que operan con expresiones enteras o reales, es decir, con objetos de tipo Short, Int, Double o Long. En Scala se pueden distinguir dos tipos de operadores aritméticos: unarios y binarios. En la tabla 1.4 se muestran los diferentes operadores aritméticos presentes en Scala. Descripción Operador Tipo Signo positivo + unario Signo negativo - unario Suma + binario Resta - binario División / binario Producto * binario Resto % binario Tabla 1.4: Operadores aritméticos en Scala Todos los operadores admiten expresiones enteras y reales. En el caso de los operadores binarios, si los dos operandos son enteros o reales el resultado de la operación será un valor entero o real respectivamente. Si uno de sus operandos es entero y el otro real entonces el resultado devuelto será de tipo real. Los operadores aritméticos también servirán para combinar expresiones de tipo carácter. En este caso, Scala tomará el valor decimal del código unicode que represente el carácter de cada uno de los operadores. scala> 1.5 * 3 res2: Double = 4.5 scala> 5 * 3 res3: Int = 15 Página 12
  • 29. Operadores relacionales El uso de operadores relacionales permite comparar expresiones de tipos de datos com- patibles, devolviendo un resultado de tipo booleano: true o false. Scala soporta los operadores relacionales que se muestran en la tabla 1.5. Los operadores relacionales son binarios. Descripción Operador Menor < Menor/Igual <= Mayor > Mayor/Igual >= Distinto != Igual == Tabla 1.5: Operadores relacionales en Scala Como se puede apreciar en el siguiente ejemplo, los resultados obtenidos después de com- parar diferentes expresiones numéricas (reales o enteras) son los esperados: scala> 12.0 * 3 == 3 * 12 res4: Boolean = true scala> 3.0f < 7 res5: Boolean = true scala> "cadena" == "cadena" res6: Boolean = true En cambio, si lo que pretende es comparar dos expresiones booleanas se deberá tener en cuenta que el valor false se considera menor que el valor true. scala> 13<10 < (11==11.0) res7: Boolean = true Cuando se comparen expresiones del tipo de datos Char o String se deberá tener en cuenta que se basan en el valor decimal del código unicode de cada carácter12 . En el caso de expresiones del tipo de datos String, cuando los caracteres situados en la primera posición de las cadenas que se estén comparando sean iguales (mismo código unicode) se comparará el segundo carácter de ambas cadenas y así sucesivamente hasta que se encuentre el primer par de caracteres distintos, ubicados en la misma posición en ambas cadenas, o hasta que la cadena cuya longitud sea menor se termine, en cuyo caso la cadena de menor longitud será menor que la cadena de mayor longitud. A continuación se muestran unos ejemplos en el intérprete de Scala que pueden ayudar a aclarar estos conceptos: scala> "hola mundo" < "hola mundo scala!" res8: Boolean = true scala> ’a’<’b’ res9: Boolean = true scala> "hola"<"hola" res10: Boolean = false scala> "hola"<="hola" res11: Boolean = true scala> "hola" <= "Hola" res12: Boolean = false 12 En Scala, una expresión de tipo String y otra de tipo Char no se pueden comparar. Página 13
  • 30. En otros lenguajes de programación como Java, el operador relacional == se utiliza pa- ra comparar tipos primitivos (comparando si los valores son iguales, como en Scala) y tipos referenciados (comparando igualdad referencial). En Scala se puede comparar la igualdad refe- rencial de tipos referenciados usando eq y neq. Operadores lógicos Los operadores lógicos permitirán combinar expresiones lógicas. Las expresiones lógicas son todas aquellas expresiones que tras ser evaluadas se obtiene: verdadero o falso. Scala soporta los operadores lógicos que se muestran en la tabla 1.6. Descripción Operador Tipo AND lógico && binario OR lógico || binario NOT lógico ! unario Tabla 1.6: Operadores lógicos en Scala Operadores bit a bit. Los operadores bit a bit trabajan sobre cadenas de bits aplicando el operador en cada uno de los bits de los operadores. Las tablas de verdad para los operadores &, | y ˆ son las que se muestran en la tabla 1.7 p q p& q p ˆ q p | q 0 0 0 0 0 0 1 0 1 1 1 0 1 1 0 1 1 0 1 1 Tabla 1.7: Tabla de verdad de los operadores bit a bit &, | y ˆ . Los operadores bit a bit soportados por Scala se muestran en la tabla 1.8. Operador Descripción Ejemplo & AND binario a & b | OR binario a | b ˆ XOR binario 45 ˜ Complemento a uno (intercambio de bits) ˜a << Desplazamiento binario a la izquierda a <<2 >> Desplazamiento binario a la derecho a >>2 >>> Desplazamiento a la derecha con relleno de ceros a >>>2 Tabla 1.8: Operadores bit a bit. Página 14
  • 31. Operadores de igualdad. Scala soporta los operadores de asignación mostrados en la tabla 1.9. Operador Descripción Ejemplo = Operador de asignación simple C = A + B += Suma y asignación A += B equivalente a A = A + B -= Resta y asignación A -= B equivalente a A = A - B *= Multiplicación y asignación A *= B equivalente a A = A * B /= División y asignación A /= B equivalente a A = A / B %= Módulo y asignación A %=B equivalente a A = A % B <<= Desplazamiento a la izquierda y asignación A <<= 2 equivalente a A = A <<2 &= Bit a bit AND y asignación A &= 2 equivalente a A = A & 2 ˆ= Bit a bit XOR y asignación A ˆ= 2 equivalente a A = A ˆ 2 |= Bit a bit OR y asignación A |= 2 es equivalente a A = A | 2 Tabla 1.9: Operadores de asignación. 1.2.2.3. Nombrar expresiones Es posible nombrar una expresión con la palabra reservada def y utilizar su nombre (identi- ficador) en lugar de la expresión: scala> def scale = 5 scale: Int scala> 7 * scale res4: Int = 35 scala> def pi = 3.141592653589793 pi: Double scala> def radius = 10 radius: Int scala> 2 * pi * radius res5: Double = 62.83185307179586 def es una primitiva declarativa: le da un nombre a una expresión, pero no la evalúa. scala> def r=8/0 r: Int scala> r java.lang.ArithmeticException: / by zero at .r(<console>:7) ... 33 elided Se puede observar que la definición de r no da error. El error se produce en el momento que se evalúa por primera vez r. Es lo contrario que la forma especial define de Scheme, que se utiliza para asociar nombres con expresiones, y en primer lugar se evalúa la expresión. En realidad, nombrar una expresión sólo aportará azúcar sintáctico, permitiendo crear funciones y evitando escribir la expresión lambda asociada. Es decir, es lo mismo que crear una función en Scheme sin argumentos. Alternativamente, se verá como Scala permite una definición de variables utilizando val o var. 1.2.2.4. Variables Las variables están encapsuladas en objetos, es decir, no pueden existir por si mismas. Las variables son referencias a instancias de clases. Página 15
  • 32. Para definir una variable se requiere: Definir su mutabilidad Definir el identificador Definir el tipo (opcional) Definir un valor inicial Sintaxis para definir una nueva variable: <var | val> < identificador> : < Tipo> = < _ | Valor Inicial> La nueva variable podrá ser un literal o el resultado de la evaluación de una expresión, una función o el valor devuelto por un método. Pero también se podrán definir funciones, métodos, bloques, etc. Por ejemplo: 1 val x : Int = 1 Se usa la palabra reservada val para indicar que la referencia no puede reasignarse, mientras que se utiliza var para indicar que sí puede reasignarse. La variable val es similar a una variable final en Java: una vez inicializada, no se podrá reasignar. Una variable var, por el contrario, se puede reasignar múltiples veces. Todas las variables o referencias en Scala tienen un tipo, debido a que el lenguaje es estric- tamente tipado. Sin embargo, los tipos pueden en muchos casos omitirse, porque el compilador de Scala tiene inferencia de tipos. Por ejemplo, las siguientes definiciones son equivalentes: scala> var x : Int = 1 scala> var x = 1 Otros ejemplos: val msg = "Hola mundo!" msg: java.lang-String = Hola mundo! En este ejemplo se aprecia que Scala tiene inferencia de tipos, es decir, al haber inicializado la variable msg con una cadena, Scala asocia el tipo de msg al tipo de datos String. Si se intenta modificar el valor de msg, no se podrá y obteniéndose un error ya que se ha definido como val: scala> msg = "Hasta luego!" <console>:5: error: reassignment to val msg = "Hasta luego!" Si se imprime por pantalla el valor de msg: scala> println(msg) Hola mundo! Si se quisiera reasignar el valor de una variable habría que utilizar la palabra reservada var: scala> var saludo = "Hola mundo!" saludo: java.lang.String = Hola mundo! scala> saludo = "Hasta luego!" saludo: java.lang.String = Hasta luego! Aunque todas las declaraciones de variables podrían realizarse utilizando var, se recomienda encarecidamente usar val cuando la variable no vaya a mutar. Página 16
  • 33. 1.2.3. Uso del carácter punto y coma (;) en Scala En Scala, el uso del punto y coma al final de una línea de programa es opcional en la mayoría de las ocasiones, siendo recomendado omitir el mismo siempre y cuando su uso no sea obligatorio. Se podría escribir: 1 def pi = 3.14159; aunque muchos programadores omitirán el uso del (;) y simplemente escribirán: 1 def pi = 3.14159 ¿Cuándo es obligatorio el uso del punto y coma? El uso del punto y coma es obligatorio para separar instrucciones escritas en la misma línea. 1 def y = x - 1; y + y Uso del punto y coma y operadores infijos en Scala. Uno de los problemas derivados de omitir el uso del punto y coma en Scala es cómo escribir expresiones que ocupen varias líneas. Si se escribiera: Expresión larga + otra expresión larga sería interpretado por Scala como dos expresiones. Si lo que se quiere es que se interprete como una única expresión, se podría hacer de dos formas: Se podría encerrar una expresión de varias líneas entre paréntesis, dando por hecho que no se usará el punto y coma en éstas líneas: (Expresión larga + otra expresión larga) Se podría escribir el operador al final de la línea, indicándole así a Scala que la expresión no está finalizada: Expresión larga + otra expresión larga Por tanto, por norma general, los saltos de línea serán tratados como puntos y coma, salvo que algunas de las siguientes condiciones sea cierta: La línea en cuestión finaliza con una palabra que no puede actuar como final de sentencia, como por ejemplo un espacio (“ ”) o los operadores infijos. La siguiente línea comienza con una palabra que no puede actuar como inicio de sentencia La línea termina dentro de paréntesis (...) o corchetes [...], puesto que éstos últimos no pueden contener múltiples sentencias Página 17
  • 34. 1.3. Bloques en Scala Un bloque en Scala estará encerrado entre llaves { ...}. Dentro de un bloque se podrán encontrar definiciones o expresiones. El último elemento de un bloque será una expresión que definirá el valor del bloque, la cual podrá estar precedida por definiciones auxiliares. Los bloques también son expresiones en sí mismos, por lo que un bloque podrá aparecer en cualquier lugar en el que pueda aparecer una expresión. Ejemplo: 1 { val x = f(3) 2 x * x 3 } 1.3.1. Visibilidad y bloques en Scala Scala sigue las reglas de ámbito habituales en lenguajes como C o Java. Las definiciones realizadas dentro de un bloque sólo serán visibles dentro del mismo y ocultan las definiciones realizadas fuera del bloque que tengan el mismo nombre. Las definiciones realizadas en bloques externos estarán visibles dentro de un bloque siempre y cuando no sean ocultadas por otras definiciones con el mismo nombre en el bloque. Ejemplo: 1 val x = 10 2 def f(y: Int)=y+1 3 val result = { 4 val x = f(3) 5 x * x 6 } + x El resultado de la ejecución de este código será 26 1.4. Evaluación en Scala 1.4.1. Evaluación de expresiones Para evaluar una expresión no primitiva: 1. Se toma el operador con mayor prioridad (ver la Sección 1.2.2.2: Prioridad y Asociati- vidad de los operadores « página 11 »), o en caso de que todos los operadores tenga la misma prioridad se toma el operador situado más a la izquierda, 2. Se evalúa sus operandos (de izquierda a derecha), 3. Se aplica el operador a los operandos. Un nombre se evalúa reemplazando el mismo por su definición. El proceso de evaluación fina- liza cuando se obtiene un valor. Ejemplo: (2 ∗ pi) ∗ radius (2 ∗ 3,14159) ∗ radius 6,28318 ∗ radius Página 18
  • 35. 6,28318 ∗ 10 62,8318 1.4.2. Evaluación de funciones La evaluación de funciones parametrizadas es similar a la de operadores: 1. Se evalúan los parámetros de la función de izquierda a derecha, 2. Se reemplaza la llamada a la función por la definición de la misma y, al mismo tiempo, 3. Se reemplazan los parámetros formales de la función por el valor de sus argumentos. Este sistema de evaluación de expresiones es llamado modelo de sustitución, cuya idea gira en torno a que lo que hace una evaluación es reducir una expresión a su valor y puede ser aplicado a todas las expresiones (siempre y cuando no tengan efectos laterales). El modelo de sustitución está formalizado en el λ-cálculo. Esta estrategia de evaluación es conocida como evaluación estricta o Call by Value. Existe otra alternativa: no evaluar los argumentos antes de reemplazar la función por la definición de la misma, llamada evaluación no estricta o Call by Name. 1.4.3. Sistema de Evaluación de Scala Scala usa evaluación estricta normalmente, pero se puede indicar explícitamente que se desea que algún argumento de una función use evaluación no estricta. Para esto último, se pondrá => delante del tipo del parámetro que se desee evaluar siguiendo esta estrategia. Ejemplo: 1 def miConst (x : Int, y: =>Int) = x Algoritmo 1.2: Función con dos parámetros. El primero es evaluado por valor y el segundo por nombre 1.4.3.1. Valores de las definiciones Anteriormente se ha dicho que los parámetros de las funciones pueden ser pasados por valor (evaluación estricta) o por nombre (evaluación no estricta). La misma distinción se puede aplicar a las definiciones. La forma def es por nombre (eva- luación no estricta) y la forma val es por valor (evaluación estricta). 1.4.3.2. Evaluación de Booleanos En la tabla 1.10 aparecen representadas las reglas de reducción para expresiones booleanas. Página 19
  • 36. !true → false !false → true true && e → e false && e → false true || e → true false || e → e Tabla 1.10: Reglas de reducción para expresiones booleanas En la tabla 1.10 se puede apreciar que && y || no siempre necesitan que el operando derecho sea evaluado. En estos casos, se dirá que estas expresiones usan una evaluación en cortocir- cuito. 1.4.4. Ámbito y visibilidad de las variables El funcionamiento de los ámbitos en Scala es el siguiente: Una invocación a una función crea un nuevo ámbito en el que se evalúa el cuerpo de la función. Es el ámbito de evaluación de la función. El ámbito de evaluación se crea dentro del ámbito en el que se definió la función a la que se invoca. Los argumentos de la función son variables no mutables locales de este nuevo ámbito que quedan ligadas a los parámetros que se utilizan en la llamada. En el nuevo ámbito se pueden definir variables locales. En el nuevo ámbito se pueden obtener el valor de variables del ámbito padre. Primer ejemplo: Supongamos el siguiente código en Scala: 1 def f(x: Int, y: Int): Int = { 2 val z = 5 3 x+y+z 4 } 5 def g(z: Int): Int = { 6 val x = 10 7 z+x 8 } 9 f(g(3),g(5)) Se definen dos funciones f y g, dentro de cada una de las cuales hay declaradas distintas variables locales. Las funciones f y g devuelven una suma de los parámetros con la variable local. En la última línea de código se realiza una invocación a f con los resultados devueltos por dos invocaciones a g . ¿Cuántos ámbitos locales se crean? ¿En qué orden? 1. En primer lugar se realizan las invocaciones a g . Cada una crea un ámbito local en el que se evalúa la función. Las invocaciones devuelven 13 y 15, respectivamente. Página 20
  • 37. 2. Después se realiza la invocación a f con esos valores 13 y 15. Esta invocación vuelve a crear un ámbito local en el que se evalúa la expresión x+y+z , devolviendo 33. Por ahora, todo es bastante normal. La diversión empezará cuando se construyan funciones anónimas durante la ejecución de otras funciones, lo cual permitirá la definición de cierres. 1.5. Estructuras de control en Scala Las estructuras de control permiten controlar el flujo de ejecución de las sentencias de un programa, método, bloque, etc. Siguiendo el flujo natural, las sentencias que componen un pro- grama se ejecutan secuencialmente una tras de otra según estén definidas13 (primero se ejecutará la primera sentencia, después la segunda ..., y así hasta la última sentencia). Las sentencias de control de flujo se emplean en los programas para ejecutar sentencias condicionalmente, repetir un conjunto de sentencias o, en general, cambiar el flujo secuencial de ejecución. Las estructuras de control se dividen en tres grandes categorías en función del flujo de ejecución: Estructuras secuenciales Estructuras condicionales Estructuras iterativas Hasta el momento se ha visto el flujo secuencial. Cada una de la sentencias que se utilizan en Scala están separadas por el carácter punto y coma (véase el uso del carácter punto y coma en Scala en la Subsección 1.2.3: Uso del carácter punto y coma (;) en Scala « página 17 »). El uso de estructuras secuenciales puede ser suficiente para la resolución de programas sencillos, pero para la resolución de programas de tipo general se necesitará controlar las sentencias que se ejecutan haciendo uso de estructuras condicionales e iterativas. 1.5.1. Estructuras condicionales Se utilizarán las estructuras condicionales para determinar si un bloque debe de ser ejecu- tado o no, en función de una condición lógica o booleana. 1.5.1.1. La sentencia if Una sentencia if consiste en una expresión booleana seguida de un bloque de expresiones. Si la expresión booleana se evalúa a cierta, entonces se ejecutará el bloque de expresiones. En caso contrario no se ejecutará el bloque de expresiones. La sintaxis de una sentencia if es: 1 if (expresion_booleana) { 2 // Sentencias que se ejecutaran si la 3 // expresion booleana se evalua a true 4 } Algoritmo 1.3: Sintaxis sentencia if Ejemplo: 13 Cuando se escribe un programa, se introduce la secuencia de sentencias dentro de un archivo. Sin sentencias de control del flujo, el intérprete ejecuta las sentencias conforme aparecen en el programa de principio a fin. Página 21
  • 38. 1 def mayorQueCinco(x: Int) = if (x > 5) { println(" El argumento ",x " es mayor que 5");} Algoritmo 1.4: Expresión condicional if La sentencia if / else La sentencia if puede ir seguida de la una declaración else y un bloque de expresiones. El bloque de expresiones que sigue a la declaración else se ejecutará en el caso de que la expresión booleana del if sea falsa. Scala tiene la expresión condicional if-else para expresar la elección entre dos alternativas (parecida a if-else de Java pero usada para expresiones, no para instrucciones). La sintaxis de una sentencia if / else es: 1 if (expresion_booleana) { 2 // Sentencias que se ejecutaran si la 3 // expresion booleana se evalua a true 4 } else { 5 // Sentencias que se ejecutaran si la 6 // expresion booleana se evalua a false 7 } Algoritmo 1.5: Sintaxis sentencia if / else Ejemplo: 1 def abs(x: Int) = if (x > 0) x else -x Algoritmo 1.6: Expresiones condicionales. Función valor absoluto La sentencia if...else if ...else La expresión if puede ir seguida por una declaración else if, lo cual nos será de gran ayuda para testear varias opciones haciendo uso de una única sentencia if. Cuando se haga uso de la sentencia if...else if...else habrá que tener en cuenta algunos puntos importantes: Una sentencia if podrá tener cero o una declaración else, la cual estará declarada siempre al final de una sentencia if. Una sentencia if podrá tener cero o varias declaraciones else if, y siempre deberán prece- der a la declaración else (si hubiera). Si la evaluación de la expresión booleana de la sentencia if es true, ninguna de las otras expresiones booleanas de las declaraciones else if serán evaluadas, y tampoco se ejecutará el bloque de la declaración else. En el algoritmo Algoritmo 1.7: Sintaxis sentencia if / else if / else « página 23 » se muestra la sintaxis de una sentencia if / else if / else. Página 22
  • 39. 1 if (expresion_booleana1) { 2 // Sentencias que se ejecutaran si la 3 // expresion booleana 1 se evalua a true 4 } else if (expresion_boolena2){ 5 // Sentencias que se ejecutaran si la 6 // expresion booleana 2 se evalua a true 7 } else if (expresion_boolena3){ 8 // Sentencias que se ejecutaran si la 9 // expresion booleana 3 se evalua a true 10 } 11 else { 12 // Sentencias que se ejecutaran si ninguna 13 // de las anteriores expresiones booleanas 14 // se evaluan a true 15 } Algoritmo 1.7: Sintaxis sentencia if / else if / else Estructuras condicionales anidadas Scala permite que las sentencias if, if/else, if/else if/else se puedan anidar. Ejemplo: 1 var x = 30; 2 var y = 10; 3 4 if( x == 30 ){ 5 if( y == 10 ){ 6 println("X = 30 and Y = 10"); 7 } 8 } Algoritmo 1.8: Sentencias if anidadas 1.5.2. Estructuras iterativas Hasta el momento se ha visto como el uso de la sentencia condicional permite dejar de ejecutar algunas sentencias dispuestas en un programa, en función del resultado de la evaluación de una expresión booleana. Pero el flujo del programa, en cualquier caso, siempre avanza hacia adelante y nunca se vuelve a ejecutar una sentencia ejecutada anteriormente. Las sentencias iterativas permitirán iterar un bloque de sentencias, es decir, ejecutar un bloque de sentencias mientras la condición especificada sea cierta. A este tipo de sentencias se les denomina bucles y al bloque de sentencias se les denominará cuerpo del bucle. En la tabla 1.11 se puede observar los diferentes tipos de bucles que presenta Scala para dar respuesta a las necesidades de iteración de los programadores. Página 23
  • 40. Bucle Descripción while Repite una sentencia o un bloque de sentencias siempre que la condición sea verdadera. Se evalúa la condición antes de ejecutar el cuerpo del bucle do...while Igual que el bucle while pero la evaluación de la condición se produce después de la ejecución del bucle. for El cuerpo del bucle se ejecuta un número determinado de veces. Presenta un sintaxis que abrevia el código que maneja la variable del bucle. Tabla 1.11: Tipos de bucles en Scala 1.5.2.1. Bucles while Un bucle while repetirá sucesivamente un bloque de sentencias mientras la condición da- da sea cierta. Un bucle while tiene una condición de control o expresión lógica que ha de ir encerrada entre paréntesis y es la encargada de controlar la secuencia de repetición. El punto clave de los bucles while es el hecho de que la evaluación de la condición se realiza antes de que se ejecute el cuerpo del bucle, por lo que si la condición se evalúa a falsa, el cuerpo del bucle no se ejecutaría ninguna vez. La sintaxis de un bucle while es: 1 while (condicion) { 2 // Sentencias que se ejecutaran mientras 3 // la condicion sea cierta 4 } Algoritmo 1.9: Sintaxis bucles while Hay que hacer notar que si la condición es cierta inicialmente, la sentencia while no termi- nará nunca (bucle infinito) a menos que en el cuerpo de la misma se modifique de alguna forma la condición de control del bucle. Ejemplo: 1 def printUntil(x: Int) = { 2 //variable local 3 var s = 0; 4 while (s <= x){ 5 println("Valor: " + s); 6 s += 1; 7 } 8 } Algoritmo 1.10: Ejemplo de bucle while. 1.5.2.2. Bucles do...while Al igual que los bucles while, los bucles do...while repetirán un bloque de sentencias hasta que la condición de control del bucle sea falsa. Por tanto, al igual que en el bucle while, el cuerpo del bucle se ejecuta mientras la expresión lógica sea cierta. Los bucles do...while también se denominan post-prueba ya que, a diferencia de los bucles while, los bucles do...while evalúan Página 24
  • 41. la condición después de ejecutar el cuerpo del bucle, motivo por el cual este tipo de bucles garantizan la ejecución del cuerpo del bucle al menos una vez. La sintaxis de un bucle while es: 1 do { 2 // Sentencias que se ejecutaran mientras 3 // la condicion sea cierta 4 } while (condicion); Algoritmo 1.11: Sintaxis bucles do... while. Se puede observar que la expresión lógica o booleana aparece al final del bucle por lo que el cuerpo del bucle se ejecutará al menos una vez. Si la condición se evalúa a cierta, el flujo de control saltará hasta la declaración do y el cuerpo del bucle se ejecutará nuevamente. Este proceso se repetirá hasta que la condición se evalúe a falsa. Ejemplo: 1 def printUntil2(x: Int) = { 2 //variable local 3 var s = 0; 4 do { 5 println("Valor: " + s); 6 s += 1; 7 }while (s <= x) 8 } Algoritmo 1.12: Ejemplo de bucle do... while. 1.5.2.3. Bucles for Los bucles for son sentencias que nos permitirán escribir eficientemente bucles que ten- gan que repetirse un número determinado de veces. En Scala podemos encontrar las siguientes variantes de bucles for: Bucles for con rangos Bucles for con colecciones Bucles for con filtros A continuación se verán los conceptos básicos de este tipo de bucles aunque, la especificidad de su implementación en Scala y su utilidad harán, al contrario de lo que se podría imaginar, que este tipo de bucles sean también muy útiles en programación funcional, capítulo en el cual se estudiarán con mayor profundidad (véase el Capítulo 3: Programación Funcional en Scala « página 53 »). Bucles for con rangos La forma más fácil de definir bucles for es haciendo uso del tipo de datos Range definido en Scala. El bucle se repetirá tantas veces como valores contenga el tipo de datos Range dado (véase la Subsubsección 3.11.3.1: Definición de rangos en Scala. La clase Range « página 107 »). La sintaxis más simple de bucles for con rangos es: Página 25
  • 42. 1 for (a <- Range) { 2 // Sentencias que se ejecutaran tantas veces 3 // como valores contenga el tipo de datos 4 // Range dado 5 } Algoritmo 1.13: Sintaxis bucles for con rangos. Range puede ser un rango de números, normalmente representado de la forma i to j o i until j. Más adelante veremos este tipo de datos en mayor profundidad. El operador flecha izquierda (<-) recibe el nombre de generador, ya que es el encargado de generar los diferentes valores del rango. La variable de control de bucle (a) se inicializará con el primer valor del rango e irá tomando, en cada iteración, los diferentes valores del mismo. Ejemplo: 1 def printUntil3(x: Int) = { 2 //variable local 3 for (a <- 0 to x) { 4 println("Valor: " + a); 5 } 6 } Algoritmo 1.14: Ejemplo de bucle for con rangos. Dentro de los bucles for se pueden utilizar varios rangos, separando cada uno de ellos por el carácter punto y coma (;). En este caso el bucle iterará sobre todas las posibles combinaciones de los mismos. Ejemplo: scala> for (x<- 1 to 3;y<- 4 to 6) {println("Valor x: "+x+" Valor y: "+y)} Valor x: 1 Valor y: 4 Valor x: 1 Valor y: 5 Valor x: 1 Valor y: 6 Valor x: 2 Valor y: 4 Valor x: 2 Valor y: 5 Valor x: 2 Valor y: 6 Valor x: 3 Valor y: 4 Valor x: 3 Valor y: 5 Valor x: 3 Valor y: 6 Bucles for con colecciones Los bucles for sirven para recorrer fácilmente los elementos de una colección. La sintaxis de los bucles for con colecciones es: 1 for (a <- Collection) { 2 // Sentencias que se ejecutaran tantas veces 3 // como valores contenga el tipo de datos 4 // Collection dado 5 } Algoritmo 1.15: Sintaxis bucles for con colecciones Se puede iterar sobre los elementos de las distintas estructuras de datos definidas en la librería Scala.collection de Scala como listas, conjuntos,etc., así como sobre los tipos de datos Página 26
  • 43. creados por el usuario14 La variable de control de bucle (a) se inicializará con el primer valor de la colección e irá tomando el valor de los distintos elementos que componen la colección en las sucesivas iteraciones del bucle. Ejemplo: 1 def forLista = { 2 val numList = List(0,1,2,3,4,5,6,7,8,9,10); 3 for( a <- numList ){ 4 println( "Valor de a: " + a ); 5 } 6 } Algoritmo 1.16: Ejemplo bucle for con colecciones. En el ejemplo del algoritmo 1.16 se puede apreciar un bucle for recorriendo una colección de números. Se ha creado esta colección usando el tipo de datos List de Scala. Las colecciones en Scala se estudiarán en mayor profundidad en la Sección 3.11: Colecciones en Scala « página 102 ». Bucles for con filtros Los bucles for en Scala permiten emplear filtros para descartar la iteración sobre algunos elementos de la colección que se desea iterar (rangos, listas, conjuntos,...) que no cumplan con alguna propiedad. Para filtrar elementos se empleará una o más sentencias if. La sintaxis de los bucles for con filtros es: 1 for (a <- Collection|Range... 2 if condicion1; if condicion2...) { 3 // Sentencias que se ejecutaran tantas veces 4 // como valores del tipo de datos 5 // Collection|Range dado satisfagan los filtros 6 } Algoritmo 1.17: Sintaxis bucles for con filtros. La variable de control de bucle (a) se inicializará con el primer valor de la colección/rango que satisfaga las condiciones: condicion1 y condicion2, e irá tomando el valor de los distintos elementos que componen la colección y que también satisfagan las condiciones impuestas como filtros. 1 def forListaconFiltros = { 2 val numList = List(0,1,2,3,4,5,6,7,8,9,10); 3 for( a <- numList 4 if a <= 5; 5 if a != 3 ){ 6 println( "Valor de a: " + a ); 7 } 8 } Algoritmo 1.18: Ejemplo bucle for con filtros 14 Estos tipos de datos creados por el usuario deberán presentar unas características especiales que serán estudia- das con mayor detalle. Página 27
  • 44. En el ejemplo del algoritmo 1.18 se recorren los elementos de la lista numList que sean menores o iguales a 5 y distintos de 3. Bucles for con yield. Los bucles for en Scala permiten almacenar los resultados de un bucle for en una variable o que éstos sean el valor devuelto por una función. Para hacer esto se añadirá la palabra reservada yield al final del cuerpo del bucle. La sintaxis general de los bucles for con yield es: 1 for (a <- Collection|Range... 2 if condicion1; if condicion2...) { 3 // Sentencias que se ejecutaran tantas veces 4 // como valores del tipo de datos 5 // Collection dado satisfagan los filtros 6 }yield a Algoritmo 1.19: Sintaxis bucles for con yield. Yield generará un valor en cada iteración del bucle que será recordado por el bucle for15 . Cuando el bucle finalice, devolverá una colección del mismo tipo de datos que la colección que estamos iterando con los valores recordados. En los bucles for con yield se podrá también hacer uso de filtros, haciendo de estos una herramienta mucho más potente. En el algoritmo 1.20 podemos ver un ejemplo del uso de bucles for con yield y filtros. 1 def forListaconYield = { 2 val numList = List(0,1,2,3,4,5,6,7,8,9,10); 3 val lista= for( a <- numList 4 if a <= 5; 5 if a != 3 )yield a * 2 6 for (b<-lista){println ("Valor recordado por yield: "+b)} 7 } Algoritmo 1.20: Ejemplo bucle for con yield En el algoritmo 1.20 se utilizan dos bucles for. El primero de ellos generará una lista con el doble de los números de la lista numList que satisfagan los filtros del bucle. El segundo bucle for iterará sobre la lista resultante del primer bucle, imprimiendo los valores por pantalla. 1.6. Interacción con Java Una de las características más importantes de Scala es que hace muy fácil la interacción con el código escrito en Java. Todas las clases de la librería java.lang son importadas por de- fecto, mientras que las otras necesitan ser importadas explícitamente. Scala hace muy fácil la interactuación con código Java. Como se verá a lo largo de los próximos capítulos, Scala es un lenguaje de programación que ha sabido integrar algunas de las principales características presentes en los lenguajes de programación más populares. Java no es la excepción, y comparte muchas cosas con éste. La diferencia que se puede ver es que para cada uno de los conceptos de Java, Scala los aumenta, 15 Como si el bucle for tuviera un buffer que no se puede ver y al que en cada iteración se le añade un elemento Página 28
  • 45. refina y mejora. Poder aprender todas las características de Scala nos equipa con más y mejores herramientas a la hora de escribir nuestros programas. También es posible heredar de clases Java e implementar interfaces Java directamente en Scala. A continuación se presenta un ejemplo que demuestra esto. Se quiere obtener y formatear la fecha actual de acuerdo a convenciones utilizadas en un país específico, por ejemplo Francia. Las librerías de clases de Java definen clases de utilidades interesantes, como Date y Da- teFormat. Ya que Scala interacciona fácilmente con Java, no es necesario implementar estas clases equivalentes en las librerías de Scala, se pueden simplemente importar las clases de los correspondientes paquetes de Java: 1 import java.util.{Date, Locale} 2 import java.text.DateFormat._ 3 object FrenchDate { 4 def main(args: Array[String]) { 5 val ahora = new Date 6 val df = getDateInstance(LONG, Locale.FRANCE) 7 println(df format ahora) 8 } 9 } Algoritmo 1.21: Fecha actual formateada. Las declaraciones de importación de Scala parecen muy similares a las de Java, sin embargo, las primeras son bastante más potentes. Se pueden importar múltiples clases desde el mismo paquete al encerrarlas entre llaves, como se muestra en la primer linea. Otra diferencia es que se pueden importar todos los nombres de un paquete o clase, utilizando el carácter guión bajo (_) en lugar del asterisco (*). Eso es porque el asterisco es un identificador válido en Scala (por ejemplo se puede nombrar a un método). Por tanto, la declaración import en la segunda línea, importa todos los miembros de la clase DateFormat. Esto hace que el método estático getDateInstance y el campo estático LONG sean directamente visibles. Dentro del método main, primero se crea una instancia de la clase Date que por defecto con- tiene la fecha actual. A continuación se define un formateador de fechas, utilizando el método estático getDateInstance que ha sido importado previamente. Finalmente, se imprime la fecha actual formateada de acuerdo a la instancia de DateFormat que fue “localizada”. Esta última línea muestra un ejemplo de un método que toma un solo argumento y que ha sido utilizado como operador infijo (véase la Subsubsección 1.2.2.2: Operadores « página 10 »). Es decir, la expresión: df format ahora es solamente otra manera más corta de escribir la expresión: df.format(ahora) 1.6.1. Ejecución sobre la JVM Una de las características más relevantes de Java no es el lenguaje, sino su máquina virtual (JVM). Una pulida maquinaria que el equipo de HotSpot ha ido mejorando a lo largo de los años. Puesto que Scala es un lenguaje basado en la JVM, se integra a la perfección dentro de Java y Página 29
  • 46. su ecosistema (herramientas, librerías, IDE,...), por lo que no será necesario desprenderse de todas las inversiones hechas en el pasado. El compilador de Scala genera bytecode, siendo indistinguible a este nivel el código escrito en Java y el escrito en Scala. Adicionalmente, puesto que se ejecuta sobre la JVM, se beneficia del rendimiento y estabilidad de dicha plataforma. Y siendo un lenguaje de tipado estático, los programas construidos con Scala se ejecutan tan rápido como los programas Java. El hecho de ser un lenguaje basado en la JVM también es la consecuencia por la que al- gunos tipos de la JVM (como vectores) acaban siendo poco elegantes en Scala o que ciertas características, como la recursividad de cola, no están implementadas en la JVM y hay que simularlas. 1.7. Ejercicios Ejercicio 1. Supongamos el siguiente código en Scala: 1 val x = 10 2 val y = 20 3 def g(y: Int): Int = { 4 x+y 5 } 6 def prueba(z: Int): Int = { 7 val x = 0 8 g(x+y+z) 9 } 10 prueba(3) ¿Qué devuelve prueba(3)? ¿En qué ámbito se evalúa la expresión x+y+z ? ¿Y la expresión x+y ? ¿Qué valores tienen esas variables en el momento de la evaluación? Página 30
  • 47. Capítulo 2 Programación Orientada a Objetos en Scala 2.1. Introducción a la programación orientada a objetos en Scala 2.1.1. Características principales de la programación orientada a objetos La popularidad de lenguajes como Java, C# o Ruby han hecho que la POO sea un paradigma ampliamente aceptado entre la mayoría de desarrolladores. Aunque existen numerosos lengua- jes orientados a objetos en el ecosistema actual, únicamente podríamos encajar unos pocos si nos ceñimos a una definición estricta de orientación a objetos. Un lenguaje orientado a objetos “puro” debería presentar las siguientes características: Encapsulamiento/ocultación de información. Herencia. Polimorfismo/Enlace dinámico. Todos los tipos predefinidos son objetos. Todas las operaciones son llevadas a cabo mediante el envío de mensajes a objetos. Todos los tipos definidos por el usuario son objetos. 2.1.2. Scala como lenguaje orientado a objetos Scala da soporte a todas las características anteriores mediante la utilización de un modelo puro de orientación a objetos muy similar al presentado por Smalltalk (lenguaje creado por Alan Kay sobre el año 1980)1 . De manera adicional a todas las características puras de un lenguaje orientado a objetos presentadas anteriormente, Scala añade algunas innovaciones en el espacio de los lenguajes orientados a objetos: 1 http://en.wikipedia.org/wiki/Smalltalk Página 31
  • 48. Composición modular de los elementos mezclados (mixin). Mecanismo que permite la composición de clases para el diseño de componentes reutilizables, evitando los pro- blemas presentados por la herencia múltiple. Similar a los interfaces Java y las clases abstractas. Por una parte se pueden definir múltiples “contratos”(del mismo modo que los interfaces). Por otro lado, se podrían tener implementaciones concretas de los métodos. Self-type. Los rasgos mezclados no dependen de ningún método y/o atributo de aque- llas clases con las que se está entremezclando, aunque en determinadas ocasiones será necesario hacer uso de las mismas. Esta capacidad es conocida en Scala como self-type. Abstracción de tipos. Existen dos mecanismos principales de abstracción en los lengua- jes de programación: la parametrización y los miembros abstractos. Scala soporta ambos estilos de abstracción de manera uniforme para tipos y valores. 2.2. Paquetes, clases, objetos y namespaces 2.2.1. Objetos Singleton Scala no soporta la definición de atributos estáticos en las clases, incorporando en su lugar el concepto de objeto singleton. La definición de objetos de este tipo es muy similar a la de las clases, salvo que se utiliza la palabra reservada object en lugar de class. Cuando un objeto singleton comparte el mismo nombre de una clase, el primero de ellos es conocido como com- panion object (objeto acompañante), mientras que la clase se denomina companion class (clase acompañante) del objeto singleton. Inicialmente, sobre todo aquellos desarrolladores provenien- tes del mundo Java, podrían ver este tipo de objetos como un contenedor en el que se podrían definir tantos métodos estáticos como quisiéramos. Una de las principales diferencias entre los objetos singleton y las clases es que los pri- meros no aceptan parámetros (no podemos instanciar un objeto singleton mediante la palabra reservada new), mientras que las clases sí lo permiten. Cada uno de los objetos singleton es implementado mediante una instancia de una clase synthetic referenciada desde una variable estática, por lo que presentan la misma semántica de inicialización que los estáticos de Java. Un objeto singleton es inicializado la primera vez que es accedido por algún código. 2.2.2. Módulos, objetos, paquetes y namespaces Si se quiere hacer referencia a un método declarado dentro de un objeto (object) se tendrá que hacer una llamada del tipo nombreObjeto.nombreMétodo(parámetros) ya que el método nombreMétodo habrá sido definido dentro del objeto nombreObjeto. En realidad, en Scala todos los valores serán objetos. El principal objetivo de un objeto es el de otorgar un namespace a sus miembros, algunas veces llamado módulo. Un objeto puede constar de cero o más miembros. Un miembro puede ser un método decla- rado con la palabra reservada def, o puede ser otro objeto declarado con las palabras reservadas val o object2 Para hacer referencia a los miembros de un objeto en Scala se utilizará la notación típica de la POO (se indicará el namespace del objeto seguido de un puto y del nombre del miembro). Ejemplo: MyModule.abs(42) 2 Los objetos también pueden contener otros tipos de objetos, que no se mencionarán en este capítulo. Página 32
  • 49. Si se observa la expresión 3 * 2, lo que se está haciendo realmente es llamar al miembro (método) * del objeto 3, es decir (3.*(2)). En general, los métodos que toman un solo argumento pueden ser usados con una sintaxis de infijo3 . De este modo, se puede comprobar como la llamada a MyModule abs 42 devolvería la misma salida que la llamada MyModule.abs(42). Es posible importar miembros de un namespace haciendo uso de la palabra reservada import. Ejemplo: import MyModule.abs Para importar todos los miembros de un namespace se usará el guión bajo. Ejemplo: import MyModule._4 En Scala, al igual que en Java, se puede utilizar la palabra reservada package, la cual creará un paquete (en realidad se crea un namespace). Por tanto, se puede crear un namespace tanto con object como con package pero si se utiliza package no se creará un objeto, por lo que, obviamente, no se podrá pasar como tal en otras llamadas. Además en un package no podrá haber definiciones de miembros usando las palabras reservadas val o def. 2.2.3. Clases Del mismo modo que en todos los lenguajes orientados a objetos, Scala permite la defini- ción de clases en las que se pueden añadir métodos y atributos. A continuación se muestra la definición de la clase MiPrimeraClase. 1 class MiPrimeraClase{ 2 val a = 1 3 } Algoritmo 2.1: Mi primera clase Si se quiere instanciar un objeto de la clase anterior habrá que hacer uso de la palabra reservada new. 1 val v = new MiPrimeraClase Cuando se defina una variable en Scala se tendrá que especificar la mutabilidad de la misma, pudiendo escoger entre: Utilizar la palabra reservada val para indicar que es inmutable. Una variable de este tipo es similar al uso de final en Java. Una vez inicializada, no se podrá reasignar jamás. De manera contraria, se podrá indicar que una variable es de tipo var, consiguiendo con esto que su valor pueda ser modificado durante todo su ciclo de vida. Uno de los principales mecanismos utilizados para garantizar la robustez de un objeto es la afirmación de que su conjunto de atributos (variables de instancia) permanece constante a lo largo de todo el ciclo de vida del mismo. El primer paso para evitar que agentes externos tengan acceso a los campos de una clase es declarar los mismos como private. Puesto que los campos privados sólo podrán ser accedidos desde métodos que se encuentran definidos en la misma clase, todo el código que podría modificar el estado del mismo estará localizado en dicha clase.5 3 En estos casos, en Scala podremos omitir el uso del punto y los paréntesis. Para más información, véase la Subsubsección 1.2.2.2: Operadores « página 10 » 4 Véase el algoritmo 1.21 (página 29) donde se podrán ver otros ejemplos de las declaraciones de importación en Scala 5 Por defecto, si no se especifica en el momento de la definición, los atributos y/o métodos de una clase tienen acceso público. Es decir, public es el cualificador por defecto en Scala. Página 33