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.
A talk given at a Wix Ukraine R&D meetup in Dnipropetrovsk, Ukraine on 6 April, 2016.
Video recording: https://youtu.be/EXxA3PlcdBg?t=3680
Sample code: https://github.com/holograph/scala-vs-java8
9. 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
10. 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;
}
}
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
• 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)
}
15. 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
19. 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
20. Right. So?
• Jackson uses 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 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!
23. How Is That Better?
• 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