java 8 and beyond, a scala story

Post on 08-Jan-2017

434 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Java 8 and Beyond,a Scala StoryTomer 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

top related