Cristian Spinetta
Software developer. More presentations on account /cspinetta
Cristian Spinetta | Gonzalo Guglielmo
Ejemplo en SCALA
Operaciones elementales
val radius: Double = 2.3;
var color: String = "red";
def area(radius: Double): Double = {
val pi: Double = 3.141592;
val sqRad: Double = radius * radius;
return pi * sqRad;
}
val description: String =
"Area is: " + area(radius);
'return' implícito
val radius: Double = 2.3;
var color: String = "red";
def area(radius: Double): Double = {
val pi: Double = 3.141592;
val sqRad: Double = radius * radius;
pi * sqRad;
}
val description: String =
"Area is: " + area(radius);
Ejemplo en SCALA
Operaciones elementales
val radius: Double = 2.3;
var color: String = "red";
def area(radius: Double): Double = {
val pi: Double = 3.141592;
val sqRad: Double = radius * radius;
return pi * sqRad;
}
val description: String =
"Area is: " + area(radius);
inferencia de tipos
val radius = 2.3;
var color = "red";
def area(radius: Double) = {
val pi = 3.141592;
val sqRad = radius * radius;
pi * sqRad;
}
val description = "Area is: " + area(radius);
Ejemplo en SCALA
Operaciones elementales
val radius: Double = 2.3;
var color: String = "red";
def area(radius: Double): Double = {
val pi: Double = 3.141592;
val sqRad: Double = radius * radius;
pi * sqRad;
}
val description: String =
"Area is: " + area(radius);
val radius = 2.3;
var color = "red";
def area(radius: Double) = {
val pi = 3.141592;
val sqRad = radius * radius;
pi * sqRad;
}
val description = "Area is: " + area(radius);
; opcional
val radius = 2.3
var color = "red"
def area(radius: Double) = {
val pi = 3.141592
val sqRad = radius * radius
pi * sqRad
}
val description = "Area is: " + area(radius)
Ejemplo en SCALA
Operaciones elementales
val radius = 2.3
var color = "red"
def area(radius: Double) = {
val pi = 3.141592
val sqRad = radius * radius
pi * sqRad
}
val description = "Area is: " + area(radius)
{ } opcional
val radius = 2.3
var color = "red"
def area(radius: Double) = 3.141592 * radius * radius
val description = "Area is: " + area(radius)
Ejemplo en SCALA
Operaciones elementales
val radius = 2.3
var color = "red"
def area(radius: Double) = 3.141592 * radius * radius
val description = "Area is: " + area(radius)
String interpolation
val radius = 2.3
var color = "red"
def area(radius: Double) = 3.141592 * radius * radius
val description = s"Area is: ${area(radius)}"
Ejemplo en SCALA
Operaciones elementales
public class User {
private Integer id;
private String name;
private LocalDate birthday;
public User(Integer id, String name,
LocalDate birthday) {
this.id = id;
this.name = name;
this.birthday = birthday;
}
public Integer getId() { return id; }
public void setId(Integer id){ this.id = id;}
// Getter and Setter for rest of fields...
@Override
public boolean equals(Object o) {
// usually a lot of code...
}
@Override
public int hashCode() {
// more code!
}
}
case class User(id: Int,
name: String,
birthday: LocalDate)
JAVA
SCALA
DTO
val status = if (age >= minAge) "adult" else "minor"
if como expresión
Sin sentencias: todo es una expresión
def audit(message: String): Unit = {
auditClient.post(message)
logger.debug("audit with message: $message")
}
Valor Unit
Los valores asignados no cambian
A balanced attitude for Scala programmers
“Prefer vals, immutable objects, and methods without side effects. Reach for them first. Use vars, mutable objects, and methods with side effects when you have a specific need and justification for them.”
scala> val name = "Peter"
name: String = Peter
scala> name = "Paul"
<console>:8: error: reassignment to val
name = "Paul"
^
Variable inmutable
scala> val names = List("Peter", "Paul")
names: List[String] = List(Peter, Paul)
scala> names :+ "Moley"
res0: List[String] = List(Peter, Paul, Moley)
Lista inmutable
Ejemplo: Cuenta bancaria
class BankAccount {
var balance = 0
def deposit(amount: Int) {
if (amount > 0) balance += amount
else balance
}
def withdraw(amount: Int): Int =
if (amount >= 0 && amount <= balance) {
balance -= amount
balance
} else {
println("insufficient funds")
balance
}
}
Con mutabilidad
val account = new BankAccount
account.deposit(30) // return 30
account.deposit(20) // return 20
account.withdraw(10) // return 10
// Current balance: 40
case class BankAccount(balance: Int) {
def deposit(amount: Int) =
if (amount > 0)
BankAccount(balance = balance + amount)
else
this
def withdraw(amount: Int) =
if (amount >= 0 && amount <= balance) {
BankAccount(balance = balance - amount)
} else {
println("insufficient funds")
this
}
}
Con inmutabilidad
BankAccount(0)
.deposit(30) // return a new BankAccount
.deposit(20) // return a new one
.withdraw(10) // return a new one
// Balance in the last BankAccount: 40
Inmutabilidad: No se cambia el valor, se crea uno nuevo.
Funciones puras, valores y composición, nada más!
Implicancias:
val value = 20
(value, value)
Hay Transparencia Referencial si la evaluación de una expresión se puede reemplazar por su resultado y el programa no cambia.
(20, 20)
Ejemplos:
(iter.next(), iter.next())
val value = iter.next()
(value, value)
Toda expresión, o tiene side-effect o tiene transparencia referencial
def compute(i: BigDecimal): Int = {
logger.info("Computing value")
i * 30
}
(compute(2.05), compute(2.05))
val value = compute(2.05)
(value, value)
def addMissingSubject(email: Email) = {
if (email.subject.isEmpty)
email.copy(subject = "No subject")
else email
}
(addMissingSubject(email1),
addMissingSubject(email1))
val value = addMissingSubject(email1)
(value, value)
Son aquellas funciones que reciben funciones como parámetro o retornan funciones como resultado.
class Person(firstName: String, lastName: String, val age: Int) {
def itIsTrueThat(f: Person => Boolean): Boolean = f(this)
}
def isOlderThan30(p: Person): Boolean = p.age > 30
val mirtha = new Person("Mirtha", "Legrand", 9999)
def isOlderThan30(p: Person): Boolean = p.age > 30
def isOlderThan50(p: Person): Boolean = p.age > 50
def isOlderThan5000(p: Person): Boolean = p.age > 5000
def isOlderThan10000(p: Person): Boolean = p.age > 10000
mirtha.itIsTrueThat(isOlderThan30) // true
mirtha.itIsTrueThat(isOlderThan50) // true
mirtha.itIsTrueThat(isOlderThan5000) // true
mirtha.itIsTrueThat(isOlderThan10000) // false
def isOlderThan(n: Int, p: Person): Boolean = p.age > n
Podríamos extraer la edad mínima contra la que queremos comparar:
Pero, ¿ya no podemos pasarla como parámetro directamente?
Aplicar una función con menos parámetros de los que originalmente recibe, devolviendo una nueva función que recibe los parámetros que le faltan y retorna lo que retornaba la función original.
def isOlderThan(n: Int)(p: Person): Boolean = p.age > n
Una función de tipo Int => Person => Boolean
Ejemplo:
val olderThan300: (Person => Boolean) = isOlderThan(300)
La podemos aplicar solo con el parámetro n:
mirtha.itIsTrueThat(olderThan300) // true
Y usamos la nueva función con la persona:
o
mirtha.itIsTrueThat(isOlderThan(300)) // true
Son funciones sin nombre, definidas en línea para ser usadas una única vez.
mirtha.itIsTrueThat(isOlderThan(300)) // true
mirtha.itIsTrueThat((p: Person) => p.age > 300) // true
mirtha.itIsTrueThat(p => p.age > 300) // true con inferencia de tipos
mirtha.itIsTrueThat(_.age > 300) // true con _
List<Person> students = new ArrayList<Person>(new Person("Juan", 9),
new Person("Pepe", 6));
List<Person> nerds = new ArrayList<Person>();
for (Person student : students) {
if (student.getGrade >= 9) {
nerds.add(student);
}
}
return nerds;
Veamos éste código en Java 1.7
Resolvemos el mismo problema en Scala
val students = List(Person("Juan", 9), Person("Pepe", 6))
students.filter(_.grade >= 9)
Declaratividad: ocuparse de qué problema resolver, y no tanto del cómo resolverlo
Expresividad: se refiere a lo auto-descriptivo del código
Se aplica una función a cada elememento de la colección, transformándolo.
List(1,2,3,4).map(elem => s"$elem !")
// List("1 !", "2 !", "3 !", "4 !")
mails.map(_.subject)
Obtengo una nueva colección con los elementos transformados
val unNerd = students.find(_.grade >= 9)
¿De qué tipo es unNerd?
Dado el siguiente código:
Tipo de datos con info extra
Option[T]: Effect para modelar null
Ejemplo:
¿De qué tipo es nerd?
Volviendo al find()...
Option[Person]
students
.find(_.grade >= 9)
.map(nerd => Response.Ok(nerd.name))
.getOrElse(Response.NotFound("no nerd found"))
val nerd = students.find(_.grade >= 9)
val nerd: Option[Person] = students.find(_.grade >= 9)
Ejemplo de uso:
Otro ejemplo: errores
def divide(dividend: Int, divisor: Int):Int = {
if (divisor == 0)
throw new RuntimeException(
"Divisor can't be 0")
dividend / divisor
}
En imperativo usaríamos Exception
En funcional usamos Effect...
def divide(dividend: Int,
divisor: Int):
Either[String, Int] = {
if (divisor == 0)
Left("Divisor can't be 0")
else
Right(dividend / divisor)
}
Es un side-effect
Either[T]: Effect para modelar errores
Es puro (sin side-effect)
Tiene la forma F[T]
type F[T] = Option[T]
Puede tener un valor T o no
type F[T] = Either[E, T]
Devuelve un error E o un resultado T
type F[T] = List[T]
Genera entre 0 y N valores T
Y muchos otros...
(Para algún E)
Programa en F que computa un valor T
¿Preguntas?
¡ Muchas Gracias !
We are hiring!
http://www.despegar.com/sumate/#
IT Talent Acq: maria.arean@despegar.com
By Cristian Spinetta
Introducimos el lenguaje de programación Scala y las bases de la programación funcional, tomando como ejemplo casos de uso real, en vez de académicos.