Scala 101 first steps with Scala – Why do we need a new language? – Show me the code"basic features"



Scala 101 first steps with Scala – Why do we need a new language? – Show me the code"basic features"

0 0


scala101firststepswithscala


On Github giampaolotrapasso / scala101firststepswithscala

Scala 101 first steps with Scala

Treviso Scala Group - 24th september 2015

Hello, my name is...

Giampaolo Trapasso

@supergiampaolo (there aren't enough heros these days!)

cosenonjaviste.it (ask for a sticker!)

blogzinga.it(have a blog? do a PR!)

Why do we need a new language?

Well..something happened in last 10~15 years

Cores 1 → 2 → 4 → ... Server nodes 1 → 10's → 1000’s. Response times seconds → milliseconds Maintenance downtimes hours → nones Data volum GBs → TBs and more

The "rise" of the Functional Programming (FP)

  • functions are used as the fundamental building blocks of a program
  • avoids changing-state and mutable data
  • calling a function f twice with the same value for an argument x will produce the same result f(x)
  • easy scaling in and out
  • many deep interesting concepts that don't fit a life.. ehm.. slide ;)

https://youtu.be/DJLDF6qZUX0

A Brief, Incomplete, and Mostly Wrong History of Programming Languages

2003 - A drunken Martin Odersky sees a Reese's Peanut Butter Cup ad featuring somebody's peanut butter getting on somebody else's chocolate and has an idea. He creates Scala, a language that unifies constructs from both object oriented and functional languages. This pisses off both groups and each promptly declares jihad.

Scala - Scalable Language

  • blends Object Oriented and Functional Programming
  • powerful static type system (but feels dynamic)
  • concise and expressive syntax
  • compiles to Java byte code and runs on the JVM
  • interoperable with Java itself (both ways!)

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)

Another hipster stuff?

Another *USA* hipster stuff?

  • Lambdacon (Bologna) ~ 280 dev
  • Scala Italy (Milano) ~ 150 + 70
  • Milano Scala Group ~ 570
  • Torino Scala Programming and Big Data Meetup ~ 200
  • G+ Community ~ 90

Show me the code"basic features"

Some feature we'll see/1

  • every value is an object
  • functions are "just" objects
  • compiler infer types (static typed)
  • (almost) everything is an expression
  • every operation is a method call

Some feature we'll see/2

  • language is designed to grow
  • collections
  • traits
  • easy singleton
  • every operation is a method call

Concise syntax - JavaPizza I

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);
  }

Concise syntax - JavaPizza/II

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;
  }

Concise syntax - JavaPizza/III

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;
  }
}

Concise syntax - Pizza

class Pizza(var tomato: Boolean = true, 
            var cheese: Boolean = true, 
            var ham: Boolean = true, 
            var size: String = "NORMAL")
  • everything is public by default,use private/protected if needed
  • var => something you can change
  • val => something final
  • specify defaults if needed
  • types are after parameters
  • setters and getters are generated
  • member variables are private (uniform access principle)
  • no semicolon needed

Creating and using objects

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

Using defaults

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")
  • if parameters have default, you can omit them on right
  • you can pass parameter using names (without order)
  • you can pass only parameters you want using name, if others have default
  • more on classes:

Nobody loves mutable pizzas!

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..

val != var

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
  • var: side effect, OOP programming
  • val: no side effect, functional programming

Defining methods

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")
  }

}

  • a method starts with a def
  • return type is optional (with a little exception)
  • Unit ~ void in Java
  • return keyword is optional, last expression will be returned
  • where's the constructor?
  • plus: string interpolation

Singletons/I

object Oven {
  def cook(pizza: Pizza): Unit = {
     println(s"Pizza $pizza is cooking")
     Thread.sleep(1000)
     println("Pizza is ready")
  }
}
  • a singleton uses the object keyword
  • pattern provided by the language
  • Scala is more OO than Java, no static
  • plus: a Java class used inside Scala

Singletons/II

object Margherita extends Pizza(true, false, false, "NORMAL") {
    override def toString = "Pizza Margherita"
}
Oven.cook(Margherita)
  • objects can extend classes
  • every overridden method must use override keyword

Case classes

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)

Inheritance/I

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}" 
}

Inheritance/II

val vito = new Handyman("Vito", 1000)
println(vito.greet)
vito.preparePizza(Margherita)
vito.servePizza(Margherita)
  • a class can inherite only one other class
  • trait ~ Java 8 interface but have also states
  • a trait can extends another one or a class
  • a class can mix-in many traits
  • trait linearization resolves the diamond problem (mostly)

Inheritance/III

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

Inheritance/IV

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
}

Inheritance/V (Compile error!)

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

Show me the code"advanced features"

Pattern matching/I

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"
    }

}

Pattern matching/II

  • a super flexible Java switch (but very different in nature)
  • in love with case classes, but possible on every class
  • can match partial definition of objects
  • can match types
  • _ is a wildcard

Working with collections and functions/I

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)

Working with collections and functions/II

  • easy to create a collection
  • functions are objects, you can pass them around
  • high order functions
  • you can use methods as functions
  • filter, map, fold and all the rest

Tuples

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)
  • to create a tuple, just open a parenthesis
  • tuples are immutable
  • zip creates tuple

Operators are just methods (an example)

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)

Implicit conversions/I

class MargheritaList(val n: Int) {

  def margheritas = {
    var list: List[Pizza] = List()
    for (i <- 1 to n)
      list = list :+ Margherita
    list
  }
}
  • using a var with an immutable list
  • note the generics
  • using collection operator :+
  • List.fill(n)(Margherita) is more idiomatic
  • you choose how much functional you want to be

Implicit conversions/II

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

A Pizza DSL

val pizza = Margherita
Pino commentPizza pizza
Oven cook pizza
pizza / 6

Question. How much keywords do you see?

For comprehension/I

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))
}

For comprehension/II

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/III

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)
  • for comprehension is different from for loop (no yield)
  • is purely syntactic sugar

The end

Q & Option[A]

Thanks for following, slides and code on https://github.com/giampaolotrapasso/

Scala 101 first steps with Scala Treviso Scala Group - 24th september 2015