Scala Language Es.26
Scala Language Es.26
Scala Language Es.26
#scala
Tabla de contenido
Acerca de 1
Observaciones 2
Versiones 2
Examples 3
Inicialización retrasada 4
Inicialización retrasada 4
Scala Quicksheet 6
Capítulo 2: Alcance 8
Introducción 8
Sintaxis 8
Examples 8
Un ámbito privado 8
Alcance protegido 9
Capítulo 3: Anotaciones 11
Sintaxis 11
Parámetros 11
Observaciones 11
Examples 11
Sintaxis 14
Observaciones 14
Examples 14
Introducción 15
Sintaxis 15
Observaciones 15
Examples 15
Sintaxis 18
Examples 18
Lo esencial 19
Sintaxis 22
Examples 22
Códigos generados 22
Observaciones 27
Examples 27
Tipo de clase simple 27
Sintaxis 31
Examples 31
Objetos Singleton 33
Objetos Acompañantes 33
Objetos 34
Constructores 36
Constructor primario 36
Constructores Auxiliares 37
Examples 38
Mapa 41
Filtrar 41
Tipos transitables 43
Doblez 44
Para cada 45
Reducir 45
Observaciones 47
Examples 47
Escollos 47
Observaciones 50
Examples 50
Ejemplo básico 50
Examples 51
Observaciones 53
Examples 53
Examples 55
Observaciones 56
Examples 56
Examples 60
Sintaxis 62
Examples 62
Extractores de tuplas 62
Extractores Regex 65
Extractores transformadores 65
Observaciones 67
Examples 67
Observaciones 70
Examples 70
Funciones anónimas 70
Subraya la taquigrafía 71
Composición 71
Examples 73
Examples 74
Composición 74
Sintaxis basica 75
Examples 78
Creando un futuro 78
Sintaxis 82
Observaciones 82
Examples 82
Conversión implícita 82
Parámetros implícitos 83
Clases Implícitas 84
Implicados en el REPL 85
Examples 87
Limitaciones a la inferencia 87
Examples 90
Conversión de colecciones Scala a colecciones Java y viceversa 90
Arrays 90
Observaciones 94
Examples 94
Introducción 98
Sintaxis 98
Observaciones 98
Examples 98
Accesos de campo 98
Método de llamadas 99
Examples 101
Examples 102
Sintaxis 110
Parámetros 110
Examples 110
Introducción 120
Sintaxis 120
Observaciones 120
Examples 120
Tratar 124
Ya sea 124
Opción 125
Examples 128
Observaciones 129
Examples 129
Sintaxis 131
Parámetros 131
Observaciones 131
Examples 131
Examples 133
Examples 135
Operadores incorporados 135
Introducción 138
Examples 138
Sintaxis 140
Parámetros 140
Examples 140
Examples 144
Introducción 146
Examples 146
Examples 149
Examples 151
Linealización 154
Examples 156
Examples 159
Introducción 160
Examples 160
Colecciones 160
Corriendo 161
Sintaxis 162
Parámetros 162
Examples 163
Scaladoc simple al método 163
Introducción 164
Examples 164
AplicarUso 164
FunctorUsage 164
Examples 166
Observaciones 167
Examples 167
Sintaxis 169
Examples 169
Examples 170
Examples 172
Examples 174
Observaciones 175
Los nombres de valores y variables deben estar en la caja inferior del camello 175
Examples 175
Examples 179
Observaciones 185
Examples 185
Sintaxis 187
Observaciones 187
Examples 187
Tipo de alias 187
Observaciones 189
Examples 189
var 189
val 190
def 190
Funciones 191
Examples 195
Covarianza 195
Invariancia 195
Contravarianza 196
Sintaxis 199
Examples 199
Zurra 200
Zurra 200
It is an unofficial and free Scala Language ebook created for educational purposes. All the content
is extracted from Stack Overflow Documentation, which is written by many hardworking individuals
at Stack Overflow. It is neither affiliated with Stack Overflow nor official Scala Language.
The content is released under Creative Commons BY-SA, and the list of contributors to each
chapter are provided in the credits section at the end of this book. Images may be copyright of
their respective owners unless otherwise specified. All trademarks and registered trademarks are
the property of their respective company owners.
Use the content presented in this book at your own risk; it is not guaranteed to be correct nor
accurate, please send your feedback and corrections to [email protected]
https://riptutorial.com/es/home 1
Capítulo 1: Empezando con Scala Language
Observaciones
Scala es un moderno lenguaje de programación multi-paradigma diseñado para expresar
patrones de programación comunes de una manera concisa, elegante y segura. Se integra sin
problemas las características de los lenguajes orientados a objetos y funcionales .
La mayoría de los ejemplos dados requieren una instalación de Scala en funcionamiento. Esta es
la página de instalación de Scala , y este es el ejemplo 'Cómo configurar Scala' . scalafiddle.net
es un buen recurso para ejecutar pequeños ejemplos de código en la web.
Versiones
2.10.1 2013-03-13
2.10.2 2013-06-06
2.10.3 2013-10-01
2.10.4 2014-03-24
2.10.5 2015-03-05
2.10.6 2015-09-18
2.11.0 2014-04-21
2.11.1 2014-05-21
2.11.2 2014-07-24
2.11.4 2014-10-30
2.11.5 2014-01-14
2.11.6 2015-03-05
2.11.7 2015-06-23
2.11.8 2016-03-08
2.11.11 2017-04-19
2.12.0 2016-11-03
https://riptutorial.com/es/home 2
Versión Fecha de lanzamiento
2.12.1 2016-12-06
2.12.2 2017-04-19
Examples
Hola mundo definiendo un método 'principal'
object Hello {
def main(args: Array[String]): Unit = {
println("Hello World!")
}
}
Demo en vivo
$ scalac HelloWorld.scala
Para ejecutarlo:
$ scala Hello
Cuando el tiempo de ejecución de Scala carga el programa, busca un objeto llamado Hello con un
método main . El método main es el punto de entrada del programa y se ejecuta.
Tenga en cuenta que, a diferencia de Java, Scala no tiene el requisito de nombrar objetos o
clases después del archivo en el que se encuentran. En su lugar, el parámetro Hello pasó en el
comando scala Hello refiere al objeto que se busca que contiene el método main que se ejecutará.
Es perfectamente posible tener varios objetos con métodos principales en el mismo archivo .scala
.
La matriz args contendrá los argumentos de línea de comando dados al programa, si los hay. Por
ejemplo, podemos modificar el programa así:
object HelloWorld {
def main(args: Array[String]): Unit = {
println("Hello World!")
for {
arg <- args
} println(s"Arg=$arg")
}
}
Compilarlo
https://riptutorial.com/es/home 3
$ scalac HelloWorld.scala
Y luego ejecutarlo:
$ scala HelloWorld 1 2 3
Hello World!
Arg=1
Arg=2
Arg=3
Demo en vivo
Al extender el rasgo de la App , puede evitar definir un método main explícito. Todo el cuerpo del
objeto HelloWorld se trata como "el método principal".
2.11.0
Inicialización retrasada
Según la documentación oficial , la App hace uso de una función llamada Inicialización
diferida . Esto significa que los campos de objeto se inicializan después de llamar al
método principal.
2.11.0
Inicialización retrasada
Según la documentación oficial , la App hace uso de una función llamada Inicialización
diferida . Esto significa que los campos de objeto se inicializan después de llamar al
método principal.
DelayedInitahora está en desuso para uso general, pero todavía es compatible con la
App como un caso especial. El soporte continuará hasta que se decida e implemente
una función de reemplazo.
Para acceder a los argumentos de la línea de comandos al extender la App , use this.args :
https://riptutorial.com/es/home 4
Al usar la App , el cuerpo del objeto se ejecutará como el método main , no hay necesidad de
anular main .
Scala puede ser usado como un lenguaje de scripting. Para demostrarlo, cree HelloWorld.scala
con el siguiente contenido:
println("Hello")
$ scala HelloWorld.scala
Hello
Si omite .scala (como si simplemente escribió scala HelloWorld ) el corredor buscará un archivo
.class compilado con .scala lugar de compilar y luego ejecutar el script.
Nota: Si se usa scala como lenguaje de scripting, no se puede definir ningún paquete.
En los sistemas operativos que utilizan bash o terminales de shell similares, los scripts de Scala se
pueden ejecutar utilizando un 'preámbulo de shell'. Cree un archivo llamado HelloWorld.sh y
coloque lo siguiente como contenido:
#!/bin/sh
exec scala "$0" "$@"
!#
println("Hello")
Las partes entre #! y !# es el 'preámbulo de shell', y se interpreta como un script bash. El resto es
Scala.
Una vez que haya guardado el archivo anterior, debe otorgarle permisos 'ejecutables'. En la
cáscara puedes hacer esto:
(Tenga en cuenta que esto le da permiso a todos: lea acerca de chmod para aprender cómo
configurarlo para conjuntos de usuarios más específicos).
$ ./HelloWorld.sh
Cuando ejecuta scala en un terminal sin parámetros adicionales, se abre un intérprete REPL
(Read-Eval-Print Loop):
https://riptutorial.com/es/home 5
nford:~ $ scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_66).
Type in expressions for evaluation. Or try :help.
scala>
scala> print(poem)
As halcyons we shall be
Pero en el REPL puede redefinir un valor val (lo que causaría un error en un programa Scala
normal, si se realizó en el mismo ámbito):
Para el resto de su sesión REPL, esta variable recién definida sombreará la variable previamente
definida. Los REPL son útiles para ver rápidamente cómo funcionan los objetos u otros códigos.
Todas las funciones de Scala están disponibles: puede definir funciones, clases, métodos, etc.
Scala Quicksheet
Descripción Código
https://riptutorial.com/es/home 6
Descripción Código
for {
x <- Seq(1,2,3)
Bucle anidado y <- Seq(4,5,6)
} print(x + ":" + y)
https://riptutorial.com/es/home 7
Capítulo 2: Alcance
Introducción
El alcance en Scala define desde dónde se puede acceder a un valor ( def , val , var o class ).
Sintaxis
• declaración
• declaración privada
• Declaración privada [este]
• Declaración privada de [fromWhere]
• declaración protegida
• Declaración protegida [de donde]
Examples
Ámbito público (predeterminado)
Por defecto, el alcance es public , se puede acceder al valor desde cualquier lugar.
package com.example {
class FooClass {
val x = "foo"
}
}
package an.other.package {
class BarClass {
val foo = new com.example.FooClass
foo.x // <- Accessing a public value from another package
}
}
Un ámbito privado
Cuando el alcance es privado, solo se puede acceder desde la clase actual u otras instancias de
la clase actual.
package com.example {
class FooClass {
private val x = "foo"
def aFoo(otherFoo: FooClass) {
otherFoo.x // <- Accessing from another instance of the same class
}
}
class BarClass {
val f = new FooClass
https://riptutorial.com/es/home 8
f.x // <- This will not compile
}
}
package com.example {
class FooClass {
private val x = "foo"
private[example] val y = "bar"
}
class BarClass {
val f = new FooClass
f.x // <- Will not compile
f.y // <- Will compile
}
}
El ámbito más restrictivo es el ámbito "objeto-privado" , que solo permite acceder a ese valor
desde la misma instancia del objeto.
class FooClass {
private[this] val x = "foo"
def aFoo(otherFoo: FooClass) = {
otherFoo.x // <- This will not compile, accessing x outside the object instance
}
}
Alcance protegido
El ámbito protegido permite acceder al valor desde cualquier subclase de la clase actual.
class FooClass {
protected val x = "foo"
}
class BarClass extends FooClass {
val y = x // It is a subclass instance, will compile
}
class ClassB {
val f = new FooClass
f.x // <- This will not compile
}
El alcance del paquete protegido permite acceder al valor solo desde cualquier subclase en un
paquete específico.
https://riptutorial.com/es/home 9
package com.example {
class FooClass {
protected[example] val x = "foo"
}
class ClassB extends FooClass {
val y = x // It's in the protected scope, will compile
}
}
package com {
class BarClass extends com.example.FooClass {
val y = x // <- Outside the protected scope, will not compile
}
}
https://riptutorial.com/es/home 10
Capítulo 3: Anotaciones
Sintaxis
• @AnAnnotation def someMethod = {...}
• @AnAnnotation class someClass {...}
• @AnnotatioWithArgs (annotation_args) def someMethod = {...}
Parámetros
Parámetro Detalles
Alguna
El nombre de la anotación.
anotación
Observaciones
Scala-lang proporciona una lista de anotaciones estándar y sus equivalentes de Java .
Examples
Usando una anotación
@deprecated
def anUnusedLegacyMethod(someArg: Any) = {
...
}
/**
https://riptutorial.com/es/home 11
* @param num Numerator
* @param denom Denominator
* @throws ArithmeticException in case `denom` is `0`
*/
class Division @throws[ArithmeticException](/*no annotation parameters*/) protected (num: Int,
denom: Int) {
private[this] val wrongValue = num / denom
El modificador de visibilidad (en este caso protected ) debe aparecer después de las anotaciones
en la misma línea. En caso de que la anotación acepte parámetros opcionales (como en este
caso @throws acepta una causa opcional), debe especificar una lista de parámetros vacía para la
anotación: () antes de los parámetros del constructor.
Nota: Se pueden especificar múltiples anotaciones, incluso del mismo tipo ( repetición de
anotaciones ).
De manera similar, con una clase de caso sin método de fábrica auxiliar (y causa especificada
para la anotación):
package animals
// Create Annotation `Mammal`
class Mammal(indigenous:String) extends scala.annotation.StaticAnnotation
scala>import scala.reflect.runtime.{universe ⇒ u}
https://riptutorial.com/es/home 12
scala>val platypusSymbol = platypusType.typeSymbol.asClass
platypusSymbol: reflect.runtime.universe.ClassSymbol = class Platypus
scala>platypusSymbol.annotations
List[reflect.runtime.universe.Annotation] = List(animals.reflection.Mammal("North America"))
https://riptutorial.com/es/home 13
Capítulo 4: Auto tipos
Sintaxis
• Tipo de rasgo {selfId => / otros miembros pueden referirse a selfId en caso de que this
signifique algo /}
• Tipo de rasgo {selfId: OtherType => / * otros miembros pueden usar selfId y será del tipo
OtherType * /
• Tipo de rasgo {selfId: OtherType1 with OtherType2 => / * selfId es de tipo OtherType1 y
OtherType2 * /
Observaciones
A menudo se utiliza con el patrón de pastel.
Examples
Ejemplo de auto tipo simple
Los auto tipos se pueden usar en rasgos y clases para definir restricciones en las clases
concretas a las que se mezcla. También es posible utilizar un identificador diferente para la this
usando esta sintaxis (útil cuando el objeto externo tiene que ser referenciado a partir de un objeto
interno).
Supongamos que desea almacenar algunos objetos. Para eso, crea interfaces para el
almacenamiento y para agregar valores a un contenedor:
trait Container[+T] {
def add(o: T): Unit
}
trait PermanentStorage[T] {
/* Constraint on self type: it should be Container
* we can refer to that type as `identifier`, usually `this` or `self`
* or the type's name is used. */
identifier: Container[T] =>
https://riptutorial.com/es/home 14
Capítulo 5: Biblioteca de continuaciones
Introducción
El estilo de paso de continuación es una forma de flujo de control que implica pasar a las
funciones el resto de la computación como un argumento de "continuación". La función en
cuestión luego invoca esa continuación para continuar la ejecución del programa. Una forma de
pensar en una continuación es como un cierre. La biblioteca de continuaciones de Scala trae
continuaciones delimitadas en la forma de los shift primitivos / reset al lenguaje.
Sintaxis
• reset {...} // Las continuaciones se extienden hasta el final del bloque de reinicio adjunto
• shift {...} // Crear una continuación indicando después de la llamada, pasándola al cierre
• A @cpsParam [B, C] // Un cálculo que requiere una función A => B para crear un valor de C
• @cps [A] // Alias para @cpsParam [A, A]
• @suspendable // Alias para @cpsParam [Unidad, Unidad]
Observaciones
shift y reset son estructuras de flujo de control primitivas, como Int.+ es una operación primitiva
y Long es un tipo primitivo. Son más primitivos que cualquiera de los dos en que las
continuaciones delimitadas se pueden usar para construir casi todas las estructuras de flujo de
control. No son muy útiles "listos para usar", pero realmente brillan cuando se usan en bibliotecas
para crear API ricas.
Las continuaciones y las mónadas también están estrechamente vinculadas. Las continuaciones
se pueden hacer en la mónada de continuación , y las mónadas son continuaciones porque su
operación de flatMap toma una continuación como parámetro.
Examples
Las devoluciones de llamada son continuaciones
https://riptutorial.com/es/home 15
El argumento de la función para readFile es una continuación, en que readFile invoca para
continuar la ejecución del programa después de que haya realizado su trabajo.
// After compilation, shift and reset are transformed back into closures
// The for comprehension first desugars to:
reset {
shift(readFile(path1)).flatMap { file1 => shift(readFile(path2)).foreach { file2 =>
processFiles(file1, file2) } }
}
// And then the callbacks are restored via CPS transformation
readFile(path1) { _.flatMap { file1 => // We see how shift moves the code after it into a
closure
readFile(path2) { _.foreach { file2 =>
processFiles(file1, file2)
}}
}} // And we see how reset closes all those closures
// And it looks just like the old version!
Si se llama shift fuera de un bloque de reset delimitador, se puede usar para crear funciones que
crean continuaciones dentro de un bloque de reset . Es importante tener en cuenta que el tipo de
shift no es solo (((A => B) => C) => A) , en realidad es (((A => B) => C) => (A @cpsParam[B, C])) .
Esa anotación marca dónde se necesitan las transformaciones de CPS. Las funciones que llaman
a shift sin reset tienen su tipo de retorno "infectado" con esa anotación.
https://riptutorial.com/es/home 16
devolver B C es el tipo de retorno "real" y, después de la transformación de CPS, la llamada a la
función tiene el tipo C
Aquí, ask almacenará la continuación en un mapa, y luego otro código puede recuperar esa
"sesión" y pasar el resultado de la consulta al usuario. De esta manera, go puede estar utilizando
una biblioteca asíncrona mientras su código parece un código imperativo normal.
https://riptutorial.com/es/home 17
Capítulo 6: Clase de opción
Sintaxis
• clase Algunos [+ T] (valor: T) extiende la opción [T]
Examples
Opciones como colecciones
Option s tienen algunas funciones útiles de orden superior que pueden entenderse fácilmente al
ver las opciones como colecciones con cero o un solo elemento , donde None comporta como la
colección vacía, y Some(x) comportan como una colección con un solo elemento, x .
En Java (y otros lenguajes), usar null es una forma común de indicar que no hay un valor
asociado a una variable de referencia. En Scala, se prefiere usar Option en lugar de usar null .
Option ajusta valores que pueden ser null .
Nonees una subclase de Option ajusta una referencia nula. Some es una subclase de Option
envuelve una referencia no nula.
Este es un código típico al llamar a una biblioteca de Java que podría devolver una referencia
nula:
https://riptutorial.com/es/home 18
// if null, then resource = None
// else resource = Some(resource)
Si getResource() devuelve un valor null , el resource será un objeto None . De lo contrario, será un
objeto Some(resource) . La forma preferida de manejar una Option es usar funciones de orden
superior disponibles dentro del tipo de Option . Por ejemplo, si desea verificar si su valor no es
None (similar a verificar si el value == null ), usaría la función isDefined :
De manera similar, para verificar una referencia null puede hacer esto:
Es preferible que trate la ejecución condicional en el valor envuelto de una Option (sin usar el
método 'excepcional' Option.get ) tratando la Option como una mónada y usando foreach :
Si se requiere una instancia de Resource lugar de una instancia de Option[Resource] ), aún puede
usar la Option para protegerse contra valores nulos. Aquí el método getOrElse proporciona un valor
predeterminado:
El código Java no manejará fácilmente la Option de Scala, por lo que al pasar valores al código
Java es una buena forma de desenvolver una Option , pasar un valor null o un valor
predeterminado razonable cuando sea apropiado:
Lo esencial
Una Option es una estructura de datos que contiene un solo valor o ningún valor en absoluto. Una
Option puede considerarse como colecciones de cero o uno de los elementos.
https://riptutorial.com/es/home 19
La opción es una clase abstracta con dos hijos: Some y None .
Optiones útil en expresiones que de lo contrario usarían null para representar la falta de un valor
concreto. Esto protege contra una NullPointerException y permite la composición de muchas
expresiones que pueden no devolver un valor utilizando combinadores como Map , FlatMap , etc.
println(countries.get("USA")) // Some(Washington)
println(countries.get("France")) // None
println(countries.get("USA").get) // Washington
println(countries.get("France").get) // Error: NoSuchElementException
println(countries.get("USA").getOrElse("Nope")) // Washington
println(countries.get("France").getOrElse("Nope")) // Nope
Option[A]está sellada y, por lo tanto, no puede extenderse. Por lo tanto, su semántica es estable
y se puede confiar en ella.
Option s tienen un método flatMap . Esto significa que pueden ser utilizados en una comprensión.
De esta manera podemos levantar funciones regulares para trabajar en las Option s sin tener que
redefinirlas.
Cuando uno de los valores es None el resultado final del cálculo también será None .
https://riptutorial.com/es/home 20
Nota: este patrón se extiende más generalmente para los conceptos llamados Monad s. (Más
información debe estar disponible en las páginas relacionadas con las comprensiones y Monad )
En general, no es posible mezclar diferentes mónadas en una comprensión. Pero dado que la
Option se puede convertir fácilmente en un Iterable , podemos combinar fácilmente las Option sy
Iterable llamando al método .toIterable .
Una pequeña nota: si hubiéramos definido nuestra comprensión para la otra manera, la
comprensión se compilaría ya que nuestra opción se convertiría implícitamente. Por esa razón, es
útil agregar siempre este .toIterable (o la función correspondiente según la colección que esté
usando) para .toIterable coherencia.
https://riptutorial.com/es/home 21
Capítulo 7: Clases de casos
Sintaxis
• clase de caso Foo () // Las clases de caso sin parámetros deben tener una lista vacía
• clase de caso Foo (a1: A1, ..., aN: AN) // Crear una clase de caso con los campos a1 ... aN
• objeto case Bar // Crear una clase de caso singleton
Examples
Igualdad de clase de caso
Códigos generados
https://riptutorial.com/es/home 22
def productElement(i: Int): Any = i match {
case 0 => name
case 1 => age
case _ => throw new IndexOutOfBoundsException(i.toString)
}
Cuando se aplica a un object , el modificador de case tiene efectos similares (aunque menos
dramáticos). Aquí las principales ganancias son una implementación de toString y un valor de
hashCode que es consistente en todos los procesos. Tenga en cuenta que los objetos de caso
(correctamente) utilizan la igualdad de referencia:
https://riptutorial.com/es/home 23
Todavía es posible implementar manualmente métodos que de otra manera serían
proporcionados por el modificador de case tanto en la clase en sí como en su objeto
complementario.
En comparación con las clases regulares, la notación de clases de casos brinda varios beneficios:
• Todos los argumentos del constructor son public y se puede acceder a ellos en objetos
inicializados (normalmente este no es el caso, como se muestra aquí):
• Proporciona una implementación para los siguientes métodos: toString , equals , hashCode
(basado en propiedades), copy , apply y no unapply :
sealed trait Animal // `sealed` modifier allows inheritance within current build-unit
only
case class Dog(age: Int) extends Animal
case class Cat(owner: String) extends Animal
val x: Animal = Dog(18)
x match {
case Dog(x) => println(s"It's a $x years old dog.")
case Cat(x) => println(s"This cat belongs to $x.")
}
El compilador de Scala prefija cada argumento en la lista de parámetros por defecto con val .
Esto significa que, por defecto, las clases de casos son inmutables. Cada parámetro recibe un
método de acceso, pero no hay métodos mutadores. Por ejemplo:
Declarar un parámetro en una clase de caso como var anula el comportamiento predeterminado y
https://riptutorial.com/es/home 24
hace que la clase de caso sea mutable:
Otra instancia cuando una clase de caso es 'mutable' es cuando el valor en la clase de caso es
mutable:
import scala.collection._
Tenga en cuenta que la 'mutación' que está ocurriendo aquí está en el mapa que m apunta, no m
sí. Por lo tanto, si algún otro objeto tuviera m como miembro, vería el cambio también. Observe
cómo en el siguiente ejemplo, cambiar la instanceA también cambia la instanceB :
import scala.collection.mutable
Las clases de casos proporcionan un método de copy que crea un nuevo objeto que comparte los
mismos campos que el anterior, con ciertos cambios.
Podemos usar esta función para crear un nuevo objeto a partir de uno anterior que tenga algunas
de las mismas características. Esta clase de caso simple demuestra esta característica:
case class Person(firstName: String, lastName: String, grade: String, subject: String)
val putu = Person("Putu", "Kevin", "A1", "Math")
val mark = putu.copy(firstName = "Ketut", lastName = "Mark")
// mark: People = People(Ketut,Mark,A1,Math)
En este ejemplo se puede ver que los dos objetos comparten características similares ( grade = A1
, subject = Math ), salvo que se han especificado en la copia ( firstName y lastName ).
https://riptutorial.com/es/home 25
Para lograr la seguridad de tipos, a veces queremos evitar el uso de tipos primitivos en nuestro
dominio. Por ejemplo, imagine una Person con un name . Típicamente, codificaríamos el name como
una String . Sin embargo, no sería difícil de mezclar una String que representa una Person 's name
con una String que representa un mensaje de error:
Para evitar tales riesgos, puede codificar los datos de esta manera:
y ahora nuestro código no se compilará si mezclamos PersonName con ErrorMessage , o incluso una
String normal.
Pero esto conlleva una pequeña sobrecarga en el tiempo de ejecución, ya que ahora tenemos
que encuadrar / desempaquetar String a / desde sus contenedores PersonName . Para evitar esto,
uno puede hacer que las clases de valores PersonName y ErrorMessage :
https://riptutorial.com/es/home 26
Capítulo 8: Clases de tipo
Observaciones
Para evitar problemas de serialización, particularmente en entornos distribuidos (por ejemplo,
Apache Spark ), es una buena práctica implementar el rasgo Serializable para instancias de
clase de tipo.
Examples
Tipo de clase simple
Una clase de tipo es simplemente un trait con uno o más parámetros de tipo:
trait Show[A] {
def show(a: A): String
}
En lugar de ampliar una clase de tipo, se proporciona una instancia implícita de la clase de tipo
para cada tipo admitido. La colocación de estas implementaciones en el objeto complementario
de la clase de tipo permite que la resolución implícita funcione sin ninguna importación especial:
object Show {
implicit val intShow: Show[Int] = new Show {
def show(x: Int): String = x.toString
}
// ..etc
}
Si desea garantizar que un parámetro genérico pasado a una función tiene una instancia de una
clase de tipo, use parámetros implícitos:
Llame al método de log anterior como cualquier otro método. No se compilará si no se puede
https://riptutorial.com/es/home 27
encontrar Show[A] implementación implícita de Show[A] para la A que se pasa al log
Este ejemplo implementa la clase de tipo Show . Esta es una clase de tipo común utilizada para
convertir instancias arbitrarias de tipos arbitrarios en String . Aunque cada objeto tiene un método
toString , no siempre está claro si toString se define o no de una manera útil. Con el uso de la
clase de tipo Show , puede garantizar que todo lo que se pase al log tenga una conversión bien
definida a String .
trait Show[A] {
def show: String
}
Para hacer que una clase que usted controla (y está escrita en Scala) extienda la clase de tipo,
agregue un implícito a su objeto complementario. Vamos a mostrar cómo podemos obtener la
clase Person de este ejemplo para ampliar Show :
Podemos hacer que esta clase amplíe Show agregando un implícito al objeto complementario de
Person :
object Person {
implicit val personShow: Show[Person] = new Show {
def show(p: Person): String = s"Person(${p.fullname})"
}
}
Un objeto complementario debe estar en el mismo archivo que la clase, por lo que necesita la
class Person y el object Person en el mismo archivo.
Para hacer una clase que no controla, o no está escrita en Scala, extienda la clase de tipo,
agregue un implícito al objeto compañero de la clase de tipo, como se muestra en el ejemplo de
Clase de tipo simple .
Si no controla ni la clase ni la clase de tipo, cree un implícito como el de arriba en cualquier lugar
e import . Usando el método de log en el ejemplo de clase de tipo simple :
object MyShow {
https://riptutorial.com/es/home 28
implicit val personShow: Show[Person] = new Show {
def show(p: Person): String = s"Person(${p.fullname})"
}
}
La implementación de Scala de las clases de tipos es bastante detallada. Una forma de reducir la
verbosidad es introducir las llamadas "clases de operación". Estas clases envolverán
automáticamente una variable / valor cuando se importen para ampliar la funcionalidad.
object Instances {
import Instances._
val three = addableInt.add(1,2)
Preferiríamos simplemente escribir escribir 1.add(2) . Por lo tanto, crearemos una "Clase de
operación" (también llamada "Clase de Operaciones") que siempre se ajustará a un tipo que
implementa Addable .
object Ops {
implicit class AddableOps[A](self: A)(implicit A: Addable[A]) {
def add(other: A): A = A.add(self, other)
https://riptutorial.com/es/home 29
}
}
Ahora podemos usar nuestra nueva función add como si fuera parte de Int y String :
object Main {
println(1.add(5))
println("mag".add("net"))
// println(1.add(3.141)) // Fails because we didn't create an instance for Double
}
}
import simulacrum._
https://riptutorial.com/es/home 30
Capítulo 9: Clases y objetos
Sintaxis
• class MyClass{} // curly braces are optional here as class body is empty
• class MyClassWithMethod {def method: MyClass = ???}
• new MyClass() //Instantiate
• object MyObject // Singleton object
• class MyClassWithGenericParameters[V1, V2](vl: V1, i: Int, v2: V2)
• class MyClassWithImplicitFieldCreation[V1](val v1: V1, val i: Int)
• new MyClassWithGenericParameters(2.3, 4, 5) o con un tipo diferente: new
MyClassWithGenericParameters[Double, Any](2.3, 4, 5)
• class MyClassWithProtectedConstructor protected[my.pack.age](s: String)
Examples
Instancia de instancias de clase
Una clase en Scala es un "plano" de una instancia de clase. Una instancia contiene el estado y el
comportamiento definidos por esa clase. Para declarar una clase:
class MyClass{} // curly braces are optional here as class body is empty
Se puede crear una instancia de una instancia usando una new palabra clave:
o:
Los paréntesis son opcionales en Scala para crear objetos de una clase que tiene un constructor
sin argumentos. Si un constructor de clase toma argumentos:
Aquí MyClass requiere un argumento Int , que solo puede usarse internamente para la clase. arg
no se puede acceder fuera de MyClass menos que se declare como un campo:
https://riptutorial.com/es/home 31
Alternativamente se puede declarar público en el constructor:
class MyClass(val arg : Int) // Class definition with arg declared public
var instance = new MyClass(2) // Instance instantiation
instance.arg //arg is now visible to clients
class MyClass
Pero, si no se presta atención, en algunos casos, el paréntesis opcional puede producir algún
comportamiento inesperado. Supongamos que queremos crear una tarea que debería ejecutarse
en un hilo separado. A continuación se muestra el código de ejemplo:
Podemos pensar que este código de ejemplo, si se ejecuta, imprimirá la Performing task. , pero
para nuestra sorpresa, no imprimirá nada. Veamos que está pasando aquí. Si observa
detenidamente, hemos utilizado llaves {} , justo después del new Thread . Creó una clase anónima
que extiende Thread :
Y luego, en el cuerpo de esta clase anónima, definimos nuestra tarea (nuevamente creando una
clase Runnable implementa la interfaz Runnable ). Así que podríamos haber pensado que usamos el
constructor public Thread(Runnable target) pero de hecho (al ignorar opcional () ) usamos el
constructor public Thread() sin nada definido en el cuerpo del método run() . Para corregir el
problema, necesitamos usar paréntesis en lugar de llaves.
https://riptutorial.com/es/home 32
val newThread = new Thread ( new Runnable {
override def run(): Unit = {
// perform task
println("Performing task.")
}
}
)
Objetos Singleton
Scala admite miembros estáticos, pero no de la misma manera que Java. Scala proporciona una
alternativa a esto llamada Objetos Singleton . Los objetos Singleton son similares a una clase
normal, excepto que no se pueden crear instancias con la new palabra clave. A continuación se
muestra una muestra de clase singleton:
object Factorial {
private val cache = Map[Int, Int]()
def getCache = cache
}
Tenga en cuenta que hemos utilizado la palabra clave object para definir el objeto singleton (en
lugar de 'class' o 'trait'). Como los objetos singleton no pueden ser instanciados, no pueden tener
parámetros. El acceso a un objeto singleton se ve así:
Tenga en cuenta que esto se ve exactamente como acceder a un método estático en una clase
de Java.
Objetos Acompañantes
En Scala, los objetos singleton pueden compartir el nombre de una clase correspondiente. En tal
escenario, el objeto singleton se conoce como un objeto complementario . Por ejemplo, debajo de
la clase Factorial se define, y un objeto complementario (también llamado Factorial ) se define
debajo de ella. Por convención, los objetos complementarios se definen en el mismo archivo que
su clase complementaria.
def fact(num : Int) : Int = if (num <= 1) 1 else (num * fact(num - 1))
https://riptutorial.com/es/home 33
Factorial.cache(num)
}
}
object Factorial {
private val cache = scala.collection.mutable.Map[Int, Int]()
}
En este ejemplo, estamos usando un cache privado para almacenar factorial de un número para
ahorrar tiempo de cálculo para números repetidos.
Tenga en cuenta que una nueva instanciación de la clase seguirá utilizando el mismo objeto
complementario, por lo que cualquier modificación de las variables miembro de ese objeto se
transferirá.
Objetos
Mientras que las clases son más como planos, los objetos son estáticos (es decir, ya están
instanciados):
object Dog {
def bark: String = "Raf"
}
object Dog {
def apply(name: String): Dog = new Dog(name)
}
https://riptutorial.com/es/home 34
Verificación de tipo : variable.isInstanceOf[Type]
variable match {
case _: Type => true
case _ => false
}
Tanto isInstanceOf como la coincidencia de patrones están verificando solo el tipo de objeto, no
su parámetro genérico (sin reificación de tipo), excepto los arreglos:
Pero
variable match {
case _: Type => true
}
Ejemplos:
https://riptutorial.com/es/home 35
val x = 3 //> x : Int = 3
x match {
case _: Int => true//better: do something
case _ => false
} //> res0: Boolean = true
x match {
case _: java.lang.Integer => true//better: do something
case _ => false
} //> res1: Boolean = true
Observación: esto es solo sobre el comportamiento en la JVM, en otras plataformas (JS, nativo) el
tipo de conversión / comprobación podría comportarse de manera diferente.
Constructores
Constructor primario
En Scala el constructor primario es el cuerpo de la clase. El nombre de la clase va seguido de una
lista de parámetros, que son los argumentos del constructor. (Como con cualquier función, se
puede omitir una lista de parámetros vacía).
class Bar {
...
}
https://riptutorial.com/es/home 36
val bar1 = new Bar() // Constructor parentheses are optional here
Cualquier operación que deba realizarse cuando se crea una instancia de un objeto se escribe
directamente en el cuerpo de la clase:
class DatabaseConnection
(host: String, port: Int, username: String, password: String) {
/* first connect to the DB, or throw an exception */
private val driver = new AwesomeDB.Driver()
driver.connect(host, port, username, password)
def isConnected: Boolean = driver.isConnected
...
}
Tenga en cuenta que se considera una buena práctica poner la menor cantidad posible de efectos
secundarios en el constructor; en lugar del código anterior, se debe considerar tener métodos de
connect y disconnect para que el código del consumidor sea responsable de programar la IO.
Constructores Auxiliares
Una clase puede tener constructores adicionales llamados 'constructores auxiliares'. Estas están
definidas por definiciones de constructor en la forma def this(...) = e , donde e debe invocar a
otro constructor:
// usage:
new Person("Grace Hopper").fullName // returns Grace Hopper
new Person("Grace", "Hopper").fullName // returns Grace Hopper
Esto implica que cada constructor puede tener un modificador diferente: solo algunos pueden
estar disponibles públicamente:
De esta manera puede controlar cómo el código de consumidor puede instanciar la clase.
https://riptutorial.com/es/home 37
Capítulo 10: Colecciones
Examples
Ordenar una lista
names.sorted
// results in: List(Alana, Allie, Beth, Kathryn, Serin)
Siempre puede revertir una lista, o una lista ordenada, usando `reverse:
names.sorted.reverse
//results in: List(Serin, Kathryn, Beth, Allie, Alana)
java.util.Arrays.sort(data)
scala.util.Sorting.quickSort(data)
Estos métodos pueden mejorar el rendimiento al ordenar colecciones más grandes si se pueden
evitar las conversiones de colección y el desempaquetado / boxeo. Para una discusión más
detallada sobre las diferencias de rendimiento, lea acerca de Scala Collection ordenados,
https://riptutorial.com/es/home 38
sortWith y sortBy Performance .
Para crear una colección de n copias de algún objeto x , use el método de relleno . Este ejemplo
crea una List , pero esto puede funcionar con otras colecciones para las que el fill tiene sentido:
// List.fill(n)(x)
scala > List.fill(3)("Hello World")
res0: List[String] = List(Hello World, Hello World, Hello World)
Ahora es una práctica recomendada utilizar Vector lugar de List porque las
implementaciones tienen un mejor rendimiento Las características de rendimiento se
pueden encontrar aquí . Vector se puede utilizar donde se utiliza la List .
Creación de listas
Tomar elemento
Elementos Prependidos
0 :: List(1, 2, 3) // List(0, 1, 2, 3)
Anexar elementos
https://riptutorial.com/es/home 39
Operaciones comunes
Tenga en cuenta que esto se refiere a la creación de una colección de tipo Map , que es
distinta del método de map .
Creación de mapas
Map[String, Int]()
val m1: Map[String, Int] = Map()
val m2: String Map Int = Map()
Un mapa puede considerarse una colección de tuples para la mayoría de las operaciones, donde
el primer elemento es la clave y el segundo es el valor.
Obtener elemento
m.get("a") // Some(1)
m.get("d") // None
m("a") // 1
m("d") // java.util.NoSuchElementException: key not found: d
m.keys // Set(a, b, c)
m.values // MapLike(1, 2, 3)
Map("a" -> 1, "b" -> 2) + ("c" -> 3) // Map(a -> 1, b -> 2, c -> 3)
Map("a" -> 1, "b" -> 2) + ("a" -> 3) // Map(a -> 3, b -> 2)
Map("a" -> 1, "b" -> 2) ++ Map("b" -> 3, "c" -> 4) // Map(a -> 1, b -> 3, c -> 4)
Operaciones comunes
En las operaciones donde ocurre una iteración sobre un mapa ( map , find , forEach , etc.), los
elementos de la colección son tuples . El parámetro de función puede usar los accesores de tupla
( _1 , _2 ), o una función parcial con un bloque de caso:
https://riptutorial.com/es/home 40
m.map {
case (key, value) => (value, key)
} // Map(1 -> a, 2 -> b, 3 -> c)
m.filter(_._2 == 2) // Map(b -> 2)
m.foldLeft(0){
case (acc, (key, value: Int)) => acc + value
} // 6
Mapa
El 'Mapeo' a través de una colección usa la función de map para transformar cada elemento de esa
colección de una manera similar. La sintaxis general es:
// Initialize
val list = List(1,2,3)
// list: List[Int] = List(1, 2, 3)
// Apply map
list.map((item: Int) => item*2)
// res0: List[Int] = List(2, 4, 6)
Filtrar
filterse utiliza cuando desea excluir o 'filtrar' ciertos elementos de una colección. Al igual que
con map , la sintaxis general toma una función, pero esa función debe devolver un Boolean :
https://riptutorial.com/es/home 41
Comprobando los números de pares
El marco de Scala Collections, según sus autores , está diseñado para ser fácil de usar, conciso,
seguro, rápido y universal.
El marco se compone de rasgos de Scala que están diseñados para ser bloques de construcción
para crear colecciones. Para obtener más información sobre estos bloques de construcción, lea el
resumen oficial de las colecciones de Scala .
Estas colecciones integradas se separan en los paquetes inmutables y mutables. Por defecto, se
utilizan las versiones inmutables. Construir una List() (sin importar nada) construirá una lista
inmutable .
Una de las características más poderosas del marco es la interfaz consistente y fácil de usar a
través de colecciones afines. Por ejemplo, sumar todos los elementos en una colección es igual
para Listas, Conjuntos, Vectores, Seqs y Arrays:
https://riptutorial.com/es/home 42
numList.reduce((n1, n2) => n1 + n2) // 15
Ahora es una práctica recomendada utilizar Vector lugar de List porque las
implementaciones tienen un mejor rendimiento Las características de rendimiento se
pueden encontrar aquí . Vector se puede utilizar donde se utiliza la List .
Tipos transitables
Clases de colección que tienen el Traversable rasgo implementar foreach y heredar muchos
métodos para realizar operaciones comunes a las colecciones, que todos funcionan de forma
idéntica. Las operaciones más comunes se enumeran aquí:
• Mapa - map , flatMap y collect producir nuevas colecciones mediante la aplicación de una
función a cada elemento de la colección original.
// split list of letters into individual strings and put them into the same list
List("a b c", "d e").flatMap(letters => letters.split(" ")) // = List("a", "b", "c", "d", "e")
val array: Array[Int] = List[Int](1, 2, 3).toArray // convert list of ints to array of ints
List().isEmpty // true
List(1).nonEmpty // true
• Recuperación de elementos : head , last , find y sus variantes de Option se utilizan para
recuperar el primer o último elemento, o encontrar un elemento específico en la colección.
https://riptutorial.com/es/home 43
y otras permiten elegir partes de la colección para seguir operando.
List(1, 2, 3, 4).forall(num => num > 0) // = true, all numbers are positive
List(-3, -2, -1, 1).forall(num => num < 0) // = false, not all numbers are negative
Doblez
El método de fold recorre una colección, utilizando un valor inicial del acumulador y aplicando
una función que utiliza cada elemento para actualizar el acumulador con éxito:
En el ejemplo anterior, se proporcionó una función anónima para fold() . También puede usar
una función nombrada que toma dos argumentos. Teniendo esto en mi, el ejemplo anterior se
puede reescribir así:
initialValue = 2;
sum = nums.fold(initialValue){
(accumulator,currentElementBeingIterated) => accumulator + currentElementBeingIterated
}
println(sum) //prints 17 because 2+1+2+3+4+5 = 17
https://riptutorial.com/es/home 44
El método de fold tiene dos variantes: foldLeft y foldRight .
foldLeft() itera de izquierda a derecha (desde el primer elemento de la colección hasta el último
en ese orden). foldRight() itera de derecha a izquierda (desde el último elemento hasta el primer
elemento). fold() itera de izquierda a derecha como foldLeft() . De hecho, fold() realidad llama a
foldLeft() internamente.
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
fold(), foldLeft() y foldRight() devolverá un valor que tiene el mismo tipo con el valor inicial que
toma. Sin embargo, a diferencia de foldLeft() y foldRight() , el valor inicial dado para fold() solo
puede ser del mismo tipo o un supertipo del tipo de la colección.
En este ejemplo, el orden no es relevante, por lo que puede cambiar fold() por foldLeft() o
foldRight() y el resultado seguirá siendo el mismo. El uso de una función que sea sensible al
orden alterará los resultados.
En caso de duda, prefiera foldLeft() sobre foldRight() . foldRight() tiene menos rendimiento.
Para cada
La función suministrada a foreach puede tener cualquier tipo de retorno, pero el resultado se
descartará . Normalmente se usa foreach cuando los efectos secundarios son deseables. Si
desea transformar los datos, considere usar un map , un filter , una for comprehension u otra
opción.
Reducir
Los métodos reduce() , reduceLeft() y reduceRight son similares a los pliegues. La función pasada
para reducir toma dos valores y produce un tercero. Cuando se opera en una lista, los dos
primeros valores son los dos primeros valores de la lista. El resultado de la función y el siguiente
valor en la lista se vuelven a aplicar a la función, produciendo un nuevo resultado. Este nuevo
resultado se aplica con el siguiente valor de la lista y así sucesivamente hasta que no haya más
https://riptutorial.com/es/home 45
elementos. Se devuelve el resultado final.
Existen algunas diferencias en cómo funcionan las funciones de reducción en comparación con
las funciones de plegado. Son:
https://riptutorial.com/es/home 46
Capítulo 11: Colecciones paralelas
Observaciones
Las colecciones paralelas facilitan la programación paralela al ocultar los detalles de
paralelización de bajo nivel. Esto hace que tomar ventaja de las arquitecturas de múltiples
núcleos sea fácil. Los ejemplos de colecciones paralelas incluyen ParArray , ParVector ,
mutable.ParHashMap , immutable.ParHashMap y ParRange . Una lista completa se puede encontrar en la
documentación .
Examples
Creación y uso de colecciones paralelas
Para crear una colección paralela a partir de una colección secuencial, llame al método par . Para
crear una colección secuencial a partir de una colección paralela, llame al método seq . Este
ejemplo muestra cómo convertir un Vector regular en un ParVector y luego de nuevo:
scala> parVect.seq
res0: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3, 4, 5)
El método par se puede encadenar, lo que le permite convertir una colección secuencial en una
colección paralela y realizar una acción de inmediato:
scala> vect.map(_ * 2)
res1: scala.collection.immutable.Vector[Int] = Vector(2, 4, 6, 8, 10)
scala> vect.par.map(_ * 2)
res2: scala.collection.parallel.immutable.ParVector[Int] = ParVector(2, 4, 6, 8, 10)
Escollos
Las colecciones paralelas realizan operaciones concurrentemente. Eso significa que todo el
trabajo se divide en partes y se distribuye a diferentes procesadores. Cada procesador desconoce
el trabajo realizado por otros. Si el orden de la colección importa, entonces el trabajo procesado
https://riptutorial.com/es/home 47
en paralelo no es determinista. (Ejecutar el mismo código dos veces puede producir resultados
diferentes).
Operaciones no asociativas
scala> list.reduce(_ - _)
res0: Int = -500498
scala> list.reduce(_ - _)
res1: Int = -500498
scala> list.reduce(_ - _)
res2: Int = -500498
scala> listPar.reduce(_ - _)
res3: Int = -408314
scala> listPar.reduce(_ - _)
res4: Int = -422884
scala> listPar.reduce(_ - _)
res5: Int = -301748
Efectos secundarios
Las operaciones que tienen efectos secundarios, como foreach , pueden no ejecutarse como se
desea en colecciones en paralelo debido a las condiciones de la carrera. Evite esto utilizando
funciones que no tengan efectos secundarios, como reduce o map .
https://riptutorial.com/es/home 48
Lea Colecciones paralelas en línea: https://riptutorial.com/es/scala/topic/3882/colecciones-
paralelas
https://riptutorial.com/es/home 49
Capítulo 12: Combinadores de analizador
Observaciones
Casos de ParseResult
• Éxito, con un marcador en cuanto al inicio del partido y el siguiente carácter que se va a
combinar.
• Fracaso, con un marcador en el inicio de donde se intentó la coincidencia. En este caso, el
analizador retrocede a esa posición, donde estará cuando el análisis continúe.
• Error, que detiene el análisis. No se realiza ningún retroceso o análisis adicional.
Examples
Ejemplo básico
import scala.util.parsing.combinator._
[1.1] failure: string matching regex `[A-Z][a-z]+' expected but `b' found
[1.6]en el ejemplo de Alice indica que el inicio de la partida está en la posición 1 , y el primer
carácter que queda para coincidir comienza en la posición 6 .
https://riptutorial.com/es/home 50
Capítulo 13: Configurando Scala
Examples
En Linux a través de dpkg
En las distribuciones basadas en Debian, incluyendo Ubuntu, la forma más sencilla es usar el
archivo de instalación .deb . Ir al sitio web de Scala . Elija la versión que desea instalar, luego
desplácese hacia abajo y busque scala-xxxdeb .
which scala
La respuesta devuelta debe ser el equivalente a lo que colocó en su variable PATH. Para verificar
que scala está funcionando:
scala
Esto debería iniciar el REPL de Scala e informar la versión (que, a su vez, debe coincidir con la
versión que descargó).
curl -O http://downloads.lightbend.com/scala/2.xx.x/scala-2.xx.x.tgz
unzip scala-2.xx.x.tgz
mv scala-2.xx.x /usr/local/share/scala
Agregue el PATH a ~/.profile o ~/.bash_profile o ~/.bashrc incluyendo este texto en uno de esos
archivos:
$SCALA_HOME=/usr/local/share/scala
export PATH=$SCALA_HOME/bin:$PATH
https://riptutorial.com/es/home 51
which scala
La respuesta devuelta debe ser el equivalente a lo que colocó en su variable PATH . Para verificar
que scala está funcionando:
scala
Esto debería iniciar el REPL de Scala e informar la versión (que, a su vez, debe coincidir con la
versión que descargó).
En computadoras Mac OSX con MacPorts instalado, abra una ventana de terminal y escriba:
Esto mostrará una lista de todos los paquetes relacionados con Scala disponibles. Para instalar
uno (en este ejemplo, la versión 2.11 de Scala):
which scala
scala
https://riptutorial.com/es/home 52
Capítulo 14: Corrientes
Observaciones
Los flujos se evalúan perezosamente, lo que significa que se pueden usar para implementar
generadores, que proporcionarán o 'generarán' un nuevo elemento del tipo especificado a pedido,
en lugar de antes del hecho. Esto asegura que solo se realicen los cálculos necesarios.
Examples
Uso de un flujo para generar una secuencia aleatoria
genRandomcrea una secuencia de números aleatorios que tiene una probabilidad de uno en cuatro
de terminar cada vez que se llama.
lazy val randos = genRandom // getRandom is lazily evaluated as randos is iterated through
for {
x <- randos
} println(x) // The number of times this prints is effectively randomized.
Tenga en cuenta la construcción #:: , que recurre perezosamente : debido a que está
anteponiendo el número aleatorio actual a una secuencia, no evalúa el resto de la secuencia
hasta que se itera.
Se pueden construir flujos que se refieran a sí mismos y, por lo tanto, se vuelvan infinitamente
recursivos.
// factorial
val fact: Stream[BigInt] = 1 #:: fact.zipWithIndex.map{case (p,x)=>p*(x+1)}
fact.take(10) // (1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880)
fact(24) // 620448401733239439360000
https://riptutorial.com/es/home 53
// random Ints between 10 and 99 (inclusive)
def rndInt: Stream[Int] = (util.Random.nextInt(90)+10) #:: rndInt
rndInt.take(10) // (20, 95, 14, 44, 42, 78, 85, 24, 99, 85)
En este contexto, la diferencia entre Var, Val y Def es interesante. Como def cada elemento se
recalcula cada vez que se hace referencia. Como valor val cada elemento se conserva y se
reutiliza una vez calculado. Esto se puede demostrar creando un efecto secundario con cada
cálculo.
// now as val
val fact: Stream[Int] = 1 #:: fact.zipWithIndex.map{case (p,x)=>print("!");p*(x+1)}
fact(5) // !!!!! 120
fact(4) // 24
fact(7) // !! 5040
Esto también explica por qué la Stream números aleatorios no funciona como un val .
https://riptutorial.com/es/home 54
Capítulo 15: Cuasiquotes
Examples
Crear un árbol de sintaxis con quasiquotes
object macro {
def addCreationDate(): java.util.Date = macro impl.addCreationDate
}
object impl {
def addCreationDate(c: Context)(): c.Expr[java.util.Date] = {
import c.universe._
Puede ser arbitrariamente complejo, pero será validado para la sintaxis de scala correcta.
https://riptutorial.com/es/home 55
Capítulo 16: Enumeraciones
Observaciones
Se prefiere el enfoque con sealed trait y case objects porque la enumeración de Scala tiene
algunos problemas:
isWeekendWithBug(WeekDays.Fri)
scala.MatchError: Fri (of class scala.Enumeration$Val)
Comparar con:
Una explicación más detallada se presenta en este artículo sobre Scala Enumeration .
Examples
Días de la semana usando Scala Enumeration
isWeekend(WeekDays.Sun)
res0: Boolean = true
También es posible agregar un nombre legible para los valores en una enumeración:
https://riptutorial.com/es/home 56
object WeekDays extends Enumeration {
val Mon = Value("Monday")
val Tue = Value("Tuesday")
val Wed = Value("Wednesday")
val Thu = Value("Thursday")
val Fri = Value("Friday")
val Sat = Value("Saturday")
val Sun = Value("Sunday")
}
println(WeekDays.Mon)
>> Monday
WeekDays.withName("Monday") == WeekDays.Mon
>> res0: Boolean = true
Tenga cuidado con el comportamiento no tan seguro de los tipos, en el que diferentes
enumeraciones se pueden evaluar como el mismo tipo de instancia:
WeekDays.Mon.isInstanceOf[Parity.Value]
>> res1: Boolean = true
object WeekDay {
case object Mon extends WeekDay
case object Tue extends WeekDay
case object Wed extends WeekDay
case object Thu extends WeekDay
case object Fri extends WeekDay
case object Sun extends WeekDay
case object Sat extends WeekDay
}
La palabra clave sealed garantiza que el rasgo WeekDay no se puede extender en otro archivo. Esto
le permite al compilador hacer ciertas suposiciones, incluyendo que todos los valores posibles de
WeekDay ya están enumerados.
Un inconveniente es que este método no le permite obtener una lista de todos los valores
posibles. Para obtener dicha lista se debe proporcionar explícitamente:
Las clases de casos también pueden extender un rasgo sealed . Por lo tanto, los objetos y las
clases de casos se pueden mezclar para crear jerarquías complejas:
https://riptutorial.com/es/home 57
sealed trait CelestialBody
object CelestialBody {
case object Earth extends CelestialBody
case object Sun extends CelestialBody
case object Moon extends CelestialBody
case class Asteroid(name: String) extends CelestialBody
}
object WeekDay {
case object Mon extends WeekDay { val name = "Monday" }
case object Tue extends WeekDay { val name = "Tuesday" }
(...)
}
O solo:
object WeekDay {
object Mon extends WeekDay("Monday")
object Tue extends WeekDay("Tuesday")
(...)
}
Esta es solo una extensión de la variante de rasgo sellado donde una macro genera un conjunto
con todas las instancias en tiempo de compilación. Esto omite el inconveniente de que un
desarrollador puede agregar un valor a la enumeración, pero se olvida de agregarlo al conjunto
allElements.
import EnumerationMacros._
https://riptutorial.com/es/home 58
Para que esto funcione necesitas esta macro:
import scala.collection.immutable.TreeSet
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
/**
A macro to produce a TreeSet of all instances of a sealed trait.
Based on Travis Brown's work:
http://stackoverflow.com/questions/13671734/iteration-over-a-sealed-trait-in-scala
CAREFUL: !!! MUST be used at END OF code block containing the instances !!!
*/
object EnumerationMacros {
def sealedInstancesOf[A]: TreeSet[A] = macro sealedInstancesOf_impl[A]
if (!symbol.isClass || !symbol.isSealed)
c.abort(c.enclosingPosition, "Can only enumerate values of a sealed trait or class.")
else {
Apply(
Select(
reify(TreeSet).tree,
TermName("apply")
),
children.map(sourceModuleRef(_))
)
}
}
}
}
https://riptutorial.com/es/home 59
Capítulo 17: Expresiones regulares
Sintaxis
• re.findAllIn (s: CharSequence): MatchIterator
• re.findAllMatchIn (s: CharSequence): Iterator [Match]
• re.findFirstIn (s: CharSequence): Option [String]
• re.findFirstMatchIn (s: CharSequence): Option [Match]
• re.findPrefixMatchIn (s: CharSequence): Option [Match]
• re.findPrefixOf (s: CharSequence): Option [String]
• re.replaceAllIn (s: CharSequence, reemplazador: Match => String): String
• re.replaceAllIn (s: CharSequence, reemplazo: String): String
• re.replaceFirstIn (s: CharSequence, reemplazo: String): String
• re.replaceSomeIn (s: CharSequence, reemplazador: Match => Option [String]): String
• re.split (s: CharSequence): Array [String]
Examples
Declarar expresiones regulares
scala.util.matching.Regex implementa una API de expresión regular idiomática para Scala como
un contenedor sobre java.util.regex.Pattern , y la sintaxis compatible es la misma. Dicho esto, el
soporte de Scala para los literales de cadenas de varias líneas hace que la marca x mucho más
útil, permitiendo comentarios e ignorando espacios en blanco de patrones:
Hay una versión sobrecargada de r , def r(names: String*): Regex que le permite asignar nombres
de grupo a sus capturas de patrones. Esto es algo frágil ya que los nombres están disociados de
las capturas, y solo deben usarse si la expresión regular se usará en múltiples ubicaciones:
https://riptutorial.com/es/home 60
val m = matched.group("m").toInt
val d = matched.group("d").toInt
java.time.LocalDate.of(y, m, d)
case None => ???
}
val re = """\((.*?)\)""".r
val str =
"(The)(example)(of)(repeating)(pattern)(in)(a)(single)(string)(I)(had)(some)(trouble)(with)(once)"
re.findAllMatchIn(str).map(_.group(1)).toList
res2: List[String] = List(The, example, of, repeating, pattern, in, a, single, string, I, had,
some, trouble, with, once)
https://riptutorial.com/es/home 61
Capítulo 18: Extractores
Sintaxis
• extractor de valores (extractValue1, _ / * segundo valor extraído ignorado * /) =
valueToBeExtracted
• valueToBeExtracted match {case extractor (extractValue1, _) => ???}
• val (tuple1, tuple2, tuple3) = tupleWith3Elements
• objeto Foo {def unapply (foo: Foo): Opción [String] = Some (foo.x); }
Examples
Extractores de tuplas
x e y se extraen de la tupla:
Tenga en cuenta que las tuplas tienen una longitud máxima de 22 y, por lo tanto, de ._1 a ._22
funcionarán (suponiendo que la tupla tenga al menos ese tamaño).
Los extractores de tuplas se pueden usar para proporcionar argumentos simbólicos para
funciones literales:
assert {
names ==
(persons map { name =>
s"${name._2}, ${name._1}"
})
}
assert {
https://riptutorial.com/es/home 62
names ==
(persons map { case (given, surname) =>
s"$surname, $given"
})
}
Una clase de caso es una clase con una gran cantidad de código estándar incluido
automáticamente. Una ventaja de esto es que Scala facilita el uso de extractores con clases de
casos.
case class Person(name: String, age: Int) // Define the case class
val p = Person("Paola", 42) // Instantiate a value with the case class type
En esta coyuntura, tanto n como a son val en el programa y se puede acceder a ellos como tales:
se dice que se han "extraído" de la pág. Continuo:
Aquí, tenemos un código que utiliza el extractor para verificar explícitamente que la person es un
objeto Person y que saca de inmediato las variables que nos interesan: n y a .
Una extracción personalizado puede ser escrito por la aplicación de la unapply método y devolver
un valor de tipo Option :
https://riptutorial.com/es/home 63
class Foo(val x: String)
object Foo {
def unapply(foo: Foo): Option[String] = Some(foo.x)
}
El tipo de unapply de unapply puede ser diferente a la Option , siempre que el tipo devuelto
proporcione los métodos get e isEmpty . En este ejemplo, la Bar se define con esos métodos y la
unapply devuelve una instancia de la Bar :
object Bar {
def unapply(bar: Bar): Bar = bar
}
El tipo de unapply de unapply también puede ser un Boolean , que es un caso especial que no
cumple con los requisitos get y isEmpty anteriores. Sin embargo, tenga en cuenta en este ejemplo
que DivisibleByTwo es un objeto, no una clase, y no toma un parámetro (y, por lo tanto, ese
parámetro no puede ser enlazado):
object DivisibleByTwo {
def unapply(num: Int): Boolean = num % 2 == 0
}
4 match {
case DivisibleByTwo() => "yes"
case _ => "no"
}
// yes
3 match {
case DivisibleByTwo() => "yes"
case _ => "no"
}
// no
https://riptutorial.com/es/home 64
Si una clase de caso tiene exactamente dos valores, su extractor puede usarse en notación de
infijo.
object Foo {
def unapply(s: String): Option[(Int, Int)] = Some((s.length, 5))
}
val a Foo b = "hello world!"
//a: Int = 12
//b: Int = 5
Extractores Regex
Una expresión regular con partes agrupadas se puede utilizar como un extractor:
Extractores transformadores
El comportamiento del extractor se puede usar para derivar valores arbitrarios de su entrada. Esto
puede ser útil en situaciones en las que desea poder actuar sobre los resultados de una
transformación en caso de que la transformación sea exitosa.
Considere como ejemplo los diversos formatos de nombre de usuario que se pueden usar en un
entorno Windows :
object UserPrincipalName {
def unapply(str: String): Option[(String, String)] = str.split('@') match {
case Array(u, d) if u.length > 0 && d.length > 0 => Some((u, d))
case _ => None
}
}
object DownLevelLogonName {
def unapply(str: String): Option[(String, String)] = str.split('\\') match {
https://riptutorial.com/es/home 65
case Array(d, u) if u.length > 0 && d.length > 0 => Some((d, u))
case _ => None
}
}
De hecho, es posible crear un extractor que muestre ambos comportamientos ampliando los tipos
que puede igualar:
object UserPrincipalName {
def unapply(obj: Any): Option[(String, String)] = obj match {
case upn: UserPrincipalName => Some((upn.username, upn.domain))
case str: String => str.split('@') match {
case Array(u, d) if u.length > 0 && d.length > 0 => Some((u, d))
case _ => None
}
case _ => None
}
}
En general, los extractores son simplemente una reformulación conveniente del patrón de Option ,
como se aplica a métodos con nombres como tryParse :
UserPrincipalName.unapply("user@domain") match {
case Some((u, d)) => ???
case None => ???
}
https://riptutorial.com/es/home 66
Capítulo 19: Función de orden superior
Observaciones
Scala hace todo lo posible para tratar los métodos y las funciones como sintácticamente idénticos.
Pero bajo el capó, son conceptos distintos.
Una función es una instancia de objeto real de tipo Function1 (o un tipo similar de otra aridad). Su
código está contenido en su método de apply . Efectivamente, simplemente actúa como un valor
que puede transmitirse.
Por cierto, la capacidad de tratar las funciones como valores es exactamente lo que se entiende
por un lenguaje que tiene soporte para funciones de orden superior. Las instancias de funciones
son el enfoque de Scala para implementar esta característica.
Una función de orden superior real es una función que toma un valor de función como argumento
o devuelve un valor de función. Pero en Scala, como todas las operaciones son métodos, es más
general pensar en métodos que reciben o devuelven parámetros de función. Entonces, el map , tal
como se define en Seq podría considerarse como una "función de orden superior" debido a que su
parámetro es una función, pero no es literalmente una función; es un metodo
Examples
Usando métodos como valores de función
El compilador Scala convertirá automáticamente los métodos en valores de función con el fin de
pasarlos a funciones de orden superior.
object MyObject {
def mapMethod(input: Int): String = {
int.toString
}
}
En el ejemplo anterior, MyObject.mapMethod no es una llamada de función, sino que se pasa a map
como un valor. De hecho, el map requiere que se le pase un valor de función, como se puede ver
en su firma. La firma para el map de una List[A] (una lista de objetos de tipo A ) es:
La parte f: (A) => B indica que el parámetro para esta llamada de método es alguna función que
toma un objeto de tipo A y devuelve un objeto de tipo B A y B se definen arbitrariamente. Volviendo
al primer ejemplo, podemos ver que mapMethod toma un Int (que corresponde a A ) y devuelve una
https://riptutorial.com/es/home 67
String(que corresponde a B ). Por mapMethod tanto, mapMethod es un valor de función válido para
pasar al map . Podríamos reescribir el mismo código así:
Esto incluye el valor de la función, que puede agregar claridad a las funciones simples.
Una función de orden superior, a diferencia de una función de primer orden, puede tener una de
tres formas:
• Ambos de los anteriores: uno o más de sus parámetros es una función, y devuelve una
función.
object HOF {
def main(args: Array[String]) {
val list =
List(("Srini","E"),("Subash","R"),("Ranjith","RK"),("Vicky","s"),("Sudhar","s"))
//HOF
val fullNameList= list.map(n => getFullName(n._1, n._2))
Aquí la función de mapa toma una función getFullName(n._1,n._2) como parámetro. Esto se llama
HOF (función de orden superior).
Scala admite la evaluación perezosa para argumentos de funciones usando notación: def
func(arg: => String) . Dicho argumento de función podría tomar un objeto String normal o una
función de orden superior con String tipo de retorno String . En el segundo caso, el argumento de
la función sería evaluado en el acceso al valor.
https://riptutorial.com/es/home 68
case true => Some(data)
case false => None
}
}
La evaluación perezosa puede ser extremadamente útil cuando desea optimizar una sobrecarga
de cálculo de argumentos caros.
https://riptutorial.com/es/home 69
Capítulo 20: Funciones
Observaciones
Scala tiene funciones de primera clase.
• Las funciones se compilan en una clase que extiende un rasgo (como Function1 ) en tiempo
de compilación, y se crean instancias a un valor en tiempo de ejecución. Los métodos, por
otro lado, son miembros de su clase, rasgo u objeto, y no existen fuera de eso.
• Un método se puede convertir en una función, pero una función no se puede convertir en un
método.
• Los métodos pueden tener parametrización de tipo, mientras que las funciones no.
• Los métodos pueden tener valores predeterminados de parámetros, mientras que las
funciones no pueden.
Examples
Funciones anónimas
Las funciones anónimas son funciones que están definidas pero no se les asigna un nombre.
La siguiente es una función anónima que toma dos enteros y devuelve la suma.
Las funciones anónimas se utilizan principalmente como argumentos para otras funciones. Por
ejemplo, la función de map en una colección espera otra función como argumento:
Los tipos de los argumentos de la función anónima se pueden omitir: los tipos se deducen
automáticamente :
https://riptutorial.com/es/home 70
Seq("Foo", "Bar", "Qux").map((x) => x.toUpperCase)
Si hay un solo argumento, se pueden omitir los paréntesis alrededor de ese argumento:
Subraya la taquigrafía
Hay una sintaxis aún más corta que no requiere nombres para los argumentos. El fragmento
anterior se puede escribir:
_ representa los argumentos de la función anónima en posición. Con una función anónima que
tiene múltiples parámetros, cada aparición de _ se referirá a un argumento diferente. Por ejemplo,
las dos expresiones siguientes son equivalentes:
Al usar esta abreviatura, cualquier argumento representado por la posición _ solo puede ser
referenciado una sola vez y en el mismo orden.
Composición
La composición de funciones permite que dos funciones funcionen y se vean como una sola
función. Expresado en términos matemáticos, dada una función f(x) y una función g(x) , la
función h(x) = f(g(x)) .
Cuando se compila una función, se compila a un tipo relacionado con Function1 . Scala
proporciona dos métodos en la Function1 aplicación relacionada a la composición: andThen y
compose . El método de compose encaja con la definición matemática anterior así:
https://riptutorial.com/es/home 71
El andThen (piensa que h(x) = g(f(x)) ) tiene una sensación más parecida a DSL:
Se asigna una nueva función anónima que se cierra sobre f y g . Esta función está vinculada a la
nueva función h en ambos casos.
Si cualquiera de f o g funciona a través de un efecto secundario, llamar a h hará que todos los
efectos secundarios de f y g ocurran en el pedido. Lo mismo ocurre con cualquier cambio de
estado mutable.
Para definir una función parcial (que también es una función), use la siguiente sintaxis:
https://riptutorial.com/es/home 72
Capítulo 21: Funciones definidas por el
usuario para Hive
Examples
Un simple UDF Hive dentro de Apache Spark
import org.apache.spark.sql.functions._
// Create a function that uses the content of the column inside the dataframe
val code = (param: String) => if (param == "myCode") 1 else 0
// With that function, create the udf function
val myUDF = udf(code)
// Apply the udf to a column inside the existing dataframe, creating a dataframe with the
additional new column
val newDataframe = aDataframe.withColumn("new_column_name", myUDF(col(inputColumn)))
https://riptutorial.com/es/home 73
Capítulo 22: Funciones parciales
Examples
Composición
Las funciones parciales se usan a menudo para definir una función total en partes:
En este uso, las funciones parciales se intentan en orden de concatenación con el método orElse .
Normalmente, se proporciona una función parcial final que coincide con todos los casos restantes.
En conjunto, la combinación de estas funciones actúa como una función total.
Este patrón se usa normalmente para separar las preocupaciones cuando una función puede
actuar efectivamente como un despachador para rutas de código dispares. Esto es común, por
ejemplo, en el método de recepción de un Actor Akka .
Si bien la función parcial se usa a menudo como una sintaxis conveniente para funciones totales,
al incluir una coincidencia de comodín final ( case _ ), en algunos métodos, su parcialidad es
clave. Un ejemplo muy común en Scala idiomático es el método de collect , definido en la
biblioteca de colecciones de Scala. Aquí, las funciones parciales permiten que las funciones
comunes de examinar los elementos de una colección para mapearlos y / o filtrarlos ocurran en
una sintaxis compacta.
Ejemplo 1
Suponiendo que tenemos una función de raíz cuadrada definida como función parcial:
https://riptutorial.com/es/home 74
Podemos invocarlo con el combinador de collect :
List(-1.1,2.2,3.3,0).collect(sqRoot)
List(-1.1,2.2,3.3,0).filter(sqRoot.isDefinedAt).map(sqRoot)
Ejemplo 2
sealed trait SuperType // `sealed` modifier allows inheritance within current build-unit only
case class A(value: Int) extends SuperType
case class B(text: String) extends SuperType
case object C extends SuperType
input.collect {
case A(value) if value < 10 => value.toString
case B(text) if text.nonEmpty => text
} // Seq("5", "hello")
Sintaxis basica
Scala tiene un tipo especial de función llamada función parcial , que se extiende a las funciones
normales , lo que significa que una instancia de PartialFunction se puede usar donde se espera
que Function1 . Las funciones parciales se pueden definir de forma anónima utilizando la sintaxis
de case también se usa en la coincidencia de patrones :
https://riptutorial.com/es/home 75
Uso como una función total
Las funciones parciales son muy comunes en Scala idiomático. A menudo se usan por su
conveniente sintaxis basada en case para definir funciones totales sobre rasgos :
sealed trait SuperType // `sealed` modifier allows inheritance within current build-unit only
case object A extends SuperType
case object B extends SuperType
case object C extends SuperType
input.map {
case A => 5
case _ => 10
} // Seq(5, 10, 10)
Esto guarda la sintaxis adicional de una declaración de match en una función anónima regular.
Comparar:
También se utiliza con frecuencia para realizar una descomposición de parámetros utilizando la
coincidencia de patrones, cuando se pasa una tupla o una clase de caso a una función:
Estas tres funciones de mapa son equivalentes, por lo tanto, use la variación que su equipo
encuentre más legible.
// 1. No extraction
numberNames.map(it => s"${it._1} is written ${it._2}" )
https://riptutorial.com/es/home 76
La función parcial debe coincidir con todas las entradas : cualquier caso que no coincida
producirá una excepción en el tiempo de ejecución.
https://riptutorial.com/es/home 77
Capítulo 23: Futuros
Examples
Creando un futuro
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
object FutureDivider {
def divide(a: Int, b: Int): Future[Int] = Future {
// Note that this is integer division.
a / b
}
}
Simplemente, el método de divide crea un Futuro que se resolverá con el cociente de a sobre b .
La forma más fácil de consumir un futuro exitoso, o más bien, obtener el valor dentro del futuro,
es usar el método de map . Supongamos que un cierto código llama a la divide método de la
FutureDivider objeto del ejemplo "Creando un futuro". ¿Cómo debería ser el código para obtener
el cociente de a sobre b ?
object Calculator {
def calculateAndReport(a: Int, b: Int) = {
val eventualQuotient = FutureDivider divide(a, b)
eventualQuotient map {
quotient => println(quotient)
}
}
}
A veces, el cálculo en un futuro puede crear una excepción, lo que hará que el futuro falle. En el
ejemplo de "Creación de un futuro", ¿qué sucede si el código de llamada pasó 55 y 0 al método de
divide ? Lanzaría una ArithmeticException después de tratar de dividir por cero, por supuesto.
¿Cómo se manejaría eso en consumir código? En realidad, hay un puñado de maneras de lidiar
con los fracasos.
object Calculator {
def calculateAndReport(a: Int, b: Int) = {
val eventualQuotient = FutureDivider divide(a, b)
https://riptutorial.com/es/home 78
eventualQuotient recover {
case ex: ArithmeticException => println(s"It failed with: ${ex.getMessage}")
}
}
}
Maneje la excepción con la proyección failed , donde la excepción se convierte en el valor del
futuro:
object Calculator {
def calculateAndReport(a: Int, b: Int) = {
val eventualQuotient = FutureDivider divide(a, b)
// Note the use of the dot operator to get the failed projection and map it.
eventualQuotient.failed.map {
ex => println(s"It failed with: ${ex.getMessage}")
}
}
}
object Calculator {
def calculateAndReport(a: Int, b: Int) = {
val eventualQuotient = FutureDivider divide(a, b)
eventualQuotient map {
quotient => println(s"Quotient: $quotient")
} recover {
case ex: ArithmeticException => println(s"It failed with: ${ex.getMessage}")
}
}
}
En algunos casos es necesario calcular una cantidad variable de valores en futuros separados.
Suponga que tiene una List[Future[Int]] , pero en su lugar debe procesarse una List[Int] .
Entonces la pregunta es cómo convertir esta instancia de la List[Future[Int]] en un
Future[List[Int]] . Para este propósito existe el método de sequence en el objeto compañero
Future .
https://riptutorial.com/es/home 79
Hay un operador alternativo llamado traverse , que funciona de manera similar pero toma una
función como un argumento adicional. Con la función de identidad x => x como parámetro, se
comporta como el operador de sequence .
Sin embargo, el argumento adicional permite modificar cada instancia futura dentro del
listOfFuture dado. Además, el primer argumento no necesita ser una lista de Future . Por lo tanto
es posible transformar el ejemplo de la siguiente manera:
La comprensión es una forma compacta de ejecutar un bloque de código que depende del
resultado exitoso de múltiples futuros.
Con f1, f2, f3 tres Future[String] que contendrán las cadenas one, two, three respectivamente,
val fCombined =
for {
s1 <- f1
s2 <- f2
s3 <- f3
} yield (s"$s1 - $s2 - $s3")
fCombined será un Future[String] contiene la cadena one - two - three una vez que todos los
futuros se hayan completado con éxito.
Además, tenga en cuenta que para la comprensión es solo un azúcar sintáctico para un método
flatMap, por lo que la construcción de objetos Futuros dentro del cuerpo eliminaría la ejecución
concurrente de bloques de código incluidos en futuros y llevaría a un código secuencial. Lo ves en
el ejemplo:
https://riptutorial.com/es/home 80
}
} yield first - second
Valor encerrada por result1 objeto sería siempre negativo, mientras que result2 serían positivos.
Para obtener más detalles sobre la comprensión y el yield en general, consulte http://docs.scala-
lang.org/tutorials/FAQ/yield.html
https://riptutorial.com/es/home 81
Capítulo 24: Implícitos
Sintaxis
• val implícito x: T = ???
Observaciones
Las clases implícitas permiten que se agreguen métodos personalizados a los tipos existentes,
sin tener que modificar su código, enriqueciendo así los tipos sin necesidad de controlar el código.
El uso de tipos implícitos para enriquecer una clase existente a menudo se denomina patrón de
"enriquecer mi biblioteca".
1. Las clases implícitas solo pueden existir dentro de otra clase, objeto o rasgo.
2. Las clases implícitas solo pueden tener un parámetro de constructor primario no implícito.
3. Puede que no haya otra definición de objeto, clase, rasgo o miembro de clase dentro del
mismo ámbito que tenga el mismo nombre que la clase implícita.
Examples
Conversión implícita
La conversión es unidireccional: en este caso, no puede convertir 42 nuevo a Foo(42) . Para ello,
se debe definir una segunda conversión implícita:
Tenga en cuenta que este es el mecanismo por el cual se puede agregar un valor flotante a un
valor entero, por ejemplo.
https://riptutorial.com/es/home 82
Las conversiones implícitas se deben usar con moderación porque ocultan lo que está
sucediendo. Es una práctica recomendada utilizar una conversión explícita a través de
una llamada de método, a menos que haya una ganancia de legibilidad tangible al
usar una conversión implícita.
Parámetros implícitos
Los parámetros implícitos pueden ser útiles si un parámetro de un tipo debe definirse una vez en
el alcance y luego aplicarse a todas las funciones que usan un valor de ese tipo.
// a normal method:
def doLongRunningTask(timeout: FiniteDuration): Long = timeout.toMillis
// to call it
doLongRunningTask(timeout) // 1000
Ahora digamos que tenemos algunos métodos que tienen una duración de tiempo de espera, y
queremos llamar a todos esos métodos utilizando el mismo tiempo de espera. Podemos definir el
tiempo de espera como una variable implícita.
// we can now call the functions without passing the timeout parameter
doLongRunningTaskA() // 1000
doLongRunningTaskB() // 1000
La forma en que funciona es que el compilador de scalac busca un valor en el ámbito que está
marcado como implícito y cuyo tipo coincide con el del parámetro implícito. Si encuentra uno,
lo aplicará como parámetro implícito.
Tenga en cuenta que esto no funcionará si define dos o incluso más implicaciones del
https://riptutorial.com/es/home 83
mismo tipo en el ámbito.
//Does not work because no implicit value is present for type `M[Int]`
//usage[Int] //Select the proper implicit value for type M[Int]!
implicit val first: M[Int] = M(1)
usage[Int] //Works when `second` is not in scope
implicit val second: M[Int] = M(2)
//Does not work because more than one implicit values are present for the type `M[Int]`
//usage[Int] //Select the proper implicit value for type M[Int]!
Un tiempo de espera es un caso de uso habitual para esto, o por ejemplo, en Akka, el sistema
Actor es (la mayoría de las veces) siempre el mismo, por lo que generalmente se pasa
implícitamente. Otro caso de uso sería diseño de la biblioteca, más comúnmente con las
bibliotecas de PF que se basan en clases de tipos (como scalaz , gatos o éxtasis ).
Generalmente se considera una mala práctica usar parámetros implícitos con tipos
básicos como Int , Long , String , etc., ya que creará confusión y hará que el código
sea menos legible.
Clases Implícitas
Las clases implícitas permiten agregar nuevos métodos a las clases previamente definidas.
La clase String no tiene método sin withoutVowels . Esto se puede agregar así:
object StringUtil {
implicit class StringEnhancer(str: String) {
def withoutVowels: String = str.replaceAll("[aeiou]", "")
}
}
La clase implícita tiene un único parámetro de constructor ( str ) con el tipo que le gustaría
extender ( String ) y contiene el método que le gustaría "agregar" al tipo ( withoutVowels ). Los
métodos recién definidos ahora se pueden usar directamente en el tipo mejorado (cuando el tipo
mejorado está en el alcance implícito):
Bajo el capó, las clases implícitas definen una conversión implícita del tipo mejorado a la clase
implícita, como esto:
https://riptutorial.com/es/home 84
Las clases implícitas a menudo se definen como clases de valor para evitar la creación de objetos
de tiempo de ejecución y, por lo tanto, eliminar la sobrecarga del tiempo de ejecución:
Con la definición mejorada anterior, no es necesario crear una nueva instancia de StringEnhancer
cada vez que se invoca el método withoutVowels .
Ahora, asumiendo que una de las instancias implícitas no está disponible ( SomeCtx1 ) mientras
que todas las demás instancias implícitas necesarias están dentro del alcance, para crear una
instancia de la clase, se debe proporcionar una instancia de SomeCtx1 .
Esto se puede hacer al mismo tiempo que se conserva una instancia implícita dentro del alcance
utilizando la palabra clave implicitly :
Implicados en el REPL
Para ver todas las implicits en el alcance durante una sesión REPL:
scala> :implicits
scala> :implicits -v
Si uno tiene una expresión y desea ver el efecto de todas las reglas de reescritura que se le
aplican (incluidas las implícitas):
(Ejemplo:
https://riptutorial.com/es/home 85
)
https://riptutorial.com/es/home 86
Capítulo 25: Inferencia de tipos
Examples
Inferencia de tipo local
Scala tiene un poderoso mecanismo de inferencia de tipos integrado al lenguaje. Este mecanismo
se denomina 'Inferencia de tipo local':
val i: Int = 1 + 2
val s: String = "I am a String"
def squared(x : Int): Int = x*x
El compilador Scala también puede deducir parámetros de tipo cuando se llaman métodos
polimórficos, o cuando se crean instancias de clases genéricas:
Limitaciones a la inferencia
Hay escenarios en los que la inferencia de tipos de Scala no funciona. Por ejemplo, el compilador
no puede inferir el tipo de parámetros del método:
https://riptutorial.com/es/home 87
// Does not compile
def factorial(n: Int) = if (n == 0 || n == 1) 1 else n * factorial(n - 1)
// Compiles
def factorial(n: Int): Int = if (n == 0 || n == 1) 1 else n * factorial(n - 1)
Cuando intenta llamarlo sin especificar el parámetro genérico, Nothing se deduce Nothing , lo que
no es muy útil para una implementación real (y su resultado no es útil). Con la siguiente solución,
el NotNothing contexto NotNothing puede evitar el uso del método sin especificar el tipo esperado
(en este ejemplo, RuntimeClass también se excluye como para ClassTags no Nothing , pero
RuntimeClass se infiere):
object NotNothing {
implicit object notNothing extends NotNothing[Any]
//We do not want Nothing to be inferred, so make an ambigous implicit
implicit object `\n The error is because the type parameter was resolved to Nothing` extends
NotNothing[Nothing]
//For classtags, RuntimeClass can also be inferred, so making that ambigous too
implicit object `\n The error is because the type parameter was resolved to RuntimeClass`
extends NotNothing[RuntimeClass]
}
object ObjectStore {
//Using context bounds
def get[T: NotNothing]: Option[T] = {
???
}
Ejemplo de uso:
object X {
//Fails to compile
//val nothingInferred = ObjectStore.get
//Fails to compile
//val runtimeClassInferred = ObjectStore.newArray()
}
https://riptutorial.com/es/home 88
Lea Inferencia de tipos en línea: https://riptutorial.com/es/scala/topic/4918/inferencia-de-tipos
https://riptutorial.com/es/home 89
Capítulo 26: Interoperabilidad de Java
Examples
Conversión de colecciones Scala a colecciones Java y viceversa
import scala.collection.JavaConverters._
Si el código Java devuelve una colección Java, puede convertirlo en una colección Scala de una
manera similar:
import scala.collection.JavaConverters._
Tenga en cuenta que estos son decoradores, por lo que simplemente envuelven las colecciones
subyacentes en una interfaz de colección de Scala o Java. Por lo tanto, las llamadas .asJava y
.asScala no copian las colecciones.
Arrays
Las matrices son matrices JVM regulares con un giro que se tratan como invariantes y tienen
constructores especiales y conversiones implícitas. Constrúyelos sin la new palabra clave.
val a = Array("element")
Puede usar un Array como otras colecciones, gracias a una conversión implícita a TraversableLike
ArrayOps :
La mayoría de las colecciones de Scala ( TraversableOnce ) tienen un método toArray que toma un
https://riptutorial.com/es/home 90
ClassTag implícito para construir la matriz de resultados:
List(0).toArray
//> res1: Array[Int] = Array(0)
Esto facilita el uso de TraversableOnce en su código de Scala y luego lo pasa al código Java que
espera una matriz.
Scala ofrece conversiones implícitas entre todos los principales tipos de colección en el objeto
JavaConverters.
Iterador java.util.Iterador
Iterador java.util.Enumeracion
Iterador java.util.Iterable
Iterador java.util.Coleccion
mutable.Buffer java.util.List
mutable.Set java.util.Set
mutable.Map java.util.Map
mutable.ConcurrentMap java.util.concurrent.ConcurrentMap
Algunas otras colecciones de Scala también se pueden convertir a Java, pero no tienen una
conversión al tipo de Scala original:
Seq java.util.List
mutable.Seq java.util.List
Conjunto java.util.Set
Mapa java.util.Map
Referencia :
https://riptutorial.com/es/home 91
Interfaces funcionales para funciones de Scala - scala-java8-compat
import java.util.function._
import scala.compat.java8.FunctionConverters._
import scala.compat.java8.OptionConverters._
class Test {
val o = Option(2.7)
val oj = o.asJava // Optional[Double]
val ojd = o.asPrimitive // OptionalDouble
val ojds = ojd.asScala // Option(2.7) again
}
import java.util.stream.IntStream
import scala.compat.java8.StreamConverters._
import scala.compat.java8.collectionImpl.{Accumulator, LongAccumulator}
https://riptutorial.com/es/home 92
Lea Interoperabilidad de Java en línea:
https://riptutorial.com/es/scala/topic/2441/interoperabilidad-de-java
https://riptutorial.com/es/home 93
Capítulo 27: Interpolación de cuerdas
Observaciones
Esta característica existe en Scala 2.10.0 y superior.
Examples
Hola Interpolación De Cuerdas
println(f"$num%2.2f")
42.00
println(f"$num%e")
4.200000e+01
println(f"$num%a")
0x1.5p5
https://riptutorial.com/es/home 94
s"${a}" // "A"
s"${f(a)}" // "AA"
Sin las llaves, Scala solo interpolaría el identificador después de $ (en este caso f ). Dado que no
hay una conversión implícita de f en una String esta es una excepción en este ejemplo:
my"foo${bar}baz"
Tenga en cuenta que no hay restricción en los argumentos o el tipo de retorno de la función de
interpolación. Esto nos lleva por un camino oscuro donde la sintaxis de interpolación se puede
usar creativamente para construir objetos arbitrarios, como se ilustra en el siguiente ejemplo:
https://riptutorial.com/es/home 95
let"a=${4}" // Let(a, 4)
let"b=${"foo"}" // error: type mismatch
let"c=" // error: not enough arguments for method let: (value: Int)Let
También es posible utilizar la función de interpolación de cadenas de Scala para crear extractores
elaborados (emparejadores de patrones), como quizás el más famoso empleado en la API de
cuasiquotes de las macros de Scala.
Dado que n"p0${i0}p1" desaparece del new StringContext("p0", "p1").n(i0) , quizás no sea
sorprendente que la funcionalidad del extractor esté habilitada al proporcionar una conversión
implícita de StringContext a una clase con propiedad n de un tipo que define un método unapply o
unapplySeq .
Como ejemplo, considere el siguiente extractor que extrae segmentos de ruta al construir una
expresión regular a partir de las partes StringContext . Luego podemos delegar la mayor parte del
trabajo pesado al método unapplySeq proporcionado por el resultado scala.util.matching.Regex :
"/documentation/scala/1629/string-interpolation" match {
case path"/documentation/${topic}/${id}/${_}" => println(s"$topic, $id")
case _ => ???
}
Tenga en cuenta que el objeto de path también podría definir un método de apply para
comportarse como un interpolador regular también.
Puede utilizar el interpolador en bruto si desea que una cadena se imprima tal como está y sin
ningún escape de literales.
Con el uso del interpolador en bruto , debería ver lo siguiente impreso en la consola:
https://riptutorial.com/es/home 96
println("Hello World In English And French\nEnglish:\tHello World\nFrench:\t\tBonjour Le
Monde")
Huellas dactilares:
https://riptutorial.com/es/home 97
Capítulo 28: Invocación Dinámica
Introducción
Scala le permite usar la invocación dinámica al llamar a métodos o acceder a campos en un
objeto. En lugar de tener esto integrado en el lenguaje, esto se logra a través de reglas de
reescritura similares a las de las conversiones implícitas, habilitadas por el rasgo del marcador [
scala.Dynamic ] [Dynamic scaladoc]. Esto le permite emular la capacidad de agregar propiedades
dinámicamente a objetos presentes en idiomas dinámicos, y más. [Scaladoc dinámico]:
http://www.scala-lang.org/api/2.12.x/scala/Dynamic.html
Sintaxis
• clase Foo extiende Dynamic
• foo.field
• foo.field = valor
• foo.method (args)
• foo.method (namedArg = x, y)
Observaciones
Para declarar subtipos de Dynamic , la dynamics característica del lenguaje debe estar habilitada, ya
sea importando scala.language.dynamics o mediante la opción del compilador -language:dynamics .
Los usuarios de esta Dynamic que no definen sus propios subtipos no necesitan habilitar esto.
Examples
Accesos de campo
Esta:
https://riptutorial.com/es/home 98
foo.field = 10 // Becomes foo.updateDynamic("field")(10)
foo.field = "10" // Does not compile; "10" is not an Int.
foo.x() // Does not compile; Foo does not define applyDynamic, which is used for methods.
foo.x.apply() // DOES compile, as Nothing is a subtype of () => Any
// Remember, the compiler is still doing static type checks, it just has one more way to
// "recover" and rewrite otherwise invalid code now.
Método de llamadas
Esta:
Un poco contrario a la intuición (pero también la única forma sensata de hacerlo funcionar), esto:
es equivalente a:
dyn.selectDynamic("x").update(y, z)
mientras
dyn.x(y)
es todavía
dyn.applyDynamic("x")(y)
Es importante ser consciente de esto, o de lo contrario puede pasar inadvertido y causar errores
https://riptutorial.com/es/home 99
extraños.
https://riptutorial.com/es/home 100
Capítulo 29: Inyección de dependencia
Examples
Patrón de pastel con clase de implementación interna.
class TimeUtilImpl{
def now() = new DateTime()
}
}
class MainControllerImpl {
def printCurrentTime() = println(timeUtil.now()) //timeUtil is injected from TimeUtil
trait
}
}
app.mainController.printCurrentTime()
}
Utilizo la clase interna (por ejemplo, TimeUtilImpl ) en cada componente porque, en mi opinión, es
más fácil de probar ya que podemos burlarnos de la clase interna. Y también es más fácil para
rastrear desde donde se llama el método cuando el proyecto se vuelve más complejo.
Por último, conecto todos los componentes juntos. Si está familiarizado con Guice, esto es
equivalente a Binding
https://riptutorial.com/es/home 101
Capítulo 30: JSON
Examples
JSON con spray-json
spray-json proporciona una manera fácil de trabajar con JSON. Usando formatos implícitos, todo
sucede "detrás de escena":
Tenga en cuenta que el último parámetro, el número de versión ( 1.3.2 ), puede ser diferente en
diferentes proyectos.
Importar la biblioteca
import spray.json._
import DefaultJsonProtocol._
El protocolo JSON predeterminado DefaultJsonProtocol contiene formatos para todos los tipos
básicos. Para proporcionar la funcionalidad JSON para tipos personalizados, use los
constructores de conveniencia para los formatos o los formatos de escritura explícitamente.
Leer json
// generates an intermediate JSON representation (abstract syntax tree)
val res = """{ "foo": "bar" }""".parseJson // JsValue = {"foo":"bar"}
Escribir json
val values = List("a", "b", "c")
https://riptutorial.com/es/home 102
values.toJson.prettyPrint // ["a", "b", "c"]
DSL
DSL no es compatible.
// serialize a Person
Person("Fred", Address("Awesome Street 9", "SuperCity"))
val fredJsonString = fred.toJson.prettyPrint
{
"name": "Fred",
"address": {
"street": "Awesome Street 9",
"city": "SuperCity"
}
}
Formato personalizado
Escriba un JsonFormat personalizado si se JsonFormat una serialización especial de un tipo. Por
ejemplo, si los nombres de campo son diferentes en Scala que en JSON. O, si diferentes tipos de
concreto son instanciados basados en la entrada.
https://riptutorial.com/es/home 103
address = fields("home").convertTo[Address]
)
}
// serialization code
override def write(person: Person): JsValue = JsObject(
"name" -> person.name.toJson,
"home" -> person.address.toJson
)
}
Circe proporciona códecs derivados de tiempo de compilación para en / decode json en clases de
casos. Un ejemplo simple se ve así:
import io.circe._
import io.circe.generic.auto._
import io.circe.parser._
import io.circe.syntax._
// {"id":1,"name":"John Doe"}
val json = user.asJson.noSpaces
import play.api.libs.json._
import play.api.libs.functional.syntax._ // if you need DSL
DefaultFormat contiene formatos predeterminados para leer / escribir todos los tipos básicos. Para
proporcionar la funcionalidad JSON para sus propios tipos, puede usar constructores de
conveniencia para formatos o escribir formatos explícitamente.
Leer json
Escribir json
https://riptutorial.com/es/home 104
val values = List("a", "b", "c")
Json.stringify(Json.toJson(values)) // ["a", "b", "c"]
DSL
Como siempre, prefiera la coincidencia de patrones con JsSuccess / JsError e intente evitar las .get
, array(i) .
// serialize a Person
val fred = Person("Fred", Address("Awesome Street 9", "SuperCity"))
val fredJsonString = Json.stringify(Json.toJson(Json.toJson(fred)))
Formato propio
Puede escribir su propio JsonFormat si necesita una serialización especial de su tipo (por
ejemplo, nombre los campos de forma diferente en scala y Json o ejemplifique diferentes tipos
concretos en función de la entrada)
https://riptutorial.com/es/home 105
Alternativa
Si el json no coincide exactamente con los campos de su clase de caso ( isAlive en la clase de
caso vs is_alive en json):
case class User(username: String, friends: Int, enemies: Int, isAlive: Boolean)
object User {
import play.api.libs.functional.syntax._
import play.api.libs.json._
case class User(username: String, friends: Int, enemies: Int, isAlive: Option[Boolean])
object User {
import play.api.libs.functional.syntax._
import play.api.libs.json._
Imagina que tienes un objeto Json, con un campo de marca de tiempo Unix:
{
"field": "example field",
"date": 1459014762000
}
solución:
https://riptutorial.com/es/home 106
Leer clases de casos personalizados
Ahora, si envuelve sus identificadores de objetos para la seguridad de tipos, disfrutará esto. Vea
el siguiente objeto json:
{
"id": 91,
"data": "Some data"
}
object JsonExampleV2 {
implicit val r: Reads[JsonExampleV2] = (
(__ \ "id").read[Long].map(MyIdentifier) and
(__ \ "data").read[String]
)(JsonExampleV2.apply _)
}
código en https://github.com/pedrorijo91/scala-play-json-examples
Dependencia de SBT:
Importaciones
import org.json4s.JsonDSL._
import org.json4s._
import org.json4s.native.JsonMethods._
DefaultFormats contiene formatos predeterminados para leer / escribir todos los tipos básicos.
Leer json
https://riptutorial.com/es/home 107
res.extract[Map[String, String]] // Map(foo -> bar)
Escribir json
DSL
trait Location
case class Street(name: String) extends Location
case class City(name: String, zipcode: String) extends Location
case class Address(street: Street, city: City) extends Location
case class Locations (locations : List[Location])
read[Locations](locationsString)
Formato propio
https://riptutorial.com/es/home 108
))
https://riptutorial.com/es/home 109
Capítulo 31: La coincidencia de patrones
Sintaxis
• selector match partialFunction
• selector de coincidencia {lista de alternativas de casos) // Esta es la forma más común de
las anteriores
Parámetros
Parámetro Detalles
Examples
Coincidencia de patrón simple
Este ejemplo muestra cómo hacer coincidir una entrada con varios valores:
f(2) // "Two"
f(3) // "Unknown!"
Demo en vivo
g(1) // "One"
g(3) // throws a MatchError
Para evitar lanzar una excepción, es una mejor práctica de programación funcional aquí manejar
el caso predeterminado ( case _ => <do something> ). Tenga en cuenta que hacer coincidir una
clase de caso puede ayudar al compilador a producir una advertencia si falta un caso. Lo mismo
ocurre con los tipos definidos por el usuario que extienden un rasgo sellado. Si la coincidencia es
https://riptutorial.com/es/home 110
total, puede que no sea necesario un caso predeterminado
También es posible hacer coincidencias con valores que no están definidos en línea. Estos deben
ser identificadores estables , que se obtienen utilizando un nombre en mayúscula o encerrando
backticks.
Con One y two definidos en otro lugar, o pasados como parámetros de función:
A diferencia de otros lenguajes de programación como Java, por ejemplo, no hay caída. Si un
bloque de caso coincide con una entrada, se ejecuta y se completa la coincidencia. Por lo tanto,
el caso menos específico debería ser el último bloque de caso.
f(5) // "Default"
f(1) // "Default"
La siguiente función de ejemplo toma un carácter y una lista de tuplas y devuelve una nueva lista
de tuplas. Si el carácter existía como el primer elemento en una de las tuplas, el segundo
elemento se incrementa. Si aún no existe en la lista, se crea una nueva tupla.
def tabulate(char: Char, tab: List[(Char, Int)]): List[(Char, Int)] = tab match {
case Nil => List((char, 1))
case (`char`, count) :: tail => (char, count + 1) :: tail
case head :: tail => head :: tabulate(char, tail)
}
https://riptutorial.com/es/home 111
case('x', count) => ...
Scala interpretará cualquier variable demarcada con una marca de verificación como un
identificador estable: también interpretará cualquier variable que comience con una letra
mayúscula de la misma manera.
Demo en vivo
Para extraer el (los) primer (s) elemento (s) y guardar el resto como una colección:
En general, cualquier forma que se pueda usar para construir una secuencia se puede usar para
hacer una comparación de patrones con una secuencia existente.
Tenga en cuenta que mientras usa Nil y :: funcionará cuando el patrón coincida con una
secuencia, la convierte a una List y puede tener resultados inesperados. Limítate a Seq( ...) y +:
para evitar esto.
Tenga en cuenta que mientras usa :: no funcionará para WrappedArray , Vector , etc., vea:
https://riptutorial.com/es/home 112
scala> def f(ints:Seq[Int]) = ints match {
| case h :: t => h
| case _ => "No match"
| }
f: (ints: Seq[Int])Any
scala> f(Array(1,2))
res0: Any = No match
Y con +:
scala> g(Array(1,2).toSeq)
res4: Any = 1
Las declaraciones de casos se pueden combinar con las expresiones if para proporcionar lógica
adicional cuando coinciden los patrones.
Es importante asegurarse de que sus guardias no creen una coincidencia no exhaustiva (el
compilador a menudo no lo detectará):
Esto lanza un MatchError en los números impares. Debe tener en cuenta todos los casos, o usar
un caso de coincidencia de comodín:
Cada clase de caso define un extractor que puede usarse para capturar a los miembros de la
https://riptutorial.com/es/home 113
clase de caso cuando coincida el patrón:
Se aplican todas las reglas normales de coincidencia de patrones: puede usar protecciones y
expresiones constantes para controlar la coincidencia:
Cuando el patrón coincida con un objeto cuyo tipo es un rasgo sellado, Scala verificará en el
momento de la compilación que todos los casos están "completamente emparejados":
https://riptutorial.com/es/home 114
case Circle(radius) => "It's a circle"
//no case for Point because it would cause a compiler warning.
}
Si posteriormente se agrega una nueva case class para Shape , todas las declaraciones de match
en Shape comenzarán a mostrar una advertencia del compilador. Esto facilita la refactorización
completa: el compilador alertará al desarrollador de todo el código que debe actualizarse.
"[email protected]" match {
case emailRegex(userName, domain, topDomain) => println(s"Hi $userName from $domain")
case _ => println(s"This is not a valid email.")
}
En este ejemplo, la expresión regular intenta coincidir con la dirección de correo electrónico
proporcionada. Si lo hace, se extrae e imprime el nombre de userName y el domain . topDomain
también se extrae, pero nada se hace con él en este ejemplo. Llamando .r en una cadena str es
equivalente a la new Regex(str) . La función r está disponible a través de una conversión implícita .
El signo @ vincula una variable a un nombre durante una coincidencia de patrón. La variable
enlazada puede ser todo el objeto coincidente o parte del objeto coincidente:
https://riptutorial.com/es/home 115
Seq(Some(1), Some(2), None) match {
// Only the first element of the matched sequence is bound to the name 'c'
case Seq(c @ Some(1), _*) => head
case _ => None
}
La coincidencia de patrones también se puede usar para verificar el tipo de una instancia, en
lugar de usar isInstanceOf[B] :
anyRef match {
case _: Number => "It is a number"
case _: String => "It is a string"
case _: CharSequence => "It is a char sequence"
}
//> res0: String = It is a string
anyRef match {
case _: Number => "It is a number"
case _: CharSequence => "It is a char sequence"
case _: String => "It is a string"
}
//> res1: String = It is a char sequence
De esta manera, es similar a una declaración clásica de "cambio", sin la funcionalidad de acceso
directo. Sin embargo, también puede establecer patrones de coincidencia y valores de 'extracción'
del tipo en cuestión. Por ejemplo:
Tenga en cuenta que en el caso de Foo y Woo usamos el guión bajo ( _ ) para "coincidir con una
variable no vinculada". Es decir, el valor (en este caso Hadas y 27 , respectivamente) no está
https://riptutorial.com/es/home 116
vinculado a un nombre y, por lo tanto, no está disponible en el controlador para ese caso. Esta es
una taquigrafía útil para hacer coincidir el valor 'cualquiera' sin preocuparse por cuál es ese valor.
La anotación @switch le dice al compilador que la declaración de match se puede reemplazar con
una sola instrucción de tableswitch en el nivel de bytecode. Esta es una optimización menor que
puede eliminar comparaciones innecesarias y cargas variables durante el tiempo de ejecución.
La anotación @switch funciona solo para coincidencias contra constantes literales e identificadores
de final val . Si la coincidencia de patrón no se puede compilar como un tableswitch /
lookupswitch , el compilador mostrará una advertencia.
import annotation.switch
Los resultados son los mismos que en una coincidencia de patrón normal:
scala> suffix(2)
res1: String = "2nd"
scala> suffix(4)
res2: String = "4th"
De la especificación de Java:
El | se puede usar para que una única sentencia de caso coincida con varias entradas para
obtener el mismo resultado:
https://riptutorial.com/es/home 117
case "foo" | "bar" => "Matched!"
case _ => "No match."
}
Tenga en cuenta que si bien los valores coincidentes funcionan de esta manera, la siguiente
coincidencia de tipos causará problemas:
val d = Foo("Diana")
val h = Bar("Hadas")
Si en el último caso (con _ ) no necesita el valor de la variable no vinculada y solo quiere hacer
otra cosa, está bien:
https://riptutorial.com/es/home 118
La comparación de patrones se puede utilizar para manejar cada elemento de manera diferente:
El primer caso muestra cómo hacer coincidir una cadena específica y obtener el precio
correspondiente. El segundo caso muestra el uso de if y la extracción de la tupla para que
coincida con los elementos de la tupla.
https://riptutorial.com/es/home 119
Capítulo 32: Macros
Introducción
Las macros son una forma de metaprogramación en tiempo de compilación. Ciertos elementos
del código Scala, como las anotaciones y los métodos, pueden transformarse en otro código
cuando se compilan. Las macros son códigos Scala normales que operan en tipos de datos que
representan otros códigos. El complemento [Macro Paradise] [] extiende las capacidades de las
macros más allá del lenguaje base. [Paraíso macro]: http://docs.scala-
lang.org/overviews/macros/paradise.html
Sintaxis
• def x () = macro x_impl // x es una macro, donde x_impl se usa para transformar el código
• def macroTransform (annottees: Any *): Any = macro impl // Usar en las anotaciones para
hacerlas macros
Observaciones
Las macros son una función de idioma que se debe habilitar, ya sea importando
scala.language.macros o con la opción del compilador -language:macros . Solo las definiciones de
macros requieren esto; El código que utiliza macros no necesita hacerlo.
Examples
Anotación de macro
Esta simple anotación de macro genera el elemento anotado tal como está.
object linkMacro {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
c.Expr[Any](q"{..$annottees}")
}
}
El @compileTimeOnly anotación genera un error con un mensaje que indica que el paradise
complemento compilador debe ser incluido para utilizar esta macro. Las instrucciones para incluir
https://riptutorial.com/es/home 120
esto a través de SBT están aquí .
@noop
case class Foo(a: String, b: Int)
@noop
object Bar {
def f(): String = "hello"
}
@noop
def g(): Int = 10
Método de macros
Cuando se define un método para que sea una macro, el compilador toma el código que se pasa
como su argumento y lo convierte en un AST. A continuación, invoca la implementación de la
macro con ese AST y devuelve un nuevo AST que luego se empalma de nuevo a su sitio de
llamada.
import reflect.macros.blackbox.Context
object Macros {
// This macro simply sees if the argument is the result of an addition expression.
// E.g. isAddition(1+1) and isAddition("a"+1).
// but !isAddition(1+1-1), as the addition is underneath a subtraction, and also
// !isAddition(x.+), and !isAddition(x.+(a,b)) as there must be exactly one argument.
def isAddition(x: Any): Boolean = macro isAddition_impl
// The signature of the macro implementation is the same as the macro definition,
// but with a new Context parameter, and everything else is wrapped in an Expr.
def isAddition_impl(c: Context)(expr: c.Expr[Any]): c.Expr[Boolean] = {
import c.universe._ // The universe contains all the useful methods and types
val plusName = TermName("+").encodedName // Take the name + and encode it as $plus
expr.tree match { // Turn expr into an AST representing the code in isAddition(...)
case Apply(Select(_, `plusName`), List(_)) => reify(true)
// Pattern match the AST to see whether we have an addition
// Above we match this AST
// Apply (function application)
// / \
// Select List(_) (exactly one argument)
// (selection ^ of entity, basically the . in x.y)
// / \
// _ \
// `plusName` (method named +)
case _ => reify(false)
// reify is a macro you use when writing macros
// It takes the code given as its argument and creates an Expr out of it
}
}
}
También es posible tener macros que tomen los Tree como argumentos. Por ejemplo, cómo reify
se utiliza para crear Expr s, el q (por quasiquote) interpolador cadena nos permite crear y
https://riptutorial.com/es/home 121
deconstruir Tree s. Tenga en cuenta que podríamos haber usado q arriba ( expr.tree es, sorpresa,
un Tree sí) también, pero no con propósitos demostrativos.
Errores en macros
Las macros pueden activar advertencias y errores del compilador mediante el uso de su Context .
Digamos que somos particularmente celosos cuando se trata de un código incorrecto, y queremos
marcar cada instancia de deuda técnica con un mensaje de información del compilador (no
pensemos en lo mala que es esta idea). Podemos usar una macro que no haga nada excepto
emitir un mensaje de este tipo.
import reflect.macros.blackbox.Context
Además, en lugar de usar ??? Para marcar código no implementado, podemos crear dos macros,
!!! y ?!? , que sirven al mismo propósito, pero emiten avisos de compilación. ?!? hará que se
emita una advertencia, y !!! causará un error absoluto
import reflect.macros.blackbox.Context
https://riptutorial.com/es/home 122
// expression. It is actually named _root_, but if someone were to shadow it, every
// reference to it would be an error. It allows us to safely access ??? and know that
// it is the one we want.
}
https://riptutorial.com/es/home 123
Capítulo 33: Manejo de errores
Examples
Tratar
import scala.util.Try
Ya sea
response.webService.status match {
case 200 => {
val person = parsePerson(response)
if(!isValid(person)) Left("Validation failed")
else Right(person)
}
getPersonFromWebService("http://some-webservice.com/person") match {
case Left(errorMessage) => println(errorMessage)
https://riptutorial.com/es/home 124
case Right(person) => println(person.surname)
}
Opción
El uso de valores null no se recomienda, a menos que se interactúe con el código Java heredado
que se espera que sea null . En su lugar, la Option debería usarse cuando el resultado de una
función puede ser algo ( Some ) o nada ( None ).
Un bloque try-catch es más apropiado para el manejo de errores, pero si la función no puede
devolver nada legítimamente, la Option es apropiada para el uso y es simple.
La coincidencia de patrones
findPerson(personName) match {
case Some(person) => println(person.surname)
case None => println(s"No person found with name $personName")
}
Utilizando pliegue
Convertir a Java
Si necesita convertir un tipo de Option a un tipo de Java con capacidad nula para la
https://riptutorial.com/es/home 125
interoperabilidad:
Cuando se lanza una exception desde un Future , puede (debería) usar recover para manejarlo.
Por ejemplo,
... lanzará una Exception desde dentro del Future . Pero viendo que podemos predecir una
Exception de tipo FairlyStupidException con una alta probabilidad, podemos manejar este caso
específicamente de una manera elegante:
Como puede ver, el método dado para recover es un PartialFunction sobre el dominio de todos
los Throwable , por lo que puede manejar solo algunos tipos y luego dejar que el resto entre en el
nivel de manejo de excepciones en los niveles más altos en la pila Future .
Tenga en cuenta que esto es similar a ejecutar el siguiente código en un contexto no Future :
try {
runNotFuture
} catch {
case e: FairlyStupidException => BadRequest("Another stupid exception!")
}
Además de las construcciones funcionales, como Try , Option y Either para el manejo de errores,
https://riptutorial.com/es/home 126
Scala también admite una sintaxis similar a la de Java, utilizando una cláusula try-catch (con un
bloque de potencial también). La cláusula catch es una coincidencia de patrón:
try {
// ... might throw exception
} catch {
case ioe: IOException => ... // more specific cases first
case e: Exception => ...
// uncaught types will be thrown
} finally {
// ...
}
Para convertir excepciones en Either o Option tipos, puede utilizar métodos que proporcionan en
scala.util.control.Exception
import scala.util.control.Exception._
https://riptutorial.com/es/home 127
Capítulo 34: Manejo de XML
Examples
Beautify o Pretty-Print XML
Esto generará el contenido utilizando un ancho de página de 150 y una constante de sangría de 4
caracteres de espacio en blanco:
<a>
Alana
<b>
<c>Beth</c>
<d>Catie</d>
</b>
</a>
Puede usar XML.loadFile("nameoffile.xml") para cargar xml desde un archivo en lugar de desde
una cadena.
https://riptutorial.com/es/home 128
Capítulo 35: Mejores prácticas
Observaciones
Prefiere vals, objetos inmutables y métodos sin efectos secundarios. Alcanzarlos
primero. Use vars, objetos mutables y métodos con efectos secundarios cuando tenga
una necesidad específica y una justificación para ellos.
Examples
Mantenlo simple
Estas cosas no están claras para los recién llegados: evite usarlas antes de que las entienda. El
uso de conceptos avanzados sin una necesidad real confunde el código, haciéndolo menos fácil
de mantener.
if (userAuthorized.nonEmtpy) {
makeRequest().map {
case Success(respone) =>
someProcessing(..)
if (resendToUser) {
sendToUser(...)
}
...
}
}
https://riptutorial.com/es/home 129
Si todas sus funciones devuelven Either u otro tipo de Validation , puede escribir:
for {
user <- authorizeUser
response <- requestToThirdParty(user)
_ <- someProcessing(...)
} {
sendToUser
}
Por defecto:
• Use val , no var , siempre que sea posible. Esto le permite aprovechar al máximo una serie
de utilidades funcionales, incluida la distribución del trabajo.
• Utilice recursion y comprehensions s, no bucles.
• Utiliza colecciones inmutables. Este es un corolario al uso de val siempre que sea posible.
• Concéntrese en las transformaciones de datos, la lógica de estilo CQRS y no CRUD.
• var se puede utilizar para el estado local (por ejemplo, dentro de un actor).
• mutable da mejor desempeño en ciertas situaciones.
https://riptutorial.com/es/home 130
Capítulo 36: Mientras bucles
Sintaxis
• while (boolean_expression) {block_expression}
Parámetros
Parámetro Detalles
Observaciones
La diferencia principal entre los bucles while y do-while while es si ejecutan la block_expression
antes de verificar si deben realizar un bucle.
Debido do-while bucles while y do-while se basan en una expresión para evaluar en false para
terminar, a menudo requieren que el estado mutable se declare fuera del bucle y luego se
modifique dentro del bucle.
Examples
Mientras bucles
var line = 0
var maximum_lines = 5
Do-While Loops
var line = 0
var maximum_lines = 5
do {
line = line + 1
https://riptutorial.com/es/home 131
println("Line number: " + line)
} while (line < maximum_lines)
La do / while de bucle se utiliza con poca frecuencia en la programación funcional, pero se puede
utilizar para evitar la falta de apoyo a la break / continue constructo, como se ve en otros idiomas:
if(initial_condition) do if(filter) {
...
} while(continuation_condition)
https://riptutorial.com/es/home 132
Capítulo 37: Mónadas
Examples
Definición de la mónada
Definicion formal
La mónada M es un tipo paramétrico M[T] con dos operaciones flatMap y unit , como:
trait M[T] {
def flatMap[U](f: T => M[U]): M[U]
}
Ejemplo :
val m = List(1, 2, 3)
def unit(x: Int): List[Int] = List(x)
def f(x: Int): List[Int] = List(x * x)
def g(x: Int): List[Int] = List(x * x * x)
val x = 1
1. Asociatividad
https://riptutorial.com/es/home 133
//Right side:
m.flatMap(x => (x * x) * (x * x) * (x * x)) //List(1, 64, 729)
2. Unidad izquierda
3. Unidad derecha
val a = List(1, 2, 3)
val b = List(3, 4, 5)
for {
i <- a
j <- b
} yield(i * j)
Lo anterior es equivalente a:
a flatMap {
i => b map {
j => i * j
}
}
Debido a que una mónada conserva la estructura de datos y solo actúa sobre los elementos
dentro de esa estructura, podemos construir estructuras de datos monádicas en cadena sin fin,
como se muestra aquí en una para comprensión.
https://riptutorial.com/es/home 134
Capítulo 38: Operadores en Scala
Examples
Operadores incorporados
Scala tiene los siguientes operadores integrados (métodos / elementos de lenguaje con reglas de
precedencia predefinidas):
Nota : los métodos que terminan con : enlazar a la derecha (y asociativa a la derecha), por lo que
la llamada con list.::(value) se puede escribir como value :: list con la sintaxis del operador. (
1 :: 2 :: 3 :: Nil es lo mismo que 1 :: (2 :: (3 :: Nil)) )
class Team {
def +(member: Person) = ...
}
ITTeam + Jack
ITTeam.+(Jack)
Para definir operadores unarios puedes prefijarlo con unary_ . Por ejemplo, unary_!
class MyBigInt {
https://riptutorial.com/es/home 135
def unary_! = ...
}
Operador
*/%
+-
https://riptutorial.com/es/home 136
Operador
=!
<>
&
La única excepción a esta regla se refiere a los operadores de asignación , por ejemplo, += , *= ,
etc. Si un operador termina con un carácter igual (=) y no es uno de los operadores de
comparación <= , >= , == o != , entonces la prioridad del operador es la misma que la asignación
simple. En otras palabras, más bajo que el de cualquier otro operador.
https://riptutorial.com/es/home 137
Capítulo 39: Paquetes
Introducción
Los paquetes en Scala administran espacios de nombres en grandes programas. Por ejemplo, la
connection nombre puede ocurrir en los paquetes com.sql y org.http . Puede usar
com.sql.connection y org.http.connection , respectivamente, para acceder a cada uno de estos
paquetes.
Examples
Estructura del paquete
package com {
package utility {
package serialization {
class Serializer
...
}
}
}
Paquetes y archivos
La cláusula del paquete no se enlaza directamente con el archivo donde se encuentra. Es posible
encontrar elementos comunes de la cláusula del paquete en diferentes archivos. Por ejemplo, las
siguientes cláusulas del paquete se encuentran en el archivo math1.scala y en el archivo
math2.scala.
Archivo math1.scala
package org {
package math {
package statistics {
class Interval
}
}
}
Archivo math2.scala
package org {
package math{
package probability {
class Density
}
}
}
https://riptutorial.com/es/home 138
Archivo study.scala
import org.math.probability.Density
import org.math.statistics.Interval
object Study {
Los paquetes de Scala deben seguir las convenciones de nomenclatura de paquetes Java.
Los nombres de paquetes se escriben en minúsculas para evitar conflictos con los nombres de
clases o interfaces. Las compañías usan su nombre de dominio de Internet invertido para
comenzar los nombres de sus paquetes, por ejemplo,
io.super.math
https://riptutorial.com/es/home 139
Capítulo 40: Para expresiones
Sintaxis
• para {cláusulas} cuerpo
• para {cláusulas} cuerpo de rendimiento
• para (cláusulas) cuerpo
• para (cláusulas) ceder cuerpo
Parámetros
Parámetro Detalles
Use esto si quiere crear o 'producir' una colección. El uso de yield hará que el
rendimiento
tipo de retorno de for sea una colección en lugar de Unit .
Examples
Basic For Loop
Esto demuestra la iteración de una variable, x , de 1 a 10 y hacer algo con ese valor. El tipo de
retorno de este for comprensión es la Unit .
Esto demuestra un filtro en un bucle for, y el uso del yield para crear una 'comprensión de
secuencia':
for ( x <- 1 to 10 if x % 2 == 0)
yield x
https://riptutorial.com/es/home 140
Una para la comprensión es útil cuando necesita crear una nueva colección basada en la
iteración y sus filtros.
for {
x <- 1 to 2
y <- 'a' to 'd'
} println("(" + x + "," + y + ")")
(Tenga en cuenta que to aquí es un método operador infijo que devuelve un rango inclusivo .
Véase la definición aquí .)
(1,a)
(1,b)
(1,c)
(1,d)
(2,a)
(2,b)
(2,c)
(2,d)
Tenga en cuenta que esta es una expresión equivalente, utilizando paréntesis en lugar de
corchetes:
for (
x <- 1 to 2
y <- 'a' to 'd'
) println("(" + x + "," + y + ")")
Para obtener todas las combinaciones en un solo vector, podemos yield el resultado y
establecerlo en un valor val :
val a = for {
x <- 1 to 2
y <- 'a' to 'd'
} yield "(%s,%s)".format(x, y)
// a: scala.collection.immutable.IndexedSeq[String] = Vector((1,a), (1,b), (1,c), (1,d),
(2,a), (2,b), (2,c), (2,d))
Si tiene varios objetos de tipos monádicos , podemos lograr combinaciones de los valores
utilizando un 'para comprensión':
for {
x <- Option(1)
https://riptutorial.com/es/home 141
y <- Option("b")
z <- List(3, 4)
} {
// Now we can use the x, y, z variables
println(x, y, z)
x // the last expression is *not* the output of the block in this case!
}
// This prints
// (1, "b", 3)
// (1, "b", 4)
Si los objetos son del mismo tipo monádico M (p. Ej., Option ), usar el yield devolverá un objeto de
tipo M lugar de Unit .
val a = for {
x <- Option(1)
y <- Option("b")
} yield {
// Now we can use the x, y variables
println(x, y)
// whatever is at the end of the block is the output
(7 * x, y)
}
// This prints:
// (1, "b")
// The val `a` is set:
// a: Option[(Int, String)] = Some((7,b))
Tenga en cuenta que la palabra clave de yield no se puede utilizar en el ejemplo original, donde
hay una combinación de tipos monádicos ( Option y List ). Tratar de hacerlo producirá un error de
falta de coincidencia de tipo de tiempo de compilación.
https://riptutorial.com/es/home 142
implementan utilizando los withFilter , foreach , flatMap y map de sus tipos de materias. Por esta
razón, sólo los tipos que han definido estos métodos pueden ser utilizados en una for
comprensión.
... reducirá el azúcar a las llamadas anidadas usando withFilter y flatMap o map :
(Tenga en cuenta que el map se usa en la comprensión más profunda, y flatMap se usa en cada
comprensión externa).
A for comprensión se puede aplicar a cualquier tipo implementando los métodos requeridos por la
representación sin azúcar. No hay restricciones en los tipos de retorno de estos métodos, siempre
que sean compositivos.
https://riptutorial.com/es/home 143
Capítulo 41: Programación a nivel de tipo
Examples
Introducción a la programación a nivel de tipo.
Si consideramos una lista heterogénea, en la que los elementos de la lista tienen tipos variados
pero conocidos, podría ser deseable poder realizar operaciones en los elementos de la lista
colectivamente sin descartar la información de tipo de los elementos. El siguiente ejemplo
implementa una operación de mapeo sobre una simple lista heterogénea.
Debido a que el tipo de elemento varía, la clase de operaciones que podemos realizar está
restringida a alguna forma de proyección de tipo, por lo que definimos una característica de
Projection tiene el type Apply[A] abstracto type Apply[A] calculando el tipo de resultado de la
proyección, y def apply[A](a: A): Apply[A] calculando el valor del resultado de la proyección.
trait Projection {
type Apply[A] // <: Any
def apply[A](a: A): Apply[A]
}
Nuestro tipo de lista heterogénea define una operación de map parametrizada por la proyección
deseada, así como el tipo de proyección. El resultado de la operación del mapa es abstracto,
variará según la clase y la proyección, y, naturalmente, debe seguir siendo un HList :
En el caso de HNil , la lista heterogénea vacía, el resultado de cualquier proyección siempre será
el mismo. Aquí declaramos el trait HNil como una conveniencia para que podamos escribir HNil
como un tipo en lugar de HNil.type :
HCons es la lista heterogénea no vacía. Aquí afirmamos que al aplicar una operación de mapa, el
tipo de cabecera resultante es el que resulta de la aplicación de la proyección al valor de
cabecera ( P#Apply[H] ), y que el tipo de cola resultante es la que resulta de mapear el proyección
sobre la cola ( T#Map[P] ), que se sabe que es una lista HList :
https://riptutorial.com/es/home 144
case class HCons[H, T <: HList](head: H, tail: T) extends HList {
type Map[P <: Projection] = HCons[P#Apply[H], T#Map[P]]
def map[P <: Projection](p: P): Map[P] = HCons(p.apply(head), tail.map(p))
}
https://riptutorial.com/es/home 145
Capítulo 42: Pruebas con ScalaCheck
Introducción
ScalaCheck es una biblioteca escrita en Scala y utilizada para realizar pruebas automatizadas
basadas en propiedades de los programas Scala o Java. ScalaCheck se inspiró originalmente en
la biblioteca QuickCheck de Haskell, pero también se ha aventurado en su propia cuenta.
ScalaCheck no tiene más dependencias externas que el tiempo de ejecución de Scala, y funciona
muy bien con sbt, la herramienta de construcción de Scala. También está completamente
integrado en los marcos de prueba ScalaTest y specs2.
Examples
Scalacheck con scalatest y mensajes de error.
import org.scalatest.prop.Checkers
import org.scalatest.{Matchers, WordSpecLike}
import org.scalacheck.Gen._
import org.scalacheck.Prop._
import org.scalacheck.Prop
object Splitter {
def splitLineByColon(message: String): (String, String) = {
val (command, argument) = message.indexOf(":") match {
case -1 =>
(message, "")
case x: Int =>
(message.substring(0, x), message.substring(x + 1))
}
(command.trim, argument.trim)
}
https://riptutorial.com/es/home 146
val (command, argument) = splitLineByColon(message)
(command.trim, argument.trim + 2)
}
}
}
}
}
}
}
}
https://riptutorial.com/es/home 147
}
}
}
La salida (fragmentos):
https://riptutorial.com/es/home 148
Capítulo 43: Pruebas con ScalaTest
Examples
Hola World Spec Test
• Este ejemplo utiliza FlatSpec y Matchers , que forman parte de la biblioteca ScalaTest.
• FlatSpec permite que las pruebas se escriban en el estilo de desarrollo impulsado por el
comportamiento (BDD) . En este estilo, se usa una oración para describir el
comportamiento esperado de una unidad de código dada. La prueba confirma que el código
se adhiere a ese comportamiento. Vea la documentación para información adicional .
Preparar
Tipo de verificación
Tenga en cuenta que los paréntesis aquí se utilizan para obtener el tipo String .
Cheque igual
https://riptutorial.com/es/home 149
helloWorld shouldEqual "Hello World"
helloWorld should === ("Hello World")
helloWorldCount shouldEqual 1
helloWorldCount shouldBe 1
helloWorldList shouldEqual List("Hello World", "Bonjour Le Monde")
helloWorldList === List("Hello World", "Bonjour Le Monde")
Cheque no igual
Verificación de longitud
Verificación de excepciones
https://riptutorial.com/es/home 150
Capítulo 44: Rasgos
Sintaxis
• rasgo ATrait {...}
• clase AClass (...) extiende ATrait {...}
• clase AClass extiende BClass con ATrait
• clase AClass extiende ATrait con BTrait
• La clase AClass extiende ATrait con BTrait con CTrait.
• clase ATrait extiende BTrait
Examples
Modificación apilable con rasgos
Puede usar rasgos para modificar los métodos de una clase, usando rasgos en forma apilable.
El siguiente ejemplo muestra cómo se pueden apilar los rasgos. El ordenamiento de los rasgos es
importante. Usando diferentes orden de rasgos, se logra un comportamiento diferente.
class Ball {
def roll(ball : String) = println("Rolling : " + ball)
}
object Balls {
def main(args: Array[String]) {
val ball1 = new Ball with Shiny with Red
ball1.roll("Ball-1") // Rolling : Shiny-Red-Ball-1
Tenga en cuenta que super se utiliza para invocar el método roll() en ambos rasgos. Solo así
podremos lograr una modificación apilable. En casos de modificación apilable, el orden de
invocación del método está determinado por la regla de linealización .
https://riptutorial.com/es/home 151
Fundamentos del rasgo
trait Identifiable {
def getIdentifier: String
def printIndentification(): Unit = println(getIdentifier)
}
Dado que no se declara ninguna AnyRef para el carácter Identifiable , por defecto se extiende
desde la clase AnyRef . Debido a que no se proporciona una definición para getIdentifier en
Identifiable , la clase Puppy debe implementarla. Sin embargo, Puppy hereda la implementación de
printIdentification de Identifiable .
En el REPL:
El problema de diamante , o herencia múltiple, es manejado por Scala usando Rasgos, que son
similares a las interfaces de Java. Los rasgos son más flexibles que las interfaces y pueden incluir
métodos implementados. Esto hace rasgos similares a mixins en otros idiomas.
Scala no admite la herencia de varias clases, pero un usuario puede extender múltiples rasgos en
una sola clase:
trait traitA {
def name = println("This is the 'grandparent' trait.")
}
https://riptutorial.com/es/home 152
grandChild.name
Aquí grandChild se hereda de traitB y traitC , que a su vez heredan de traitA . La salida (a
continuación) también muestra el orden de prioridad al resolver qué implementaciones de
métodos se llaman primero:
C is a child of A.
B is a child of A.
This is the 'grandparent' trait.
Tenga en cuenta que, cuando se utiliza super para invocar métodos en class o trait , la regla de
linealización entra en juego para decidir la jerarquía de llamadas. El orden de linealización para
grandChild será:
grandChild -> traitC -> traitB -> traitA -> AnyRef -> Any
trait Printer {
def print(msg : String) = println (msg)
}
object TestPrinter{
def main(args: Array[String]) {
new CustomPrinter().print("Hello World!")
}
}
*************
-------------
Hello World!
CustomPrinter -> DelimitWithStar -> DelimitWithHyphen -> Printer -> AnyRef -> Any
https://riptutorial.com/es/home 153
Linealización
En caso de modificación apilable , Scala organiza clases y rasgos en un orden lineal para
determinar la jerarquía de llamadas a métodos, lo que se conoce como linealización . La regla de
linealización se usa solo para aquellos métodos que involucran la invocación de métodos a través
de super() . Consideremos esto con un ejemplo:
class Shape {
def paint (shape: String): Unit = {
println(shape)
}
}
https://riptutorial.com/es/home 154
porque, en la linealización de MyShape hasta ahora ( Paso 2 ), Shape -> AnyRef -> Any ya ha
aparecido. Por lo tanto, se ignora. Así, la linealización Blue será:
Blue -> Color -> Dotted -> Border -> Shape -> AnyRef -> Any
Círculo -> Azul -> Color -> Punteado -> Borde -> Forma -> AnyRef -> Cualquiera
Este orden de linealización decide el orden de invocación de los métodos cuando se utiliza super
en cualquier clase o rasgo. La primera implementación del método desde la derecha se invoca,
en el orden de linealización. Si se new MyShape().paint("Circle ") , se imprimirá:
https://riptutorial.com/es/home 155
Capítulo 45: Recursion
Examples
Recursion de cola
Usando la recursión regular, cada llamada recursiva inserta otra entrada en la pila de llamadas.
Cuando se completa la recursión, la aplicación tiene que quitar cada entrada por completo hacia
abajo. Si hay muchas llamadas a funciones recursivas, puede terminar con una pila enorme.
Recursion regular
Este ejemplo no es recursivo de cola porque cuando se realiza la llamada recursiva, la función
debe realizar un seguimiento de la multiplicación que debe hacer con el resultado después de que
se devuelve la llamada.
println(fact(5))
La llamada a la función con el parámetro dará como resultado una pila que se verá así:
(fact 5)
(* 5 (fact 4))
(* 5 (* 4 (fact 3)))
(* 5 (* 4 (* 3 (fact 2))))
(* 5 (* 4 (* 3 (* 2 (fact 1)))))
(* 5 (* 4 (* 3 (* 2 (* 1 (fact 0))))))
(* 5 (* 4 (* 3 (* 2 (* 1 * 1)))))
(* 5 (* 4 (* 3 (* 2))))
(* 5 (* 4 (* 6)))
(* 5 (* 24))
120
Si intentamos anotar este ejemplo con @tailrec , obtendremos el siguiente mensaje de error: could
not optimize @tailrec annotated method fact: it contains a recursive call not in tail position
Recursion de cola
En la recursión de cola, primero realiza sus cálculos y luego ejecuta la llamada recursiva,
https://riptutorial.com/es/home 156
pasando los resultados de su paso actual al siguiente paso recursivo.
println(fact_with_tailrec(5))
(fact_with_tailrec 5)
(fact_inside 5 1)
(fact_inside 4 5)
(fact_inside 3 20)
(fact_inside 2 60)
(fact_inside 1 120)
Solo existe la necesidad de realizar un seguimiento de la misma cantidad de datos para cada
llamada a fact_inside porque la función simplemente está devolviendo el valor que llegó a la parte
superior. Esto significa que incluso si se llama a fact_with_tail 1000000 , solo se necesita la
misma cantidad de espacio que fact_with_tail 3 . Este no es el caso con la llamada no recursiva
de cola, y como tales valores grandes pueden causar un desbordamiento de pila.
import scala.util.control.TailCalls._
https://riptutorial.com/es/home 157
fib(40).result
https://riptutorial.com/es/home 158
Capítulo 46: Reflexión
Examples
Cargando una clase usando la reflexión
import scala.reflect.runtime.universe._
val mirror = runtimeMirror(getClass.getClassLoader)
val module = mirror.staticModule("org.data.TempClass")
https://riptutorial.com/es/home 159
Capítulo 47: Scala.js
Introducción
Scala.js es un puerto de Scala que se compila a JavaScript , que al final se ejecutará fuera de la
JVM . Tiene ventajas como la escritura fuerte, la optimización de código en tiempo de compilación,
la interoperabilidad total con las bibliotecas de JavaScript.
Examples
console.log en Scala.js
Clase simple
Colecciones
Manipulando DOM
import org.scalajs.dom
import dom.document
https://riptutorial.com/es/home 160
target.appendChild(pNode)
}
Dependencia sbt
Corriendo
sbt run
sbt ~run
sbt fastOptJS
https://riptutorial.com/es/home 161
Capítulo 48: Scaladoc
Sintaxis
• Va por encima de los métodos, campos, clases o paquetes.
• Comienza con /**
• Cada línea tiene un inicio * procesando con los comentarios.
• Termina con */
Parámetros
Parámetro Detalles
Clase especifica _
Método específico _
Método, constructor y / o
_
etiquetas de clase.
Uso _
Otro _
https://riptutorial.com/es/home 162
Parámetro Detalles
Examples
Scaladoc simple al método
/**
* Explain briefly what method does here
* @param x Explain briefly what should be x and how this affects the method.
* @param y Explain briefly what should be y and how this affects the method.
* @return Explain what is returned from execution.
*/
def method(x: Int, y: String): Option[Double] = {
// Method content
}
https://riptutorial.com/es/home 163
Capítulo 49: Scalaz
Introducción
Scalaz es una librería Scala para programación funcional.
Examples
AplicarUso
import scalaz._
import Scalaz._
scala> Apply[Option].ap(1.some)(some(intToString))
res1: Option[String] = Some(1)
scala> Apply[Option].ap(none)(some(intToString))
res2: Option[String] = None
FunctorUsage
import scalaz._
import Scalaz._
scala> val len: String => Int = _.length
len: String => Int = $$Lambda$1164/969820333@7e758f40
scala> Functor[Option].map(Some("foo"))(len)
res0: Option[Int] = Some(3)
scala> Functor[Option].map(None)(len)
res1: Option[Int] = None
https://riptutorial.com/es/home 164
scala> :kind Functor
scalaz.Functor's kind is X[F[A]]
Uso de la flecha
import scalaz._
import Scalaz._
scala> val plus1 = (_: Int) + 1
plus1: Int => Int = $$Lambda$1167/1113119649@6a6bfd97
https://riptutorial.com/es/home 165
Capítulo 50: Si expresiones
Examples
Básico Si Expresiones
En Scala (en contraste con Java y la mayoría de los otros lenguajes), if es una expresión en
lugar de una declaración . En cualquier caso, la sintaxis es idéntica:
val result = if(x > 0) "Greater than 0" else "Less than or equals 0"
\\ result: String = Greater than 0
Arriba vemos que la expresión if se evalúa y el result se establece en ese valor resultante.
El tipo de retorno de una expresión if es el supertipo de todas las ramas lógicas. Esto significa
que para este ejemplo, el tipo de retorno es una String . Como no todas las expresiones if
devuelven un valor (como una instrucción if que no tiene else lógica de bifurcación), es posible
que el tipo de retorno sea Any :
Si no se puede devolver ningún valor (por ejemplo, si solo se usan efectos secundarios como
println dentro de las ramas lógicas), el tipo de retorno será Unit :
ifexpresiones en Scala son similares a cómo funciona el operador ternario en Java . Debido a
esta similitud, no hay tal operador en Scala: sería redundante.
https://riptutorial.com/es/home 166
Capítulo 51: Símbolos literales
Observaciones
Scala viene con un concepto de símbolos : cadenas que se internan , es decir: dos símbolos con
el mismo nombre (la misma secuencia de caracteres), en contra de las cadenas, se referirán al
mismo objeto durante la ejecución.
Los símbolos son una característica de muchos idiomas: Lisp, Ruby y Erlang y más, sin embargo,
en Scala son de uso relativamente pequeño. Buena característica para tener sin embargo.
Utilizar:
Cualquier literal que comience con una comilla simple ' , seguido de uno o más dígitos, letras o
puntuaciones inferiores _ es un símbolo literal. El primer carácter es una excepción ya que no
puede ser un dígito.
Buenas definiciones:
'ATM
'IPv4
'IPv6
'map_to_operations
'data_format_2006
Symbol("hakuna matata")
Symbol("To be or not to be that is a question")
Malas definiciones:
'8'th_division
'94_pattern
'bad-format
Examples
Reemplazo de cadenas en cláusulas de casos
Digamos que tenemos múltiples fuentes de datos que incluyen base de datos, archivo, solicitud y
lista de argumentos . Dependiendo de la fuente elegida cambiamos nuestro enfoque:
https://riptutorial.com/es/home 167
}
Podríamos haber usado muy bien String en lugar de Symbol . No lo hicimos, porque ninguna de las
características de las cadenas es útil en este contexto.
Esto hace que el código sea más simple y menos propenso a errores.
https://riptutorial.com/es/home 168
Capítulo 52: sincronizado
Sintaxis
• objectToSynchronizeOn.synchronized { /* code to run */}
• synchronized {/* code to run, can be suspended with wait */}
Examples
sincronizar en un objeto
synchronized es una construcción de concurrencia de bajo nivel que puede ayudar a evitar que
múltiples hilos accedan a los mismos recursos. Introducción a la JVM utilizando el lenguaje Java .
anInstance.synchronized {
// code to run when the intristic lock on `anInstance` is acquired
// other thread cannot enter concurrently unless `wait` is called on `anInstance` to suspend
// other threads can continue of the execution of this thread if they `notify` or
`notifyAll` `anInstance`'s lock
}
https://riptutorial.com/es/home 169
Capítulo 53: Sobrecarga del operador
Examples
Definición de operadores de infijo personalizados
En Scala, los operadores (como + , - , * , ++ , etc.) son solo métodos. Por ejemplo, 1 + 2 se puede
escribir como 1.+(2) . Este tipo de métodos se denominan 'operadores de infijo' .
Esto significa que los métodos personalizados se pueden definir en sus propios tipos, reutilizando
estos operadores:
Tenga en cuenta que los operadores de infijo solo pueden tener un solo argumento; el objeto
antes de que el operador llame a su propio operador en el objeto después del operador. Cualquier
método Scala con un solo argumento se puede usar como un operador de infijo.
Esto debe ser usado con parcimony. En general, se considera una buena práctica solo
si su propio método hace exactamente lo que uno esperaría de ese operador. En caso
de duda, use un nombre más conservador, como add lugar de + .
Los operadores unarios se pueden definir precediendo al operador con unary_ . Los operadores
unarios están limitados a unary_+ , unary_- , unary_! y unary_~ :
https://riptutorial.com/es/home 170
def unary_- = {
val newData = for (r <- 0 until rows) yield
for (c <- 0 until cols) yield this.data(r)(c) * -1
Esto debe ser usado con parcimony. Sobrecargar a un operador unario con una
definición que no es lo que uno esperaría puede llevar a la confusión del código.
https://riptutorial.com/es/home 171
Capítulo 54: Tipo de parametrización
(genéricos)
Examples
El tipo de opción
// lots of methods...
}
También podemos ver que esto tiene un método parametrizado, fold , que devuelve algo de tipo B
Métodos parametrizados
El tipo de retorno de un método puede depender del tipo de parámetro. En este ejemplo, x es el
parámetro, A es el tipo de x , que se conoce como el parámetro de tipo .
f(1) // 1
f("two") // "two"
f[Float](3) // 3.0F
Scala utilizará la inferencia de tipos para determinar el tipo de retorno, lo que restringe los
métodos a los que se puede llamar en el parámetro. Por lo tanto, se debe tener cuidado: lo
siguiente es un error de tiempo de compilación porque * no está definido para cada tipo A :
https://riptutorial.com/es/home 172
Colección genérica
class Cons(val head: Int, val tail: IntList) extends IntList { ... }
trait List[T] {
def isEmpty: Boolean
def head: T
def tail: List[T]
}
https://riptutorial.com/es/home 173
Capítulo 55: Tipos de métodos abstractos
únicos (tipos SAM)
Observaciones
Los métodos abstractos únicos son tipos, introducidos en Java 8 , que tienen exactamente un
miembro abstracto.
Examples
Sintaxis lambda
NOTA: Esto solo está disponible en Scala 2.12+ (y en las versiones recientes de la versión
2.11.x con los -Xexperimental -Xfuture compiler)
2.11.8
trait Runnable {
def run(): Unit
}
2.11.8
trait Runnable {
def run(): Unit
def concrete: Int = 42
}
https://riptutorial.com/es/home 174
Capítulo 56: Trabajando con datos en estilo
inmutable.
Observaciones
El método, el valor y los nombres de las variables deben estar en la caja inferior del
camello
Fuente: http://docs.scala-lang.org/style/naming-conventions.html
Esta compilación:
Examples
No es solo val vs. var.
val y var
scala> a = 456
<console>:8: error: reassignment to val
a = 456
scala> b = 321
https://riptutorial.com/es/home 175
b: Int = 321
• valreferencias val son inmutables: como una variable final en Java , una vez que se ha
inicializado no se puede cambiar
• var referencias var son reasignables como una simple declaración de variable en Java
scala> mut
Map(123 -> 123, 456 -> 456, 789 -> 789)
scala> imm
Map()
scala> imm + ("123" -> 123) + ("456" -> 456) + ("789" -> 789)
Map(123 -> 123, 456 -> 456, 789 -> 789)
Tomemos como ejemplo una función que toma 2 Map y devolvemos un Map contiene cada
elemento en ma y mb :
Un primer intento podría ser iterar a través de los elementos de uno de los mapas usando for
((k, v) <- map) y de alguna manera devolver el mapa combinado.
https://riptutorial.com/es/home 176
Este primer movimiento agrega inmediatamente una restricción: ahora se necesita una
mutación fuera de la misma for . Esto es más claro cuando se elimina el azúcar for :
// this:
for ((k, v) <- map) { ??? }
// is equivalent to:
map.foreach { case (k, v) => ??? }
foreach basa en los efectos secundarios. Cada vez que queremos que algo suceda dentro de un
foreach necesitamos "efectos secundarios", en este caso podríamos mutar un var result variable
var result o podemos usar una estructura de datos mutable.
Luego itere a través de mb agregando sus elementos y si la key del elemento actual en ma ya
existe, anulémosla con mb one.
Implementación mutable
Hasta ahora todo bien, "tuvimos que usar colecciones mutables" y una implementación correcta
podría ser:
Como se esperaba:
scala> merge2Maps(Map("a" -> 11, "b" -> 12), Map("b" -> 22, "c" -> 23))
Map(a -> 11, b -> 22, c -> 23)
Plegado al rescate.
¿Cómo podemos deshacernos de foreach en este escenario? Si lo único que debemos hacer es
iterar básicamente sobre los elementos de la colección y aplicar una función mientras se acumula
el resultado en la opción, podría estar usando .foldLeft :
https://riptutorial.com/es/home 177
def merge2Maps(ma: Map[String, Int], mb: Map[String, Int]): Map[String, Int] = {
mb.foldLeft(ma) { case (result, (k, v)) => result + (k -> v) }
// or more concisely mb.foldLeft(ma) { _ + _ }
}
Resultado intermedio
Obviamente, esta solución inmutable produce y destruye muchas instancias del Map mientras se
pliega, pero vale la pena mencionar que esas instancias no son un clon completo del Map
acumulado, sino que comparten una estructura significativa (datos) con la instancia existente.
Es más fácil razonar acerca de la semántica si es más declarativo como el enfoque .foldLeft . El
uso de estructuras de datos inmutables podría ayudar a hacer que nuestra implementación sea
más fácil de razonar.
https://riptutorial.com/es/home 178
Capítulo 57: Trabajando con gradle
Examples
Configuración básica
group 'scala_gradle'
version '1.0-SNAPSHOT'
repositories {
jcenter()
mavenCentral()
maven {
url "https://repo.typesafe.com/typesafe/maven-releases"
}
}
dependencies {
compile group: 'org.scala-lang', name: 'scala-library', version: '2.10.6'
}
Después de pasar por el ejemplo de la Configuración básica , es posible que repita la mayor
parte de él en cada uno de los proyectos de Scala Gradle. Huele a código repetitivo ...
¿Qué pasaría si, en lugar de aplicar el complemento de Scala ofrecido por Gradle, pudiera aplicar
su propio complemento de Scala, que sería responsable de manejar toda su lógica de
compilación común, extendiendo, al mismo tiempo, el complemento ya existente.
https://riptutorial.com/es/home 179
ayuda de la API de Gradle, como se describe en la documentación . Como lenguaje de
implementación, puede usar Scala en sí o incluso Java. Sin embargo, la mayoría de los ejemplos
que puede encontrar en todos los documentos están escritos en Groovy. Si necesita más
muestras de código o quiere comprender qué hay detrás del complemento de Scala, por ejemplo,
puede consultar el repositorio de github de Gradle .
Escribiendo el plugin
Requerimientos
Pauta de implementación
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
compile gradleApi()
compile "org.scala-lang:scala-library:2.12.0"
}
Notas :
https://riptutorial.com/es/home 180
2. Crear una nueva clase Scala para la implementación del complemento.
package com.btesila.gradle.plugins
Notas :
• para implementar un complemento, simplemente extienda el rasgo de Plugin del tipo Project
y anule el método de apply
• dentro del método de aplicación, tiene acceso a la instancia de Project a la que se aplica el
complemento y puede usarlo para agregarle lógica de compilación
• este complemento no hace más que aplicar el ya existente Gradle Scala Plugin
En primer lugar, creamos una clase ScalaVersion , que contendrá las dos propiedades de la
versión
class ScalaVersion {
var major: String = "2.12"
var minor: String = "0"
}
Una cosa interesante acerca de los complementos de Gradle es el hecho de que siempre puede
agregar o anular propiedades específicas. Un complemento recibe este tipo de información del
usuario a través del ExtensionContainer adjunto a una instancia del Project gradle. Para más
detalles, mira esto .
Al agregar lo siguiente al método de apply , básicamente estamos haciendo esto:
• Si no hay una propiedad scalaVersion definida en el proyecto, agregamos una con los
valores predeterminados.
• de lo contrario, obtenemos el existente como instancia de ScalaVersion , para usarlo aún
más
Esto es equivalente a escribir lo siguiente en el archivo de compilación del proyecto que aplica el
complemento:
https://riptutorial.com/es/home 181
ext {
scalaVersion.major = "2.12"
scalaVersion.minor = "0"
project.getDependencies.add("compile", s"org.scala-lang:scala-
library:${scalaVersion.major}.${scalaVersion.minor}")
Esto es equivalente a escribir lo siguiente en el archivo de compilación del proyecto que aplica el
complemento:
compile "org.scala-lang:scala-library:2.12.0"
Nota : el SourceSetContainer tiene información sobre todos los directorios de origen presentes en
el proyecto. Lo que hace el complemento Gradle Scala es agregar los conjuntos de fuente
adicionales a los de Java, como se puede ver en los documentos del complemento .
project.getTasks.create("createDirs", classOf[CreateDirs])
https://riptutorial.com/es/home 182
Al final, su clase ScalaCustomPlugin debería verse así:
project.getDependencies.add("compile", s"org.scala-lang:scala-
library:${scalaVersion.major}.${scalaVersion.minor}")
project.getTasks.create("createDirs", classOf[CreateDirs])
}
}
Cada complemento de Gradle tiene un id que se utiliza en la declaración de apply . Por ejemplo,
al escribir lo siguiente en el archivo de compilación, se traduce en un desencadenante para
Gradle para encontrar y aplicar el complemento con id scala .
De la misma manera, nos gustaría aplicar nuestro nuevo complemento de la siguiente manera,
implementation-class=com.btesila.gradle.plugins.ScalaCustomPlugin
https://riptutorial.com/es/home 183
Después, ejecute de nuevo la gradle install .
Usando el plugin
1. cree un nuevo proyecto Gradle vacío y agregue lo siguiente al archivo de compilación
buildscript {
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
//modify this path to match the installed plugin project in your local repository
classpath 'com.btesila:working-with-gradle:1.0-SNAPSHOT'
}
}
repositories {
mavenLocal()
mavenCentral()
}
2. ejecute gradle createDirs - ahora debería tener todos los directorios de origen generados
3. reemplaza la versión de Scala agregando esto al archivo de compilación:
ext {
scalaVersion.major = "2.11"
scalaVersion.minor = "8"
}
println(project.ext.scalaVersion.major)
println(project.ext.scalaVersion.minor)
4. agregue una biblioteca de dependencias que sea compatible con los binarios de la versión
Scala
dependencies {
compile withScalaVersion("com.typesafe.scala-logging:scala-logging:3.5.0")
}
¡Eso es! Ahora puede usar este complemento en todos sus proyectos sin repetir el mismo texto
tradicional.
https://riptutorial.com/es/home 184
Capítulo 58: Tuplas
Observaciones
¿Por qué las tuplas se limitan a la longitud 23?
Las tuplas son reescritas como objetos por el compilador. El compilador tiene acceso a Tuple1
través de Tuple22 . Este límite arbitrario fue decidido por los diseñadores de idiomas.
Examples
Creando un nuevo tuple
Una tupla es una colección heterogénea de dos a veintidós valores. Una tupla se puede definir
utilizando paréntesis. Para tuplas de tamaño 2 (también llamadas 'par') hay una sintaxis de flecha.
x es una tupla de tamaño dos. Para acceder a los elementos de una tupla use ._1 , a través de
._22 . Por ejemplo, podemos usar x._1 para acceder al primer elemento de la tupla x . x._2 accede
al segundo elemento. Más elegantemente, puedes usar extractores de tuplas .
La sintaxis de la flecha para crear tuplas de tamaño dos se usa principalmente en Mapas, que son
colecciones de pares (key -> value) :
scala> m + x
res0: scala.collection.immutable.Map[Int,String] = Map(2 -> world, 1 -> hello)
scala> (m + x).toList
res1: List[(Int, String)] = List((2,world), (1,hello))
La sintaxis para el par en el mapa es la sintaxis de la flecha, dejando claro que 1 es la clave y a
es el valor asociado con esa clave.
https://riptutorial.com/es/home 185
Las tuplas se usan a menudo dentro de las colecciones, pero deben manejarse de una manera
específica. Por ejemplo, dada la siguiente lista de tuplas:
Puede parecer natural agregar los elementos juntos mediante el desempaquetado implícito de la
tupla:
Scala no puede desempaquetar implícitamente las tuplas de esta manera. Tenemos dos opciones
para arreglar este mapa. El primero es usar los _1 posicionales _1 y _2 :
La otra opción es usar una declaración de case para descomprimir las tuplas usando un patrón de
coincidencia:
Estas restricciones se aplican a cualquier función de orden superior aplicada a una colección de
tuplas.
https://riptutorial.com/es/home 186
Capítulo 59: Unidades de manipulación
(medidas)
Sintaxis
• Medidor de clase (medidores de val: doble) extiende AnyVal
• tipo metro = doble
Observaciones
Se recomienda usar clases de valor para las unidades o una biblioteca dedicada para ellas.
Examples
Tipo de alias
Este enfoque simple tiene serios inconvenientes para el manejo de la unidad, ya que cualquier
otro tipo que sea Double será compatible con él:
Todas las compilaciones anteriores, por lo que en este caso las unidades solo se pueden usar
para marcar los tipos de entrada / salida para los lectores del código (solo la intención).
Clases de valor
Las clases de valor proporcionan una forma segura para el tipo de codificación de unidades,
incluso si requieren un poco más de caracteres para usarlas:
Al extender AnyVal s, no hay una penalización de tiempo de ejecución por usarlos, en el nivel JVM,
esos son tipos primitivos regulares ( Double s en este caso).
https://riptutorial.com/es/home 187
En caso de que desee generar automáticamente otras unidades (como Velocity aka
MeterPerSecond ), este enfoque no es el mejor, aunque hay bibliotecas que también se pueden usar
en esos casos:
• Squants
• unidades
• ScalaQuantity
https://riptutorial.com/es/home 188
Capítulo 60: Var, Val y Def
Observaciones
Como los val son semánticamente estáticos, se inicializan "en el lugar" donde aparecen en el
código. Esto puede producir un comportamiento sorprendente e indeseable cuando se usa en
clases abstractas y rasgos.
Por ejemplo, digamos que nos gustaría hacer un rasgo llamado PlusOne que defina una operación
de incremento en un Int envuelto. Dado que los Int s son inmutables, el valor más uno se conoce
en la inicialización y nunca se cambiará después, por lo que semánticamente es un valor val . Sin
embargo, definirlo de esta manera producirá un resultado inesperado.
trait PlusOne {
val i:Int
val incr = i + 1
}
No importa cuál es el valor i se construye IntWrapper con, llamando .incr en el objeto devuelto
siempre devuelve 1. Esto es porque el val incr es inicializado en el rasgo, antes de la clase que
se extiende, y en ese momento i sólo tiene el valor por defecto de 0 . (En otras condiciones,
puede completarse con Nil , null o un valor predeterminado similar).
La regla general, entonces, es evitar usar val en cualquier valor que dependa de un campo
abstracto. En su lugar, use lazy val , que no evalúa hasta que se necesita, o def , que evalúa
cada vez que se llama. Sin embargo, tenga en cuenta que si el valor de lazy val es forzado a
evaluar por un val antes de que se complete la inicialización, ocurrirá el mismo error.
Un violín (escrito en Scala-Js, pero se aplica el mismo comportamiento) se puede encontrar aquí.
Examples
Var, Val y Def
var
Una var es una variable de referencia, similar a las variables en lenguajes como Java. Se pueden
asignar diferentes objetos a una var libremente, siempre que el objeto dado tenga el mismo tipo
con el que se declaró la var :
scala> var x = 1
x: Int = 1
https://riptutorial.com/es/home 189
scala> x = 2
x: Int = 2
Observe en el ejemplo anterior el tipo de la var fue inferida por el compilador dada la primera
asignación de valor.
val
Un val es una referencia constante. Por lo tanto, no se puede asignar un nuevo objeto a un val
que ya se ha asignado.
scala> val y = 1
y: Int = 1
scala> y = 2
<console>:12: error: reassignment to val
y = 2
^
Sin embargo, el objeto al que apunta un val no es constante. Ese objeto puede ser modificado:
scala> arr(0) = 1
scala> arr
res1: Array[Int] = Array(1, 0)
def
Una def define un método. Un método no puede ser reasignado a
scala> def z = 1
z: Int
scala> z = 2
<console>:12: error: value z_= is not a member of object $iw
z = 2
^
En los ejemplos anteriores, val y y def z devuelven el mismo valor. Sin embargo, una def se
evalúa cuando se llama , mientras que una val o var se evalúa cuando se asigna . Esto puede
https://riptutorial.com/es/home 190
resultar en un comportamiento diferente cuando la definición tiene efectos secundarios:
scala> a + 1
res2: Int = 2
scala> b + 1
Hi
res3: Int = 2
Funciones
Como las funciones son valores, se pueden asignar a val / var / def s. Todo lo demás funciona de
la misma manera que arriba:
scala> x(1)
res0: String = value=1
scala> y(2)
res1: String = value=2
scala> z(3)
res2: String = value=3
Perezoso val
lazy vales una función de lenguaje en la que la inicialización de un val se retrasa hasta que se
accede por primera vez. Después de ese punto, actúa como un val normal.
Para usarlo agregue la palabra clave lazy antes de val . Por ejemplo, usando el REPL:
https://riptutorial.com/es/home 191
| "my bar value"
| }
Initializing bar
bar: String = my bar value
scala> foo
Initializing
res3: String = my foo value
scala> bar
res4: String = my bar value
scala> foo
res5: String = my foo value
Este ejemplo demuestra el orden de ejecución. Cuando el lazy val se declara, todo lo que se
guarda en el foo valor es una llamada a la función vago que no ha sido evaluado. Cuando el
normal val se establece, vemos el println llamada de ejecutar y se le asigna el valor a bar .
Cuando evaluamos foo la primera vez, vemos println ejecuta println , pero no cuando se evalúa
la segunda vez. De manera similar, cuando se evalúa la bar , no vemos que se ejecute println ,
solo cuando se declara.
Veamos un ejemplo con dos objetos que deben declararse al mismo tiempo durante la
creación de instancias:
object comicBook {
def main(args:Array[String]): Unit = {
gotham.hero.talk()
gotham.villain.talk()
}
}
https://riptutorial.com/es/home 192
class Supervillain(val name: String) {
lazy val toKill = gotham.hero
def talk(): Unit = {
println(s"Let me loosen up Gotham a little bit ${toKill.name}!")
}
}
object gotham {
val hero: Superhero = new Superhero("Batman")
val villain: Supervillain = new Supervillain("Joker")
}
Sin la palabra clave lazy , los objetos respectivos no pueden ser miembros de un objeto. La
ejecución de dicho programa daría lugar a una java.lang.NullPointerException . Mediante el
uso lazy , la referencia se puede asignar antes de que se inicialice, sin temor a tener un
valor sin inicializar.
Sobrecarga def
Esto funciona de la misma manera ya sea dentro de clases, rasgos, objetos o no.
Al invocar una def , los parámetros pueden asignarse explícitamente por nombre. Si lo hace,
significa que no necesitan ser ordenados correctamente. Por ejemplo, defina printUs() como:
Esto hace que se impriman one, two, three en todos los casos.
https://riptutorial.com/es/home 193
Si no se nombran todos los argumentos, los primeros argumentos se comparan por orden. Ningún
argumento posicional (sin nombre) puede seguir a uno nombrado:
https://riptutorial.com/es/home 194
Capítulo 61: Variación de tipo
Examples
Covarianza
El símbolo + marca un parámetro de tipo como covariante . Aquí decimos que "El Producer es
covariante en A ":
trait Producer[+A] {
def produce: A
}
Un parámetro de tipo covariante puede considerarse como un tipo de "salida". Marcar A como
covariante afirma que el Producer[X] <: Producer[Y] siempre que X <: Y Por ejemplo, un
Producer[Cat] es un Producer[Animal] válido, ya que todos los gatos producidos también son
animales válidos.
trait Co[+A] {
def produce: A
def handle(a: A): Unit
}
Un enfoque para lidiar con esta restricción es usar parámetros de tipo limitados por el parámetro
de tipo covariante. En el siguiente ejemplo, sabemos que B es un supertipo de A Por lo tanto, dada
la Option[X] <: Option[Y] para X <: Y , sabemos que la Option[X] def getOrElse[B >: X](b: => B): B
puede aceptar cualquier supertipo de X - que incluye los supertipos de Y como lo requiere la
Option[Y] :
trait Option[+A] {
def getOrElse[B >: A](b: => B): B
}
Invariancia
Por defecto, todos los parámetros de tipo son invariantes. Dado el trait A[B] , decimos que " A es
invariante en B ". Esto significa que, dadas las dos parametrizaciones A[Cat] y A[Animal] , no
afirmamos que exista una relación de subclase / superclase entre estos dos tipos; no sostiene
que A[Cat] <: A[Animal] ni que A[Cat] >: A[Animal] independientemente de la relación entre el Cat
y el Animal .
https://riptutorial.com/es/home 195
Las anotaciones de varianza nos proporcionan un medio para declarar tal relación e impone
reglas sobre el uso de parámetros de tipo para que la relación siga siendo válida.
Contravarianza
El símbolo - marca un parámetro de tipo como contravariante - aquí decimos que "El Handler es
contravariante en A ":
trait Handler[-A] {
def handle(a: A): Unit
}
trait Contra[-A] {
def handle(a: A): Unit
def produce: A
}
Sin embargo, tenga en cuenta que, a los fines de la resolución de sobrecarga, la contravarianza
también invierte de forma contraintuitiva la especificidad de un tipo en el parámetro de tipo
contravariante: se considera que " Handler[Animal] es" más específico "que Handler[Cat] .
Como no es posible sobrecargar los métodos en los parámetros de tipo, este comportamiento
generalmente solo se vuelve problemático al resolver argumentos implícitos. En el siguiente
ejemplo, nunca se usará ofCat , ya que el tipo de retorno de ofAnimal es más específico:
implicitly[Handler[Cat]].handle(new Cat)
Este comportamiento está actualmente programado para cambiar en punto y es por eso que
(como ejemplo) scala.math.Ordering es invariante en su tipo de parámetro T Una solución es hacer
que su clase de tipo sea invariante, y parametrizar la definición implícita en el caso de que quiera
que se aplique a las subclases de un tipo dado:
trait Person
object Person {
implicit def ordering[A <: Person]: Ordering[A] = ???
}
https://riptutorial.com/es/home 196
Covarianza de una colección.
Debido a que las colecciones son típicamente covariantes en su tipo de elemento *, se puede
pasar una colección de un subtipo donde se espera un supertipo:
object Animal {
def printAnimalNames(animals: Seq[Animal]) = {
animals.foreach(animal => println(animal.name))
}
}
Animal.printAnimalNames(myDogs)
// Curly
// Larry
// Moe
Puede que no parezca magia, pero el hecho de que un Seq[Dog] sea aceptado por un método que
espera un Seq[Animal] es el concepto completo de un tipo de tipo superior (aquí: Seq ) que es
covariante en su parámetro de tipo.
También hay una forma de que un solo método acepte un argumento covariante, en lugar de
tener todo el rasgo covariante. Esto puede ser necesario porque le gustaría usar T en una
posición contravariante, pero aún así es covariante.
trait LocalVariance[T]{
/// ??? throws a NotImplementedError
def produce: T = ???
// the implicit evidence provided by the compiler confirms that S is a
// subtype of T.
def handle[S](s: S)(implicit evidence: S <:< T) = {
// and we can use the evidence to convert s into t.
val t: T = evidence(s)
???
}
}
trait A {}
trait B extends A {}
object Test {
val lv = new LocalVariance[A] {}
https://riptutorial.com/es/home 197
Lea Variación de tipo en línea: https://riptutorial.com/es/scala/topic/1651/variacion-de-tipo
https://riptutorial.com/es/home 198
Capítulo 62: Zurra
Sintaxis
• aFunction (10) _ // Using '_' Le dice al compilador que todos los parámetros en el resto de
los grupos de parámetros serán procesados.
• nArityFunction.curried // Convierte una función n-arity a una versión con currículum
equivalente
• anotherFunction (x) (_: String) (z) // Currying un parámetro arbitrario. Necesita su tipo
explícitamente establecido.
Examples
Un multiplicador configurable como función de curry.
switchBetween3AndE(true) // "3"
switchBetween3AndE(false) // "E"
numberMinus5(7) // 2
fiveMinusNumber(7) // -2
https://riptutorial.com/es/home 199
Zurra
La add curry lo transforma en una función que toma un Int y devuelve una función (de un Int a
un Int )
Puede aplicar este concepto a cualquier función que tome múltiples argumentos. El curry de una
función que toma múltiples argumentos, la transforma en una serie de aplicaciones de funciones
que toman un argumento:
val x = add3Curr(1)(2)(42)
Zurra
Concretamente, en términos de tipos de escala, en el contexto de una función que toma dos
argumentos, (tiene aridad 2) es la conversión de
val f: (A, B) => C // a function that takes two arguments of type `A` and `B` respectively
// and returns a value of type `C`
val curriedF: A => B => C // a function that take an argument of type `A`
// and returns *a function*
// that takes an argument of type `B` and returns a `C`
Así que para las funciones arity-2 podemos escribir la función curry como:
https://riptutorial.com/es/home 200
}
Uso:
1. Puede escribir funciones al curry como métodos. así que curriedF se puede escribir como:
2. Puede deshacer el curry (es decir, pasar de A => B => C a (A, B) => C ) utilizando un método
de biblioteca estándar: Function.uncurried
El curry es la técnica de traducir la evaluación de una función que toma múltiples argumentos
para evaluar una secuencia de funciones, cada una con un solo argumento .
Ejemplo 1
Supongamos que el ingreso anual total es una función compuesta por el ingreso y una
bonificación:
Tenga en cuenta en la definición anterior que el tipo también se puede ver / escribir como:
https://riptutorial.com/es/home 201
Supongamos que la porción del ingreso anual se conoce de antemano:
partialTotalYearlyIncome(100)
Ejemplo 2
class CarWheelsFactory {
def applyCarWheels(carManufacturing:(String,String) => String): String => String =
carManufacturing.curried("applied wheels..")
}
class CarBodyFactory {
def applyCarBody(partialCarWithWheels: String => String): String =
partialCarWithWheels("applied car body..")
}
Tenga en cuenta que la CarWheelsFactory anterior aplica la función de fabricación del automóvil y
solo aplica las ruedas.
Lo que tenemos es una lista de tarjetas de crédito y nos gustaría calcular las primas de todas las
tarjetas que la compañía de tarjetas de crédito tiene que pagar. Las primas dependen del número
total de tarjetas de crédito, por lo que la empresa las ajusta en consecuencia.
Ya tenemos una función que calcula la prima de una sola tarjeta de crédito y tiene en cuenta el
total de tarjetas que la compañía ha emitido:
https://riptutorial.com/es/home 202
case class CreditCard(creditInfo: CreditCardInfo, issuer: Person, account: Account)
object CreditCard {
def getPremium(totalCards: Int, creditCard: CreditCard): Double = { ... }
}
Ahora, un enfoque razonable para este problema sería asignar cada tarjeta de crédito a una prima
y reducirla a una suma. Algo como esto:
object CreditCard {
def getPremium(totalCards: Int)(creditCard: CreditCard): Double = { ... }
}
https://riptutorial.com/es/home 203
Creditos
S.
Capítulos Contributors
No
Biblioteca de
5 dmitry, HTNW
continuaciones
Colecciones
11 Nathaniel Ford, Shuklaswag
paralelas
Combinadores de
12 Nathaniel Ford
analizador
https://riptutorial.com/es/home 204
14 Corrientes jwvh, Nathaniel Ford, Oleg Pyzhcov
15 Cuasiquotes gregghz
Expresiones
17 dmitry, J Cracknell, Nathaniel Ford
regulares
Función de orden
19 acjay, ches, Nathaniel Ford, nukie, Rajat Jain, Srini
superior
Funciones definidas
21 por el usuario para Camilo Sampedro
Hive
Inyección de
29 Hoang Ong
dependencia
https://riptutorial.com/es/home 205
ipoteka, John, Muki, Nathaniel Ford, pedrorijo91, suj1th, void,
30 JSON
Wogan, zoitol
Programación a nivel
41 J Cracknell
de tipo
Pruebas con
42 Andrzej Jozwik
ScalaCheck
Pruebas con
43 Nadim Bahadoor, Nathaniel Ford
ScalaTest
https://riptutorial.com/es/home 206
48 Scaladoc Camilo Sampedro, Gábor Bakos, Nathaniel Ford
49 Scalaz chengpohi
Sobrecarga del
53 corvus_192, implicitdef, inzi, mnoronha, Nathaniel Ford, Simon
operador
Tipo de
54 parametrización akauppi, Andy Hayden, Eero Helenius, Nathaniel Ford, vivek
(genéricos)
Tipos de métodos
55 abstractos únicos Gábor Bakos, Gabriele Petronella, Nathaniel Ford
(tipos SAM)
Trabajando con
56 datos en estilo Filippo Vitale
inmutable.
Trabajando con
57 Bianca Tesila, Nathaniel Ford, Rjk
gradle
Unidades de
59 manipulación Gábor Bakos
(medidas)
https://riptutorial.com/es/home 207