Treviso Scala Group - 24th september 2015
Giampaolo Trapasso
@supergiampaolo (there aren't enough heros these days!)
cosenonjaviste.it (ask for a sticker!)
blogzinga.it(have a blog? do a PR!)
https://youtu.be/DJLDF6qZUX0
Scala OO+FP nella misura che voglio, Tre cose JVM, JDK+Librerie+Linguaggio (il più debole
concisività e semplicità C++->Java->Scala
Java: verboso, non veramente OOP
Tre cose JVM, JDK+Librerie+Linguaggio (il più debole)
public class JavaPizza { private Boolean tomato = true; private Boolean cheese = true; private Boolean ham = true; private String size = "NORMAL"; public JavaPizza(Boolean tomato, Boolean cheese, Boolean ham, String size) { this.setTomato(tomato); this.setCheese(cheese); this.setHam(ham); this.setSize(size); }
public Boolean getTomato() { return tomato; } public void setTomato(Boolean tomato) { this.tomato = tomato; } public Boolean getCheese() { return cheese; } public void setCheese(Boolean cheese) { this.cheese = cheese; }
public Boolean getHam() { return ham; } public void setHam(Boolean ham) { this.ham = ham; } public String getSize() { return size; } public void setSize(String size) { this.size = size; } }
class Pizza(var tomato: Boolean = true, var cheese: Boolean = true, var ham: Boolean = true, var size: String = "NORMAL")
val pizza = new Pizza(true, false, true, "HUGE") // type inference
is the same as
val pizza : Pizza = new Pizza(true, false, true, "HUGE")
val smallSize = "SMALL" // type inference again val newTomato = false // and again pizza.size = smallSize pizza.tomato =newTomato
val pizza1 = new Pizza(true, true, false, "NORMAL") val pizza2 = new Pizza(true, true) val pizza3 = new Pizza(tomato = true, ham = true, size = "HUGE", cheese = true,) val pizza4 = new Pizza(size = "SMALL")
In the following, we'll use only immutable pizzas
class Pizza(val tomato: Boolean = true, val cheese: Boolean = true, val ham: Boolean = true, val size: String = "NORMAL")
Think about how much Java code you saved..
var secondPizza = new Pizza(true, true, true, "NORMAL") secondPizza = new Pizza(false, false, true, "HUGE") // will compile val thirdPizza = new Pizza(true, false, true, "SMALL") thirdPizza = secondPizza // won't compile
class Pizza(val tomato: Boolean = true, val cheese: Boolean = true, val ham: Boolean = true, val size: String = "NORMAL"){ def slice(numberOfPieces: Int): Unit = { println(s"Pizza is in ${numberOfPieces} pieces") } }
object Oven { def cook(pizza: Pizza): Unit = { println(s"Pizza $pizza is cooking") Thread.sleep(1000) println("Pizza is ready") } }
object Margherita extends Pizza(true, false, false, "NORMAL") { override def toString = "Pizza Margherita" }
Oven.cook(Margherita)
case class Pizza(val tomato: Boolean = true, val cheese: Boolean = true, val ham: Boolean = true, val size: String = "NORMAL") { def slice(numberOfPieces: Int): Unit = { println(s"Pizza is in ${numberOfPieces} pieces") } } val p1 = Pizza.apply(cheese = false) // not idiomatic val p2 = Pizza(cheese = false) // the same as val p = new Pizza(cheese = false)
abstract class Worker(val name: String){ def greet : String // this is abstract } trait PizzaMaker{ def preparePizza(pizza: Pizza) = println(s"I prepare ${pizza}") } trait Waiter{ def servePizza(pizza: Pizza) = println(s"this is your ${pizza}") } class Handyman(override val name: String, val wage: Int) extends Worker(name) with PizzaMaker with Waiter { override def greet: String = s"Hello, I'm ${name}" }
val vito = new Handyman("Vito", 1000) println(vito.greet) vito.preparePizza(Margherita) vito.servePizza(Margherita)
abstract class Worker(val name: String){ def greet : String // this is abstract } trait PizzaMaker extends Worker{ def preparePizza(pizza: Pizza) = println(s"I prepare ${pizza}") override def greet = "Hello" } trait Waiter extends Worker { def servePizza(pizza: Pizza) = println(s"this is your ${pizza}") override def greet = "How can I serve you?" } class Handyman(override val name: String, val wage: Int) extends Worker(name) with Waiter with PizzaMaker
abstract class Worker(val name: String){ def greet : String // this is abstract } trait PizzaMaker { def preparePizza(pizza: Pizza) = println(s"I prepare ${pizza}") def greet = "Hello" } trait Waiter { def servePizza(pizza: Pizza) = println(s"this is your ${pizza}") def greet = "How can I serve you?" } class Handyman(override val name: String, val wage: Int) extends Worker(name) with Waiter with PizzaMaker { override def greet = super[Waiter].greet }
abstract class Worker(val name: String){ def greet : String // this is abstract } class Intern trait PizzaMaker extends Intern { def greet = "Hello" } trait Waiter { def greet = "How can I serve you?" } class Handyman(override val name: String, val wage: Int) extends Worker(name) with Waiter with PizzaMaker
object Pino { def comment(pizza: Pizza) = pizza match { case Pizza(tomato, cheese, ham, size) if size == "HUGE" => "Wow!" case Pizza(false, cheese, ham, size) => s"No tomato on this ${size} pizza" case Pizza(_, _, _, "SMALL") => "Little champion, your pizza is coming" case pizza@Margherita => s"I like your ${pizza.size} Margherita" case _ => "OK" } }
val order: List[Pizza] = List(pizza1, pizza2, pizza3, pizza4) val noTomato: (Pizza => Boolean) = (p => p.tomato == false) order .filter(noTomato) .filter(p => p.ham == true) .map(pizza => Pino.comment(pizza)) .map(println)
val bigOrder: (Pizza, Int) = (Pizza(ham = true), 7) val howMany: Int = bigOrder._2 // bigOrder._2 = 4 //don't compile, tuple is immutable val newBigOrder = bigOrder.copy(_2 = 4) val number = List(3, 4, 2, 1) val hugeOrder: List[(Pizza, Int)] = order.zip(number) //List((Pizza(true,true,false,NORMAL),3), (Pizza(true,true,true,NORMAL),4)... val hugeOrder2 = hugeOrder.map(t => t.swap)
case class Pizza(tomato: Boolean = true, cheese: Boolean = true, ham: Boolean = true, size: String = "NORMAL"){ def slice(numberOfPieces: Int) = s"Pizza is in ${numberOfPieces} pieces" def /(numberOfPieces: Int) = slice(numberOfPieces) }
val pizza = Pizza() pizza.slice(4) pizza./(4) pizza / 4
Simple rules to simplify syntax (towards DSL)
class MargheritaList(val n: Int) { def margheritas = { var list: List[Pizza] = List() for (i <- 1 to n) list = list :+ Margherita list } }
val order = new MargheritaList(4).margheritas()
a bit verbose..let's introduce a implicit conversion
implicit def fromIntToMargherita(n: Int) = new MargheritaList(n)
compiler implicity does conversion for us
val order = 4.margheritas
or better
val order = 4 margheritas
Hint: use import scala.language.implicitConversions and import scala.language.postfixOps to get rid of warning or to raise attention
val pizza = Margherita Pino commentPizza pizza Oven cook pizza pizza / 6
Question. How much keywords do you see?
Let's change slice definition
case class Slice(val p: Pizza, val fractions: Int) { override def toString = s"1/$fractions of $p" } case class Pizza(val tomato: Boolean = true, val cheese: Boolean = true, val ham: Boolean = true, val size: String = "NORMAL") { def slice(n: Int): List[Slice] = List.fill(n)(Slice(this, n)) }
val order = List(Pizza(), Pizza(ham = false))
How to get the list of every slice description?
val list = order.map(p => p.slice(4)) // List[List[Slice]] is wrong val slices = order.flatMap(p => p.slice(4)) // List[Slice] val descriptions = listOfSlices.map(slice => slice.toString)
or
val descriptions = order.flatMap(p => p.slice(4)).map(slice => slice.toString)
For comprehension may be more readable
val descriptions = for { pizza <- order slice <- pizza.slice(4) } yield slice.toString
than
val descriptions = order.flatMap(p => p.slice(4)).map(slice => slice.toString)
Q & Option[A]
Thanks for following, slides and code on https://github.com/giampaolotrapasso/