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/