====== Scala ======
===== Herramientas =====
* {{:functional:scala:sbt.tgz|sbt}}
* [[http://www.typesafe.com/stack/downloads/scala-ide|IDE de Scala]]
==== Uso ====
=== sbt ===
* Es la herramienta de compilación de Scala.
* Para agregarlo al path, en Linux, añadiremos la siguiente línea al final del .bashrc:
export PATH=/home/alfred/bin/sbt/bin:$PATH
* Una vez lo ejecutemos, la carpeta donde lo hagamos será el directorio del proyecto.
* Existen los siguientes comandos:
* ''console'', empieza el REPL (Scala interprete), para salir de él ''ctrl+d''.
* ''compile'', compila el código en ese directorio.
* ''run'', ejecutará el código que hay en el directorio del proyecto si este tiene un método ''main''.
=== Scala IDE ===
* Podemos crear una worksheet (botón derecho y añadir worksheet), esto es código Scala que se evalua a tiempo real cada vez que se guarda el fichero.
object test {
println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet
val x = 4 //> x : Int = 4
def inc (i: Int) = i + 1 //> inc: (i: Int)Int
inc(x)*5 //> res0: Int = 25
}
* ''ctrl+shift+f'' formatea el código.
===== Conceptos =====
==== Conceptos básicos ====
=== Vocabulario ===
* **Clases**, el concepto es parecido al de la POO. Contienen propiedades y métodos, como en Java, pueden inicializarse a partir del ''new'' y de un constructor y pueden haber diversas instancias (u objetos) de la misma clase.
* **Traits**, son parecidas a las interfaces de la POO aunque también pueden contener implementaciones de métodos o definiciones de campos.
* **Objetos**, son como clases en POO pero la diferencia es que únicamente existe una instancia.
* **Paquetes**, Haciendo ''package foo.bar'' al principio de un fichero de código diremos que ese fichero pertenece al paquete ''foo.bar'' al cual, para importarlo, será necesario hacer ''import foo.bar._'' o haciendo ''foo.bar.MyClass'' si quiere importar una clase concreta o ''import.bar.baz._'' si queremos importar el objeto ''baz''.
=== El main... ===
En Scala hay dos formas de indicar un punto de entrada a un programa, aunque siempre será a partir de un objeto... \\
Puede hacerse extendiendo el objeto de ''App''...
object HelloWorld extends App {
println("Hello, World!")
}
... O añadiendo a un objeto el método ''def main(args: Array[String])'':
object HelloWorld {
def main(args: Array[String]) {
println("Hello, World!")
}
}
=== Sintaxis de código ===
Definir un valor llamado ''radious'':
def radious = 10
Definir una expresión:
def sumOfSquares(x: Double, y: Double) = square(x) + square(y)
Definir el tipo de retorno de una expresión:
def sumOfSquares(x: Double, y: Double): Double = ...
Para evaluar una exprisión por nombre, en vez de por valor se usa ''=>'':
def constOne(x:Int, y: => Int) = 1
También puedes usar expresiones ''if-else'' de la siguiente forma:
def abs(x:Int) = if (x >= 0) x else -x
Tabla de operadores booleanos:
true && e --> e
false && e --> false
true || e --> true
false || e --> e
''def'' significa "por nombre", ''val'' por valor. Por nombre se genera al evaluar esa expresión, por valor cuando se utiliza:
def loop: Boolean = loop
def x = loop // would die when it is used
val x = loop // would die here
Podemos hacer expresiones multilínea usando ''|'':
def and(x:Boolean, y:Boolean) =
| if (x) y else false
Otra opción para hacer expreiones multilínea es usando paréntesis:
(someLongExpression1
+ someLongExpression2)
O, una última opción de expreiones multilínea es el uso de operadores:
someLongExpression1 +
someLongExpression2
Los bloques se delimitan por ''{ ... }'' y las definiciones existentes dentro de un bloque son visibles dentro de este, además ocultan las definiciones de fuera. \\
Semicolons are optional in most cases. We can put several expression in one line:
El punto y coma es opcional en muchos casos, aún así nos permite para añadir más expresiones en una sola línea:
val y = 1; x + x
=== Listas ===
Un objeto List contiene los siguientes métodos:
* ''.isEmpty'': Indica si la lista está vacía.
* ''.head'': Devuelve el primer elemento.
* ''.tail'': Devuelve el resto de elementos distintos al primero.
def sum(xs: List[Int]): Int = {
if (xs.isEmpty) 0
else xs.head + sum(xs.tail)
}
def max(xs: List[Int]): Int = {
if (xs.isEmpty) throw new java.util.NoSuchElementException
if (!xs.tail.isEmpty) {
val m = max(xs.tail)
if (xs.head > m) xs.head
else m
} else xs.head
}
=== Excepciones ===
if (xs.isEmpty) throw new java.util.NoSuchElementException
==== Montar tests ====
* {{:functional:scala:test_jars.tar.gz|Jars para tests}}
==== Funciones y evaluación ====
Sintaxis de definición:
def nombre (par_name : tipo) : tipo_return = { ... }
Para indicar en Scala que un parámetro (en este caso el segundo) se evaluará por nombre, haremos:
def first (x: Double, y: => Double) = x
=== Diferencia entre evaluación por nombre y por valor ===
Teniendo...
square (x: Double) : Double = x * x
sumOfSquares (x: Double, y: Double) : Double = square(x) + square(y)
Por valor cada argumento se evalua una vez:
sumOfSquares(3, 2+2)
sumOfSquares(3, 4)
square(3) + square(4)
3 * 3 + square(4)
9 + square(4)
9 + 4 * 4
9 + 16
25
Por nombre cada argumento se evalua cuando es llamado:
sumOfSquares(3, 2+2)
square(3) + square(2+2)
3 * 3 + square(2+2)
9 + square(2+2)
9 + (2+2) * (2+2)
9 + 4 * (2+2)
9 + 4 * 4
25
Si tuviesemos la siguientes expresiones
def loop () = loop
def first (x: Double, y: Double) = x
first(x, loop)
Si evaluamos la tercera por nombre terminaría pero si la evaluamos por valor cae en bucle infinito. \\
Cuando definimos usando ''val'' lo hacemos haciéndolo por valor. Cuando lo hacemos usando ''def'' lo hacemos por nombre:
def loop () : Boolean = loop
def x = loop
val x = loop // bucle infinito
==== Notas ====
* Cuando tenemos una función recursiva necesitamos indicar su valor devuelto.