Skip to content

Instantly share code, notes, and snippets.

@rbonvall
Last active April 5, 2016 13:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rbonvall/d11b21a259ff498c2f88041904f15507 to your computer and use it in GitHub Desktop.
Save rbonvall/d11b21a259ff498c2f88041904f15507 to your computer and use it in GitHub Desktop.
Charla «Unos tipos con opciones».
// Unos tipos con opciones //
// //
// Roberto Bonvallet //
// @rbonvall //
// //
// Viernes 1 de abril de 2016 //
// Santiago Scala Meetup //
// El tipo de retorno de esta función
// no dice todo lo que la función podría hacer:
def f(x: Double): Double = {
println(x)
throw new Exception("...")
Request("http://deep.web/córneas")
null
}
// Existen tipos que sí describen
// los distintos efectos que puede tener un valor:
def f(x: Double): Option[Double] // podría no existir;
def f(x: Double): List[Double] // podría ser más de uno;
def f(x: Double): Try[Double] // podría fallar;
def f(x: Double): Try[Option[Double]] // podría fallar o no existir;
def f(x: Double): Future[Double] // podría demorarse y fallar;
def f(x: Double): Observable[Double] // permite suscribirse;
def f(x: Double): IO[Double] // interactúa con el mundo externo (en lenguajes como Haskell).
// El tipo Option[T] representa un valor que podría o no existir.
// Obliga al programador a considerar ambos casos explícitamente.
def f(x: Double): Option[Double]
val y = f(9.99) match {
case Some(value) ⇒ 3.14 * value
case None ⇒ 1.23
}
// Por ejemplo, el metodo .find de las listas retorna un Option:
val xs = List(17, 24, 33, 1, 19, 24)
xs.find(_ % 3 == 0) // → Some(24)
xs.find(_ % 7 == 0) // → None
// Un método conveniente de los Option es .getOrElse,
// que permite obtener un valor con seguridad:
xs.find(_ % 3 == 0).getOrElse(10) // → 24
xs.find(_ % 7 == 0).getOrElse(10) // → 10
// Ejemplo: encontrar una fecha para hacer una fiesta
val partyDay: Date =
myFriends.find { f ⇒ f.birthday.month == 4 } // Buscar algún amigo que tenga cumpleaños en abril.
.map { f ⇒ f.birthday.copy(year=2016) } // Si lo hay, tomar su fecha de nacimiento y cambiarla a este año
.getOrElse(Date(2016, 4, 30)) // y si no lo hay, retornar el 30 de abril de este año.
// .fold es un método más type-safe de hacer .map+.getOrElse
val partyDay: Date =
myFriends.find { f ⇒ f.birthday.month == 4 }
.fold ((Date(2016, 4, 30))) { f ⇒ f.birthday.copy(year=2016) }
// Creemos una clase Cardinality, que representa cuántas veces un elemento puede existir.
// Siempre hay un límite inferior (por lo menos cero veces),
// pero podría no haber un límite superior (algo podría aparecer un número ilimitado de veces).
case class Cardinality(min: Int, max: Option[Int])
// Algunos requerimientos son:
// el mínimo no puede ser negativo,
// el máximo (si existe) no puede ser menor que 1,
// el máximo (si existe) no puede ser menor que el mínimo.
case class Cardinality(min: Int, max: Option[Int]) {
require(min >= 0)
max match {
case Some(m) ⇒
require(m >= 1)
require(m >= min)
case None ⇒
}
}
// Usando métodos del tipo Option podemos reescribir el pattern match más claramente.
// Aunque en este caso es seguro (pues verificamos antes si es vacío),
// siempre es mejor evitar usar max.get.
case class Cardinality(min: Int, max: Option[Int]) {
require(min >= 0)
require(max.isEmpty || max.get >= 0)
require(max.isEmpty || max.get >= min)
}
// max.isEmpty es lo mismo que !max.isDefined
// El método .forall permite reescribir la condición anterior
// de manera completamente segura:
case class Cardinality(min: Int, max: Option[Int]) {
require(min >= 0)
require(max.forall(_ >= 1))
require(max.forall(_ >= min))
}
// Agreguemos una representación glamorosa en string para nuestra clase:
case class Cardinality(min: Int, max: Option[Int]) {
require(min >= 0)
require(max.forall(_ >= 1))
require(max.forall(_ >= min))
override def toString = max match {
case Some(m) if m == min ⇒ s"Cardinality($min)"
case Some(m) ⇒ s"Cardinality($min, $m)"
case None ⇒ s"Cardinality($min, ∞)"
}
}
// Nuevamente, podemos reescribir el pattern match usando métodos de Option:
case class Cardinality(min: Int, max: Option[Int]) {
require(min >= 0)
require(max.forall(_ >= 1))
require(max.forall(_ >= min))
override def toString =
if (max.contains(min)) s"Cardinality($min)"
else s"Cardinality($min, ${max.getOrElse("∞")})"
}
// Ejemplos de uso de nuestra clase:
println(Cardinality(3, Some(3))) // → Cardinality(3)
println(Cardinality(3, Some(5))) // → Cardinality(3, 5)
println(Cardinality(3, None)) // → Cardinality(3, ∞)
println(Cardinality(3, Some(2))) // → IllegalArgumentException
println(Cardinality(-1, None)) // → IllegalArgumentException
println(Cardinality(0, Some(0))) // → IllegalArgumentException
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment