Java 8 and Beyond,
a Scala Story
Tomer Gabel, April 2016
Preface
• Java 8 was released
in 2014
• Which begs the
question…
• Is Scala still relevant?
– Yes.
– This talk is about
convincing you!
Quick Agenda
• A bit of history
– The Java-Scala gap
– How Java 8 reduces it
– Remaining gaps
• Showcase!
– Traits
– Pattern matching
– Implicits
The Java-Scala gap
Historically (pre Java 8)
• Type inference
• Lambdas
• Traits
• Collections library
• DSLs
• Implicits
The Java-Scala gap
Currently (with Java 8)
• Type inference
• Lambdas
• Traits
• Collections library
• DSLs
• Implicits
There’s So Much More…
For-comprehensions
Flexible scoping
Built-in tuples
Higher-kinded types
Implicit conversions
Declaration-site
variance
(Partial) Functions
Bottom types
Structural types
Type members
Path-dependent types
Macros
RIGHT.
WHY SHOULD YOU CARE?
SHOWCASE #1:
TRAITS
Ye Olde Logging
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ClassWithLogs {
private static Logger log =
LoggerFactory.getLogger(ClassWithLogs.class);
public String getNormalizedName(Person person) {
log.info("getNormalizedName called");
log.debug("Normalizing " + person.toString());
String normalizedName =
person.getName().toUpperCase().trim();
log.debug("Normalized name is: " + normalizedName);
return normalizedName;
}
}
Eager
Evaluation
Boilerplate
Improvement?
public class LoggingSample implements Logging {
public String getNormalizedName(Person person) {
info("getNormalizedName called");
debug("Normalizing " + person.toString());
String normalizedName =
person.getName().toUpperCase().trim();
debug("Normalized name is: " + normalizedName);
return normalizedName;
}
}
Java Interface Limitations
• No state allowed
– Need Logger instance
– Workaround: getter
– But... boilerplate :-(
• Only public methods
– Logging APIs visible!
– … as is logger()
public interface Logging {
Logger logger();
default void debug(String msg) {
if (logger().isDebugEnabled())
logger().debug(msg);
}
default void info(String msg) {
if (logger().isInfoEnabled())
logger().info(msg);
}
}
And Lazy Evaluation?
• Can be implemented with a lambda:
import java.util.function.Supplier;
default void debug(Supplier<String> message) {
if (getLogger().isDebugEnabled())
getLogger().debug(message.get());
}
• But there’s boilerplate at the call site:
debug(() -> "Normalizing " + person.toString());
Scala Traits
• Allow state
• Participate in
lifecycle
• Support
visibility
• Multiple
inheritance!
trait Logging {
private val logger =
LoggerFactory.getLogger(getClass)
protected def warn(msg: => String) =
if (logger.isWarnEnabled)
logger.warn(msg)
protected def debug(msg: => String) =
if (logger.isDebugEnabled)
logger.debug(msg)
}
SHOWCASE #2:
PATTERN MATCHING
Switcheroo
• Switch statement is incredibly limited
– Only supports primitives (and strings)
– No arbitrary expressions (e.g. guards)
– No result values
• Workarounds are ugly
– Nested control structures
– Encoding enums instead of using types
Pattern Matching
• Pattern matching in
Scala:
– Allows arbitrary types
– Supports guards
– Checks for
exhaustiveness
– User-extensible
– Ubiquitous
ARE YOU READY FOR CODE?
SHOWCASE #3:
IMPLICITS
Serialization in a Nutshell
• Break compound
type into
components
Reflection
• Iterate over
components
• Dispatch by type
Dispatch
• Make your
customers happy
• Save the world
• Adopt a puppy
Profit
Right. So?
• Jackson uses runtime
reflection
– Hard to predict
– Lossy (e.g. erasure)
• Pluggable via
modules
– Easy to forget
– Test to avoid mistakes
A Better Way
• Scala supports implicit parameters
– These are filled in by the compiler
– Well-defined rules for implicit search
• This lets you define type classes:
trait Serializer[T] {
def serialize(value: T): JsonValue
def deserialize(value: JsonValue): Try[T]
}
Composition
• Type classes are resolved recursively
• You can encode dependencies:
– If Serializer[T] is known, you can always
handle Option[T]
– Same principle applies to maps, sequences
etc.
• The compiler handles wiring for you
– To an arbitrary level of nesting!
How Is That Better?
• Performance
– No reflective access
– No runtime codegen
• Reliability
– Missing serializer =
compile-time error
– Lossless
– No spurious tests
WE’RE DONE HERE!
… AND YES, WE’RE HIRING :-)
Thank you for listening
tomer@tomergabel.com
@tomerg
http://il.linkedin.com/in/tomergabel
Sample Code:
https://github.com/holograph/scala-vs-java8

Java 8 and Beyond, a Scala Story

  • 1.
    Java 8 andBeyond, a Scala Story Tomer Gabel, April 2016
  • 2.
    Preface • Java 8was released in 2014 • Which begs the question… • Is Scala still relevant? – Yes. – This talk is about convincing you!
  • 3.
    Quick Agenda • Abit of history – The Java-Scala gap – How Java 8 reduces it – Remaining gaps • Showcase! – Traits – Pattern matching – Implicits
  • 4.
    The Java-Scala gap Historically(pre Java 8) • Type inference • Lambdas • Traits • Collections library • DSLs • Implicits
  • 5.
    The Java-Scala gap Currently(with Java 8) • Type inference • Lambdas • Traits • Collections library • DSLs • Implicits
  • 6.
    There’s So MuchMore… For-comprehensions Flexible scoping Built-in tuples Higher-kinded types Implicit conversions Declaration-site variance (Partial) Functions Bottom types Structural types Type members Path-dependent types Macros
  • 7.
  • 8.
  • 9.
    Ye Olde Logging importorg.slf4j.Logger; import org.slf4j.LoggerFactory; public class ClassWithLogs { private static Logger log = LoggerFactory.getLogger(ClassWithLogs.class); public String getNormalizedName(Person person) { log.info("getNormalizedName called"); log.debug("Normalizing " + person.toString()); String normalizedName = person.getName().toUpperCase().trim(); log.debug("Normalized name is: " + normalizedName); return normalizedName; } } Eager Evaluation Boilerplate
  • 10.
    Improvement? public class LoggingSampleimplements Logging { public String getNormalizedName(Person person) { info("getNormalizedName called"); debug("Normalizing " + person.toString()); String normalizedName = person.getName().toUpperCase().trim(); debug("Normalized name is: " + normalizedName); return normalizedName; } }
  • 11.
    Java Interface Limitations •No state allowed – Need Logger instance – Workaround: getter – But... boilerplate :-( • Only public methods – Logging APIs visible! – … as is logger() public interface Logging { Logger logger(); default void debug(String msg) { if (logger().isDebugEnabled()) logger().debug(msg); } default void info(String msg) { if (logger().isInfoEnabled()) logger().info(msg); } }
  • 12.
    And Lazy Evaluation? •Can be implemented with a lambda: import java.util.function.Supplier; default void debug(Supplier<String> message) { if (getLogger().isDebugEnabled()) getLogger().debug(message.get()); } • But there’s boilerplate at the call site: debug(() -> "Normalizing " + person.toString());
  • 13.
    Scala Traits • Allowstate • Participate in lifecycle • Support visibility • Multiple inheritance! trait Logging { private val logger = LoggerFactory.getLogger(getClass) protected def warn(msg: => String) = if (logger.isWarnEnabled) logger.warn(msg) protected def debug(msg: => String) = if (logger.isDebugEnabled) logger.debug(msg) }
  • 14.
  • 15.
    Switcheroo • Switch statementis incredibly limited – Only supports primitives (and strings) – No arbitrary expressions (e.g. guards) – No result values • Workarounds are ugly – Nested control structures – Encoding enums instead of using types
  • 16.
    Pattern Matching • Patternmatching in Scala: – Allows arbitrary types – Supports guards – Checks for exhaustiveness – User-extensible – Ubiquitous
  • 17.
    ARE YOU READYFOR CODE?
  • 18.
  • 19.
    Serialization in aNutshell • Break compound type into components Reflection • Iterate over components • Dispatch by type Dispatch • Make your customers happy • Save the world • Adopt a puppy Profit
  • 20.
    Right. So? • Jacksonuses runtime reflection – Hard to predict – Lossy (e.g. erasure) • Pluggable via modules – Easy to forget – Test to avoid mistakes
  • 21.
    A Better Way •Scala supports implicit parameters – These are filled in by the compiler – Well-defined rules for implicit search • This lets you define type classes: trait Serializer[T] { def serialize(value: T): JsonValue def deserialize(value: JsonValue): Try[T] }
  • 22.
    Composition • Type classesare resolved recursively • You can encode dependencies: – If Serializer[T] is known, you can always handle Option[T] – Same principle applies to maps, sequences etc. • The compiler handles wiring for you – To an arbitrary level of nesting!
  • 23.
    How Is ThatBetter? • Performance – No reflective access – No runtime codegen • Reliability – Missing serializer = compile-time error – Lossless – No spurious tests
  • 24.
    WE’RE DONE HERE! …AND YES, WE’RE HIRING :-) Thank you for listening tomer@tomergabel.com @tomerg http://il.linkedin.com/in/tomergabel Sample Code: https://github.com/holograph/scala-vs-java8