Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Java 8 and Beyond, a Scala Story

Java 8 and Beyond, a Scala Story

A talk given at a Wix Ukraine R&D meetup in Dnipro, Ukraine on 6 April, 2016.

With Java 8 adoption skyrocketing, is Scala still relevant? In our opinion, the answer is an unequivocal yes. To make our point, Tomer Gabel (system architect at Wix) will showcase practical examples where Scala's features provide a definitive advantage over Java 8. These include:

* Effective logging with traits and by-name parameters;
* Pattern matching for fun and profit;
* Type-safe, efficient serialization with type classes.

Video recording: https://youtu.be/EXxA3PlcdBg?t=3680
Sample code: https://github.com/holograph/scala-vs-java8

Tomer Gabel

April 06, 2016
Tweet

More Decks by Tomer Gabel

Other Decks in Programming

Transcript

  1. Preface • Java 8 was released in 2014 • Which

    begs the question… • Is Scala still relevant? – Yes. – This talk is about convincing you!
  2. Quick Agenda • A bit of history – The Java-Scala

    gap – How Java 8 reduces it – Remaining gaps • Showcase! – Traits – Pattern matching – Implicits
  3. The Java-Scala gap Historically (pre Java 8) • Type inference

    • Lambdas • Traits • Collections library • DSLs • Implicits
  4. The Java-Scala gap Currently (with Java 8) • Type inference

    • Lambdas • Traits • Collections library • DSLs • Implicits
  5. 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
  6. 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
  7. 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; } }
  8. 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); } }
  9. 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());
  10. 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) }
  11. 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
  12. Pattern Matching • Pattern matching in Scala: – Allows arbitrary

    types – Supports guards – Checks for exhaustiveness – User-extensible – Ubiquitous
  13. 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
  14. Right. So? • Jackson uses runtime reflection – Hard to

    predict – Lossy (e.g. erasure) • Pluggable via modules – Easy to forget – Test to avoid mistakes
  15. 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] }
  16. 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!
  17. How Is That Better? • Performance – No reflective access

    – No runtime codegen • Reliability – Missing serializer = compile-time error – Lossless – No spurious tests
  18. WE’RE DONE HERE! … AND YES, WE’RE HIRING :-) Thank

    you for listening [email protected] @tomerg http://il.linkedin.com/in/tomergabel Sample Code: https://github.com/holograph/scala-vs-java8