joy of scala
Post on 15-Apr-2017
11.768 Views
Preview:
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