joy of scala

Post on 15-Apr-2017

11.767 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

The Joy of

Maxim Novak

@maximnovak

maximn@wix.com https://github.com/maximn

Or : Why I love Scala

The Joy of

Maxim Novak

@maximnovak

maximn@wix.com https://github.com/maximn

Or : Why I love Scala

HiI’m Maxim Novak.

• Working on Wix’s back-end• 8 years in the software

industry• 2 years avid Scala advocate

Scala is the better Java.

Scala is the better Java.

Simple conceptsBig impact

Why I Scala

Conciseness

public class Checkout { private double tax;

public Checkout(double tax) { this.tax = tax; }

double total(final List<Product> products) { double total = 0.0;

for (Product product : products) { total += product.getPrice(); }

return total * tax; }}

Java (7) Checkout class

public class Checkout { private double tax;

public Checkout(double tax) { this.tax = tax; }

double total(final List<Product> products) { return products .stream() .mapToDouble(Product::getPrice) .sum() * tax; }}

Java (8) Checkout class

public class Checkout { private double tax;

public Checkout(double tax) { this.tax = tax; }

double total(final List<Product> products) { return products .stream() .mapToDouble(Product::getPrice) .sum() * tax; }}

Public by default

Public by defaultclass Checkout { private double tax;

public Checkout(double tax) { this.tax = tax; }

double total(final List<Product> products) { return products .stream() .mapToDouble(Product::getPrice) .sum() * tax; }}

class Checkout { private double tax;

public Checkout(double tax) { this.tax = tax; }

double total(final List<Product> products) { return products .stream() .mapToDouble(Product::getPrice) .sum() * tax; }}

No need for semi-colons

No need for semi-colonsclass Checkout { private double tax

public Checkout(double tax) { this.tax = tax }

double total(final List<Product> products) { return products .stream() .mapToDouble(Product::getPrice) .sum() * tax }}

class Checkout { private double tax

public Checkout(double tax) { this.tax = tax }

double total(final List<Product> products) { return products .stream() .mapToDouble(Product::getPrice) .sum() * tax }}

Concise constructor and fields

Concise constructor and fields

class Checkout(tax: Double) { double total(final List<Product> products) { return products .stream() .mapToDouble(Product::getPrice) .sum() * tax }}

class Checkout(tax: Double) { double total(final List<Product> products) { return products .stream() .mapToDouble(Product::getPrice) .sum() * tax }}

Return last statement by default

Return last statement by default

class Checkout(tax: Double) { double total(final List<Product> products) { products .stream() .mapToDouble(Product::getPrice) .sum() * tax }}

class Checkout(tax: Double) { double total(final List<Product> products) { products .stream() .mapToDouble(Product::getPrice) .sum() * tax }}

Method arguments are final by defaultAnd the Name comes before the Type

Method arguments are final by defaultAnd the Name comes before the Type

class Checkout(tax: Double) { def total(products: Seq[Product]): Double = { products .stream() .mapToDouble(Product::getPrice) .sum() * tax }}

class Checkout(tax: Double) { def total(products: Seq[Product]): Double = { products .stream() .mapToDouble(Product::getPrice) .sum() * tax }}

Type Inference

Type Inference

class Checkout(tax: Double) { def total(products: Seq[Product]) = { products .stream() .mapToDouble(Product::getPrice) .sum() * tax }}

class Checkout(tax: Double) { def total(products: Seq[Product]) = { products .stream() .mapToDouble(Product::getPrice) .sum() * tax }}

Remove more boilerplate

class Checkout(tax: Double) { def total(products: Seq[Product]) =

products.map(_.getPrice).sum * tax}

P P P P 1 3 7 4 15

.map(_.getPrice) .sum

Remove more boilerplate

class Checkout(tax: Double) { def total(products: Seq[Product]) =

products.map(_.getPrice).sum * tax}

Scalapublic class Checkout { private double tax;

public Checkout(double tax) { this.tax = tax; }

double total(final List<Product> products) { return products .stream() .mapToDouble(Product::getPrice) .sum() * tax; }}

Java

Why it’s Good for Ya

Immutable by default

Immutable by default

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.

- Programming in Scala By Martin Odersky, Lex Spoon, Bill Venners

http://www.yegor256.com/2014/06/09/objects-should-be-immutable.html

Immutability

Set<Date> set = new HashSet<>();Date date = new Date(2);set.add(date);date.setTime(4);

System.out.println(set.contains(date));

Easier to use = always thread safe = testable

http://www.yegor256.com/2014/06/09/objects-should-be-immutable.html

HashCode Object

1

2

3

4

...

...

n

date

date

Domain Objects

public class Product {

public Product(String name, double price) { this.name = name; this.price = price; } private String name; private double price;

public double getPrice() { return price; }

public String getName() { return name; }

@Override public String toString() { return "Product{" + "name='" + name + '\'' + ", price=" + price + '}'; }

@Override public boolean equals(Object o) {

if (this == o) return true; if (o == null || getClass() != o.getClass()) return false;

Product product = (Product) o;

if (Double.compare(product.price, price) != 0) return false; return name != null ? name.equals(product.name) : product.name == null;

}

@Override public int hashCode() { int result; long temp; temp = Double.doubleToLongBits(price); result = (int) (temp ^ (temp >>> 32)); result = 31 * result + (name != null ?

name.hashCode() : 0); return result; }}

public void setPrice(double price) { this.price = price; }

public void setName(String name) { this.name = name; }

Case Classes

case class Product(name: String, price: Double)

Less code = Less bugs = Readable code = Easier to maintain

Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code. ...[Therefore,] making it easy to read makes it easier to write.- Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship

Case Classes

case class Product(name: String, price: Double)

What else?

val book = Product("Book", 42)val discountedBook = book.copy(price = 32)

Static Types InferencePromotes better naming over type declaration

val i = 8val s = "bar"val withExplicitType: String = "baz"val product = new Product(“name", 18)val seq = Seq(1, 2, 3)

def withString(s: String) = …withString(i)

Error:(29, 16) type mismatch; found : Int required: String withString(i) ^

Static Types InferencePromotes better naming over type declaration

private def add(a: Int, b: Int) = a + b

public def add(a: Int, b: Int): Int = a + b

Clarity & Comprehension

Why I Scala

1. Use types to understand functions

interface Calculator { Double divideOneBy(Integer divisor)}

calculator.divideOneBy(0);

null ArithmeticException Double.NaN

What’s the result ?

Meet Scala OptionsA better way for handling NULLs

Option [ T ]

None Some [ T ]

Meet Scala OptionsA better way for handling NULLs

trait Calculator { def divideOneBy(i: Int): Option[Double]}

calculator.divideOneBy(0) == Nonecalculator.divideOneBy(2) == Some(0.5)

Meet Scala Optionsval result: Option[Double] = calculator.divideOneBy(...)

var halfResult: Double = null

if (result.isDefined) { halfResult = result.get / 2 }else { ??? }

Imperative Style

Meet Scala OptionsImperative Style

val result: Option[Double] = calculator.divideOneBy(...)

var halfResult: Option[Double] = None

if (result.isDefined) { halfResult = Some(result.get / 2)}

Meet Scala OptionsFunctional style

val result: Option[Double] = calculator.divideOneBy(...)

val halfResult = result.map(r => r / 2)

Meet Scala OptionsFunctional style

val result: Option[Double] = calculator.divideOneBy(...)

val halfResult = result.map(_ / 2)

Meet Scala OptionsDefault Values

val productPhotoUrl: Option[String] = ...

val photoUrl: String = productPhotoUrl.getOrElse("http://site.com/defaultProductImage.jpg")

What You Can Do with Options

val default: Option[String] = ...val fallback: Option[String] = ...

val url = default.orElse(fallback)

Fallback

Java LibrariesWorking with Java? Just wrap it with an Option

Option(null) == None

Option(obj) == Some(obj)

Java LibrariesWorking with Java? Just wrap it with an Option

def javaApi(): SomeType = ...

val result = Option(javaApi())

NullPointerException – Never Again

Meet Scala’s TryThe flow is clear when it comes to Exceptions

Try [ T ]

Failure Success [ T ]

Why I Scala

1. Use types to understand functions2. Elegant flow

For Comprehensioncase class Photo(url: String)case class Product(name: String, photo: Photo)

def photoUrl(product: Product) = { var url: String = null if (product != null) { if (product.photo != null) { if (product.photo.url != null) { url = product.photo.url } } } url}

case class Photo(url: String)case class Product(name: String, photo: Photo)

def photoUrl(product: Product) = { var url: String = null if (product != null) { if (product.photo != null) { if (product.photo.url != null) { url = product.photo.url } } } url}

case class Photo(url: Option[String])case class Product(name: String, photo: Option[Photo])

def photoUrl(productOpt: Option[Product]) = for { product <- productOpt photo <- product.photo url <- photo.url } yield url

Why I Scala

1. Use types to understand functions2. Elegant flow3. Powerful Pattern matching

Pattern MatchingA more powerful switch/case

val x: Any = ...

x match { case 1 => "It's an integer 1" case "1" => "It's a String \"1\"" case b: Boolean => "It's the a boolean : " + b.toString case i: Int if i > 0 => "It's a positive Integer : " + i case _: String => "It's a String, don’t care the value" case _: Float | _: Double => "Something numeric" case _ => "Didn't match any condition"}

val obj: AnyRef = ...

obj match { case Product(_, 0) =>

println("FREE! Don’t care about the name") case Product(name, price) =>

println(name + " cost" + price)}

Pattern MatchingExtracting Case-Classes

case class Product(name: String, price: Double)

val url: java.net.URL = ...

url match { case HTTP(address) => "HTTP! It’s : " + address case _ => "Unknown protocol"}

object HTTP { def unapply(url: URL): Option[String] = if (url.getProtocol == "http") Some(url.toString) else None}

Custom ExtractorsMake your code cleaner by extracting the how to’s

val url: java.net.URL = ...

url match { case HTTP(address) => "HTTP! It’s : " + address case FTP(address) => "FTP! It’s : " + address case File(path) => "File! It’s : " + path case CustomProtocol(foo, bar) => "Custom! It’s : " + foo + "/" + bar case _ => "Unknown protocol"}

Custom ExtractorsMake your code cleaner by extracting the how to’s

Why I Scala

1. Use types to understand functions2. Elegant flow3. Powerful Pattern matching4. Awesome Parameters

Default Parameters

class DatabaseConnection { public DatabaseConnection(String host, int port, Credentials credentials) {}}

Constructor overloading

Default Parameters

class DatabaseConnection { public DatabaseConnection(String host, int port, Credentials credentials) {} public DatabaseConnection(String host, Credentials credentials) { this(host, 3306, credentials); }}

Constructor overloading

Default Parametersclass DatabaseConnection { public DatabaseConnection(String host, int port, Credentials credentials) {} public DatabaseConnection(String host, Credentials credentials) { this(host, 3306, credentials); }

public DatabaseConnection(Credentials credentials) { this("localhost", credentials); }}

Constructor overloading

Default Parametersclass DatabaseConnection { public DatabaseConnection(String host, int port, Credentials credentials) {} public DatabaseConnection(String host, Credentials credentials) { this(host, 3306, credentials); }

public DatabaseConnection(Credentials credentials) { this("localhost", credentials); }

public DatabaseConnection() { this(Credentials.empty); }}

Constructor overloading

Default Parametersclass DatabaseConnectionBuilder { private String host = "localhost"; private int port = 3306; private Credentials credentials = Credentials.empty

public DatabaseConnectionBuilder withHost(int port) { this.host = host; return this; }

public DatabaseConnectionBuilder withPort(int port) { this.port = port; return this; }

public DatabaseConnectionBuilder withCredentials(Credentials credentials) { this.credentials = credentials; return this; }

public DatabaseConnection build() { return new DatabaseConnection(host, port, credentials); }}

Builder Pattern

Default Parametersclass DatabaseConnection(host: String = "localhost", port: Int = 3306, credentials: Credentials = Credentials.empty)

Default Parametersclass DatabaseConnection(host: String = "localhost", port: Int = 3306, credentials: Credentials = Credentials.empty)

new DatabaseConnection("otherhost.com")

Default Parametersclass DatabaseConnection(host: String = "localhost", port: Int = 3306, credentials: Credentials = Credentials.empty)

new DatabaseConnection(port = 3030)

Named Parameters

new File(path).setExecutable(true, false)

Named Parameters

boolean executable = true;boolean ownerOnly = false;new File(path).setExecutable(executable, ownerOnly);

Named Parameters

new File(path).setExecutable(executable = true, ownerOnly = false)

Why I Scala

1. Use types to understand functions2. Elegant flow3. Powerful Pattern matching4. Awesome Parameters5. Easier Strings

String InterpolationMore readable, Use $ for variables

val name = "Max"val lang = "Scala"

val desc = s"Hello $name. $lang is awesome!"

Hello Max. Scala is awesome!

Quotes & MultilineMore readable, Easier to implement

val withQuotes = """Now I can use "quotes" without escaping"""

val multiline = """{ "firstName": "John", "lastName": "Smith", "age": 25 }"""

FUN & PROFIT

http://stackoverflow.com/research/developer-survey-2016

FUN & PROFIT

http://stackoverflow.com/research/developer-survey-2016

First Steps

You’re not alone

Adopted by:

… and a rapidly growing community

Migrating from JavaYou can do it! Things working for you:

EASY to start

Migrating from JavaYou can do it! Things working for you:

All Java libraries that you know and love work in Scala too

Migrating from JavaYou can do it! Things working for you:

You can start imperative and move to functional, decide where you want to be

Migrating from JavaThere are caveats. Things working against you:

(Too much?) freedom in programming style

http://www.lihaoyi.com/post/StrategicScalaStylePrincipleofLeastPower.html

Migrating from JavaThere are caveats. Things working against you:

Slower compile time

Migrating from JavaThere are caveats. Things working against you:

Less tooling

Migrating from JavaThere are caveats. Things working against you:

Recruiting

Java

Step 1Tests in Scala

Scala

Step 2Convert class to JALA, make it compile, refactor

Step 3scala.collections.JavaConversions._

Step 4@BeanProperty Annotation

Step 5Preserve Git

history

Java

Step 1Tests in Scala

Scala

Step 2Convert class to JALA, make it compile, refactor

Step 3scala.collections.JavaConversions._

Step 4@BeanProperty Annotation

Step 5Preserve Git

history

Wanna Learn More?https://twitter.github.io/scala_school/

http://danielwestheide.com/scala/neophytes.html

http://twitter.github.io/effectivescala/

https://www.scala-exercises.org/std_lib/assertsAdvanced Topics• Multiple inheritance• Implicits• Macros• Functional libraries

Thank You!Any Questions?Maxim Novak

@maximnovakmaximn@wix.com https://github.com/maximn

top related