naïveté vs. experience

Download Naïveté vs. Experience

If you can't read please download the document

Upload: mike-fogus

Post on 16-Apr-2017

9.176 views

Category:

Technology


0 download

TRANSCRIPT

Navet vs. ExperienceHow We Thought We Could Use Scala and Clojure, and How We Actually Did

Who

AI, Distributed Simulation, Code Generation, Machine Vision

C, C++, Java, CLIPS, Clojure, Scala

Co-author of The Joy of Clojure http://joyofclojure.com

Local Boy

Michael Fogus: Programmer

Why

Java

Compression Potential

Compression Potential

Essential Boilerplate

Essential Boilerplate

equals

hashCode

get

set

public Foo

toString

Essential Boilerplate

equals

hashCode

get

set

public Foo

toString

try

catch

finally

;

{

}

@override

static

int

System.out

boolean

.

void

import

String

Exception

throws

toString

toString

throws

throws

finally

finally

finally

get

get

equals

hashCode

try

}

System.out

boolean

toString

throws

throws

finally

get

public Foo

catch

finally

;

int

import

String

Exception

throws

toString

get

equals

get

toString

try

finally

static

import

String

toString

throws

finally

equals

try

toString

throws

get

finally

String

Exception

hashCode

finally

{

@override

int

void

Exception

toString

throws

finally

hashCode

toString

throws

finally

public Foo

finally

int

Exception

@override

static

finally

hashCode

try

throws

throws

static

@override

hashCode

throws

hashCode

@override

void

toString

toString

int

hashCode

void

finally

finally

public Foo

int

hashCode

@override

void

toString

toString

int

hashCode

void

finally

finally

public Foo

int

hashCode

@override

void

toString

toString

int

hashCode

void

finally

finally

public Foo

int

toString

static

finally

equals

try

finally

String

toString

throws

finally

toString

finally

What

Java

Java

Compiler

Problem

We can speak to the compiler or the problem-space. In Java the compiler is often king.Java

List foo = new ArrayList();foo.add(new HashMap() {{ put(bar, baz); }});Compiler

Problem

Java

Compiler

Problem

Placating the compiler can often occlude the problem at hand.

Java != JVM

Reprieve

JRuby

Jython

Jess

Scala

Clojure

Groovy

Rhino

JRuby was out because we use Python/Jython

Jess was replaced by Drools and relegated to a supporting role

Groovy provided nothing over Jython

Rhino was too scaryJRuby

Jython

Jess

Scala

Clojure

Groovy

Rhino

Scala

Clojure

Scala

Scala

Compiler

Problem

Using Scala allows us to speak more to the problem at hand.Scala

Compiler

Problem

val foo = List[Map[String,String]]()foo ++ Map("bar" -> "baz")

Scala

Compiler

Problem

The problem is only partially occluded. This can increase the closer to remain to Java's semantics.Clojure

Clojure

Compiler

Problem

You rarely talk to the compiler, and when you do it's often at the end when trying to gain speed.(def foo {})(assoc foo bar baz)Clojure

Compiler

Problem

Clojure

Compiler

Problem

zzz

Clojure

Compiler

Problem

Runtime

We've changed the model entirely. Runtime is king and this is scary for some coming from a static language like Java.

DSLs help to address the problem space directly.

Scala has good support for DSLs too, but not to the insane levels of Clojure/Lisp.Essential Boilerplate

public class Person { private String lastName; private String firstName; private Person spouse;

public Person(String firstName, String lastName, Person spouse) { this.lastName = lastName; this.firstName = firstName; this.spouse = spouse; }

public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public Person getSpouse() { return spouse; }

public String toString() { return firstName + " " + lastName +

(spouse != null ? " married to " + spouse.getFirstName() + "." : "."); }

}

This was my initial pitch...Less Boilerplate

case class Person(val firstName:String, val lastName:String, val spouse:Option[Person]) {

public Person(String firstName, String lastName, Person spouse) { this.lastName = lastName; this.firstName = firstName; this.spouse = spouse; }

public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public Person getSpouse() { return spouse; }

public String toString() { return firstName + " " + lastName +

(spouse != null ? " married to " + spouse.getFirstName() + "." : "."); }

}

Kittens

case class Person(val firstName:String, val lastName:String, val spouse:Option[Person]) {

public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public Person getSpouse() { return spouse; }

public String toString() { return firstName + " " + lastName +

(spouse != null ? " married to " + spouse.getFirstName() + "." : "."); }

}

These intermediate transformations are irrelevant given that they were not legal Scala nor Java... so why not kittens?Kittens and Aquaman

case class Person(val firstName:String, val lastName:String, val spouse:Option[Person]) {

override def toString():String = firstName + " " + lastName +

return firstName + " " + lastName +

(spouse != null ? " married to " + spouse.getFirstName() + "." : "."); }

}

Scala

case class Person(val firstName:String, val lastName:String, val spouse:Option[Person]) {

override def toString():String = firstName + " " + lastName +

(spouse match {

case None => "."

case Some(s) => " married to " + s.firstName + "."

});

}

By default, Scala classes are immutable.

This transformation demonstration was a huge hit! Things were beautiful, and the Java devs could understand the code fairly well.

But...Disingenuous

This exercise is a dishonest pitch.

1) It doesn't speak directly to Scala's strengths

2) Often interoperability hinders such drastic code reductions

3) ...Of Course...

(defrecord Person [fname lname spouse] Object

(toString [_]

(str fname " " lname

(when-let [n (:fname spouse)]

(str " married to " n))

\.)))

By default, Scala classes are immutable.

Demonstrating a similar transformation for Clojure was less than successful...

Scala Won
(mostly)

Mostly...

Scala is used in the mainline source.

Clojure is relegated to support tools and one-offs.

(I also use it for cron jobs)How

Embedded XML

Everything is an Object

Functional Programming

List Comprehensions

Case Classes

Pattern Matching

Interoperability

Currying

Implicits

Architecture

ExecDispatchServiceMiddleware

The Internetservices

This is a simplified for clarity and to make the points at hand.

Four layers per service.

Recursive definitions.

ExecDispatchServiceMiddleware

The Internetservices

Inbound requests touch 3 layers

Outbound responses touch 1

This is also a simplification, but good as an overview.

ExecDispatchServiceMiddleware

The Internetservices

Service layer is just a public facade.

ExecDispatchServiceMiddleware

The Internetservices

Middleware is responsible for the actions to be performed (gathering, filtering, etc...). This is also where caching occurs (where appropriate).

ExecDispatchServiceMiddleware

The Internetservices

Dispatch determines where the sub-services are located. Sometimes there are local and sometimes remote.

ExecDispatchServiceMiddleware

The Internetservices

Services do the actual work...

ExecDispatchServiceMiddleware

The Internetservices

Exec is just an executor. Sometimes results come back directly and sometimes they are delayed. Exec knows what to do in either case. Exec also handles execptions.

ExecDispatchServiceMiddleware

The Internetservices

ExecDispatchServiceMiddleware

The Internet

ExecDispatchServiceMiddleware

The Internet

Let's zoom into one of the little-s services to see what it looks like...

ExecDispatchServiceMiddleware

The Internet

ExecDispatchServiceMiddleware

A service is also a ServiceUsing this abstraction we are able to gain a high-level of reusability.

ExecDispatchServiceMiddleware

The Internet

ExecDispatchServiceMiddleware

Navet

Embedded XML

Everything is an Object

Functional Programming

List Comprehensions

Case Classes

Pattern Matching

Interoperability

Currying

Implicits

Interoperability

Everything is an Object

Embedded XML

Functional Programming

List Comprehensions

Case Classes

Pattern Matching

Currying

Implicits

Interoperability

Functional Programming

List Comprehensions

Pattern Matching

Currying

Experience

Interoperability

Type Aliases

Traits

Functional Programming

List Comprehensions

Immutability

Pattern Matching

Currying

Closures

Disingenuous

Disingenuous

Interoperability
(near seamless essential)

Type Aliases

D.I.

Type Aliases

class Service extends IService {

def get(ref:ReferenceParam):ComponentReturn = { ... }

...

}

By default, Scala classes are immutable.

Although not functional, type aliases turned out to be extremely useful.Type Aliases

class Service extends IService {

def get(ref:ReferenceParam):ComponentReturn = { ... }

...

}

Type Aliases

class Service extends IService {

def get(ref:ReferenceParam):ComponentReturn = { ... }

...

}

type ReferenceParam = java.util.Map[String,String]

type ComponentReturn = java.util.Map[String,Object]

We could describe interfaces using logical names of types and somewhere else describe the concrete types.Traits

D.I.

D.I.

Traits

class Service extends IService {

def get(ref:ReferenceParam):ComponentReturn = { ... }

...

}

trait RPCTypes {

type ReferenceParam = java.util.Map[String,String]

type ComponentReturn = java.util.Map[String,Object]

}

The concrete types were aggregated in traits. (this is a simplified view... in reality we split the return and parameter types and used implicits as glue)Traits

class Service extends IService with RPCTypes {

def get(ref:ReferenceParam):ComponentReturn = { ... }

...

}

trait RPCTypes {

type ReferenceParam = java.util.Map[String,String]

type ComponentReturn = java.util.Map[String,Object]

}

ExecDispatchServiceMiddleware

The Internet

ExecDispatchServiceMiddleware

RPCTypesLocalTypesCachedTypesDelayedTypes...

RPCTypesLocalTypes...

Now we just mix in the types (and the conversions as needed)Functional Programming

List Comprehensions

Immutability

When a tree falls in a lonely forest ... does it make a sound?

Charles Riborg Mann and George Ransom Twiss

If a pure function mutates some local data in order to produce an immutable return value, is that ok?

Rich Hickey

Scala and Java do not enforce immutability and in fact in interop the very opposite is the case. We were able to be quite successful using tree falls in the woods immutability however.Pattern Matching
(a language for error shapes)

Currying
(gratuitous, but cool, control structures)

Closures
(gratuitous, but cool, simplified delays)

Woe

Breaking Changes

Optional Immutability

Weak Laziness

Less Functional

Grand Hierarchies

Literals-lite

Breaking Changes

Optional Immutability

Weak Laziness

Less Functional

Grand Hierarchies

Literals-lite

Clojure!

Thanks To

You

My employer

Dean Wampler for feedback

Rich Hickey and Martin Odersky for Clojure and Scala

Chris Houser my co-author

Ryan Tomayko for the screaming face

The fam

Questions?
(example questions below)

Joel or Mike?

Is Scala too complicated?

Best zombie movies?

Kobaia is de hundin?

Favorite hangout in Baltimore?

What's with the tie?