SlideShare une entreprise Scribd logo
1  sur  35
Back to Basics: Type Classes
Tomer Gabel, Wix
August, 2014
THE EXPRESSION PROBLEM
“Define a datatype by cases, where one can add new cases to
the datatype and new functions over the datatype, without
recompiling existing code, and while retaining static type
safety (e.g., no casts).”
-- Philip Wadler
Let’s Build a Calculator
• Operators:
– Addition (+)
– Subtraction (-)
– Multiplication (*)
– Division (/)
– Remainder (%)
• Types:
– Integers (32-bit signed)
– Longs (64-bit signed)
– Floats (32-bit IEEE 754)
– Longs (64-bit IEEE 754)
Two Views of the Same Problem
Pattern Matching
sealed trait Operand
case class Int32(value: Int) extends Operand
case class Real32(value: Float) extends Operand
// ...
def addition[T <: Operand](left: T, right: T): T =
(left, right) match {
case (Int32 (l), Int32 (r)) => Int32 (l + r)
case (Real32(l), Real32(r)) => Real32(l + r)
// ...
}
Object Oriented
sealed trait Operand
case class Int32(value: Int) extends Operand {
def add(other: Int) = Int32(value + other)
def subtract(other: Int) = Int32(value - other)
// ...
}
case class Real32(value: Float) extends Operand {
def add(other: Float) = Real32(value + other)
def subtract(other: Float) = Real32(value - other)
// ...
}
Two Views of the Same Problem
Pattern Matching
sealed trait Operand
case class Int32(value: Int) extends Operand
case class Real32(value: Float) extends Operand
// ...
def addition[T <: Operand](left: T, right: T): T =
(left, right) match {
case (Int32 (l), Int32 (r)) => Int32 (l + r)
case (Real32(l), Real32(r)) => Real32(l + r)
// ...
}
Object Oriented
sealed trait Operand
case class Int32(value: Int) extends Operand {
def add(other: Int) = Int32(value + other)
def subtract(other: Int) = Int32(value - other)
// ...
}
case class Real32(value: Float) extends Operand {
def add(other: Float) = Real32(value + other)
def subtract(other: Float) = Real32(value - other)
// ...
}
Two Views of the Same Problem
Pattern Matching
sealed trait Operand
case class Int32(value: Int) extends Operand
case class Real32(value: Float) extends Operand
// ...
def addition[T <: Operand](left: T, right: T): T =
(left, right) match {
case (Int32 (l), Int32 (r)) => Int32 (l + r)
case (Real32(l), Real32(r)) => Real32(l + r)
// ...
}
Object Oriented
sealed trait Operand
case class Int32(value: Int) extends Operand {
def add(other: Int) = Int32(value + other)
def subtract(other: Int) = Int32(value - other)
// ...
}
case class Real32(value: Float) extends Operand {
def add(other: Float) = Real32(value + other)
def subtract(other: Float) = Real32(value - other)
// ...
}
TYPE CLASSES TO THE RESCUE
What’s a Type Class?
• A type class:
– Enables ad-hoc polymorphism
– Statically typed (i.e. type-safe)
– Borrowed from Haskell
• Solves the expression problem:
– Behavior can be extended
– … at compile-time
– ... after the fact
– … without changing/recompiling
existing code
Example #1: Equality
• Scala inherits legacy aspects of Java
– This includes AnyRef.equals:
def equals( other: AnyRef ): Boolean
– So the following compiles:
3.14159265359 == "pi" // Evaluates to false
• What if we wanted to implement type-safe equality?
– Let’s define a type-safe isEqual function:
isEqual( 3.14159265359, "pi” ) // Does not compile!
What’s in a Type Class?
• Three components are
required:
– A signature
– Implementations for
supported types
– A function that requires
a type class This is where things get hairy.
Slight Digression
• A method in Scala can have multiple parameter lists:
def someMethod( x: Int )( y: String )( z: Double ): Unit = {
println( s"x=$x, y=$y, z=$z" )
}
scala> someMethod( 10 )( "abc" )( scala.math.Pi )
x=10, y=abc, z=3.141592653589793
• There are multiple uses for this, but the most important is…
Scala Implicits
• The last parameter list of a method can be marked implicit
• Implicit parameters are filled in by the compiler
– In effect, you require evidence of the compiler
– … such as the existence of a type class in scope
– You can also specify parameters explicitly, if needed
Putting it together
• Let’s define our type class:
trait Equality[ L, R ] {
def equals( left: L, right: R ): Boolean
}
• … and our isEqual function:
def isEqual[ L, R ]( left: L, right: R )
( implicit ev: Equality[ L, R ] ): Boolean =
ev.equals( left, right )
This is where the magic happens
Still missing something!
• We have no implementations of the Equality trait, so nothing works!
scala> isEqual( 3, 3 )
<console>:10: error: could not find implicit value for parameter ev:
Equality[Int,Int]
• We need to implement Equality[ T, T ]:
implicit def sameTypeEquality[ T ] = new Equality[ T, T ] {
def equals( left: T, right: T ) = left.equals( right )
}
• And now it works:
scala> isEqual( 3, 3 )
res1: Boolean = true
Ad-hoc Polymorphism
• Now we’ve met our original goal:
scala> isEqual( 3.14159265359, "pi" )
<console>:11: error: could not find implicit value for parameter ev: Equality[Double,String]
• But what if we wanted to equate doubles and strings?
• Well then, let’s add another implementation!
implicit object DoubleEqualsString extends Equality[ Double, String ] {
def equals( left: Double, right: String ) = left.toString == right
}
• Et voila, no recompilation or code changes needed:
scala> isEqual( 3.14159265359, "pi" )
res5: Boolean = false
QUESTIONS SO FAR
Example #2: Sort Me, Maybe
• Let’s implement a sort
function (e.g. bubble sort)
• With one caveat:
– It should operate on any type
– … for which an ordering exists
• Obviously, we’ll use type
classes!
Possible Solution
trait Ordering[ T ] { def isLessThan( left: T, right: T ): Boolean }
def sort[ T ]( items: Seq[ T ] )( implicit ord: Ordering[ T ] ): Seq[ T ] = {
val buffer = mutable.ArrayBuffer( items:_* )
for ( i <- 0 until items.size;
j <- ( i + 1 ) until items.size )
if ( ord.isLessThan( buffer( j ), buffer( i ) ) ) {
val temp = buffer( i )
buffer( i ) = buffer( j )
buffer( j ) = temp
}
buffer
}
Possible Solution, cont.
• Sample implementation for integers:
implicit object IntOrdering extends Ordering[ Int ] {
def isLessThan( left: Int, right: Int ) = left < right
}
val numbers = Seq( 4, 1, 10, 8, 14, 2 )
Assert( sort( numbers ) == Seq( 1, 2, 4, 8, 10, 14 ) )
Possible Solution, cont.
• Sample implementation for a domain entity:
case class Person( name: String, age: Int )
implicit object PersonOrdering extends Ordering[ Person ] {
def isLessThan( left: Person, right: Person ) =
left.age < right.age
}
val haim = Person( "Haim", 12 )
val dafna = Person( "Dafna", 20 )
val ofer = Person( "Ofer", 1 )
assert( sort( Seq( haim, dafna, ofer ) ) ==
Seq( ofer, haim, dafna ) )
Implicit Search Order
Current Scope
• Defined implicits
• Explicit imports
• Wildcard imports
Companion
• … of T
• … of supertypes of T
Outer Scope
• Enclosing class
REAL WORLD EXAMPLES, PLEASE?
Example #3: Server Pipeline
• REST is good, but annoying to write. Let’s simplify:
case class DTO( message: String )
class MyServlet extends NicerHttpServlet {
private val counter = new AtomicInteger( 0 )
get( "/service" ) {
counter.incrementAndGet()
DTO( "hello, world!" )
}
get( "/count" ) {
counter.get()
}
}
Uses return value;
no direct response manipulation
Example #3: Server Pipeline
• What’s in a server?
– Routing
– Rendering
– Error handling
• Let’s focus on rendering:
trait ResponseRenderer[ T ] {
def render( value : T,
request : HttpServletRequest,
response: HttpServletResponse ): Unit
}
Example #3: Server Pipeline
• A couple of basic renderers:
implicit object StringRenderer extends ResponseRenderer[ String ] {
def render( value: String, request: HttpServletRequest, response: HttpServletResponse ) = {
val w = response.getWriter
try w.write( value )
finally w.close()
}
}
implicit object IntRenderer extends ResponseRenderer[ Int ] {
def render( value: Int, request: HttpServletRequest, response: HttpServletResponse ) =
implicitly[ ResponseRenderer[ String ] ].render( value.toString, request, response )
}
Example #3: Server Pipeline
• Putting it together:
trait NicerHttpServlet extends HttpServlet {
private trait Handler {
type Response
def result: Response
def renderer: ResponseRenderer[ Response ]
}
private var handlers: Map[ String, Handler ] = Map.empty
protected def get[ T : ResponseRenderer ]( url: String )( thunk: => T ) =
handlers += url -> new Handler {
type Response = T
def result = thunk
def renderer = implicitly[ ResponseRenderer[ T ] ]
}
Example #3: Server Pipeline
• And finally:
override def doGet( req: HttpServletRequest, resp: HttpServletResponse ) =
handlers.get( req.getRequestURI ) match {
case None =>
resp.sendError( HttpServletResponse.SC_NOT_FOUND )
case Some( handler ) =>
try handler.renderer.render( handler.result, req, resp )
catch { case e: Exception =>
resp.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR )
}
}
PHEW.
Take a deep breath
Example #4: JSON Serialization
• Assume we already have a good model for JSON
• How do we add type-safe serialization?
JsonValue
JsonObject JsonArray
JsonBoolean JsonInt
JsonDouble JsonNull
JsonField
Example #4: JSON Serialization
• Let’s start with a typeclass:
trait JsonSerializer[ T ] {
def serialize( value: T ): JsonValue
def deserialize( value: JsonValue ): T
}
• And the corresponding library signature:
def serialize[ T ]( instance: T )( implicit ser: JsonSerializer[ T ] ) =
ser.serialize( instance )
def deserialize[ T ]( json: JsonValue )( implicit ser: JsonSerializer[ T ] ) =
ser.deserialize( json )
Example #4: JSON Serialization
• Define a few basic serializers…
implicit object BooleanSerializer extends JsonSerializer[ Boolean ] {
def serialize( value: Boolean ) = JsonBoolean( value )
def deserialize( value: JsonValue ) = value match {
case JsonBoolean( bool ) => bool
case other => error( other )
}
}
implicit object StringSerializer extends JsonSerializer[ String ] {
def serialize( value: String ) = JsonString( value )
def deserialize( value: JsonValue ) = value match {
case JsonString( string ) => string
case other => error( other )
}
}
Example #4: JSON Serialization
• We can also handle nested structures
– The compiler resolves typeclasses recursively!
• For example, Option[ T ] :
implicit def optionSerializer[ T ]( implicit ser: JsonSerializer[ T ] ) =
new JsonSerializer[ Option[ T ] ] {
def serialize( value: Option[ T ] ) =
value map ser.serialize getOrElse JsonNull
def deserialize( value: JsonValue ) = value match {
case JsonNull => None
case other => Some( ser.deserialize( other ) )
}
}
Require a serializer for T
… and delegate to it
Example #4: JSON Serialization
• What about an arbitrary type?
case class Person( name: String, surname: String, age: Int )
implicit object PersonSerializer extends JsonSerializer[ Person ] {
def serialize( value: Person ) = JsonObject(
JsonField( "name", serialize( value.name ) ),
JsonField( "surname", serialize( value.surname ) ),
JsonField( "age", serialize( value.age ) )
)
def deserialize( value: JsonValue ) = value match {
case obj: JsonObject =>
Person(
name = deserialize[ String ]( obj  "name" ),
surname = deserialize[ String ]( obj  "surname" ),
age = deserialize[ Int ]( obj  "age" )
)
case _ => error( value )
}
}
Summary
• We added serialization for Person after the fact without…
– … modifying the serialization framework
– … modifying the domain object
• We did not compromise:
– … type safety or performance
– … modularity or encapsulation
• This applies everywhere!
clients of either are unaffected!
… and we’re done
• Thank you for your time!
• Questions/comments?
– tomer@tomergabel.com
– @tomerg
– http://www.tomergabel.com
• Code samples:
– http://git.io/aWc9eQ

Contenu connexe

Tendances

Scala categorytheory
Scala categorytheoryScala categorytheory
Scala categorytheoryKnoldus Inc.
 
High Wizardry in the Land of Scala
High Wizardry in the Land of ScalaHigh Wizardry in the Land of Scala
High Wizardry in the Land of Scaladjspiewak
 
First-Class Patterns
First-Class PatternsFirst-Class Patterns
First-Class PatternsJohn De Goes
 
Scala introduction
Scala introductionScala introduction
Scala introductionvito jeng
 
Few simple-type-tricks in scala
Few simple-type-tricks in scalaFew simple-type-tricks in scala
Few simple-type-tricks in scalaRuslan Shevchenko
 
Practically Functional
Practically FunctionalPractically Functional
Practically Functionaldjspiewak
 
Functions In Scala
Functions In Scala Functions In Scala
Functions In Scala Knoldus Inc.
 
Metaprogramming in Scala 2.10, Eugene Burmako,
Metaprogramming  in Scala 2.10, Eugene Burmako, Metaprogramming  in Scala 2.10, Eugene Burmako,
Metaprogramming in Scala 2.10, Eugene Burmako, Vasil Remeniuk
 
High-Performance Haskell
High-Performance HaskellHigh-Performance Haskell
High-Performance HaskellJohan Tibell
 
Scala traits training by Sanjeev Kumar @Kick Start Scala traits & Play, organ...
Scala traits training by Sanjeev Kumar @Kick Start Scala traits & Play, organ...Scala traits training by Sanjeev Kumar @Kick Start Scala traits & Play, organ...
Scala traits training by Sanjeev Kumar @Kick Start Scala traits & Play, organ...Sanjeev_Knoldus
 

Tendances (20)

Scala collections
Scala collectionsScala collections
Scala collections
 
Getting Started With Scala
Getting Started With ScalaGetting Started With Scala
Getting Started With Scala
 
Scala categorytheory
Scala categorytheoryScala categorytheory
Scala categorytheory
 
Scala jargon cheatsheet
Scala jargon cheatsheetScala jargon cheatsheet
Scala jargon cheatsheet
 
High Wizardry in the Land of Scala
High Wizardry in the Land of ScalaHigh Wizardry in the Land of Scala
High Wizardry in the Land of Scala
 
Why Haskell
Why HaskellWhy Haskell
Why Haskell
 
Meet scala
Meet scalaMeet scala
Meet scala
 
First-Class Patterns
First-Class PatternsFirst-Class Patterns
First-Class Patterns
 
Scala Bootcamp 1
Scala Bootcamp 1Scala Bootcamp 1
Scala Bootcamp 1
 
Scala fundamentals
Scala fundamentalsScala fundamentals
Scala fundamentals
 
Scala introduction
Scala introductionScala introduction
Scala introduction
 
Few simple-type-tricks in scala
Few simple-type-tricks in scalaFew simple-type-tricks in scala
Few simple-type-tricks in scala
 
Practically Functional
Practically FunctionalPractically Functional
Practically Functional
 
Scala Paradigms
Scala ParadigmsScala Paradigms
Scala Paradigms
 
Functions In Scala
Functions In Scala Functions In Scala
Functions In Scala
 
Metaprogramming in Scala 2.10, Eugene Burmako,
Metaprogramming  in Scala 2.10, Eugene Burmako, Metaprogramming  in Scala 2.10, Eugene Burmako,
Metaprogramming in Scala 2.10, Eugene Burmako,
 
C# programming
C# programming C# programming
C# programming
 
Java Generics
Java GenericsJava Generics
Java Generics
 
High-Performance Haskell
High-Performance HaskellHigh-Performance Haskell
High-Performance Haskell
 
Scala traits training by Sanjeev Kumar @Kick Start Scala traits & Play, organ...
Scala traits training by Sanjeev Kumar @Kick Start Scala traits & Play, organ...Scala traits training by Sanjeev Kumar @Kick Start Scala traits & Play, organ...
Scala traits training by Sanjeev Kumar @Kick Start Scala traits & Play, organ...
 

Similaire à Scala Back to Basics: Type Classes

Legacy lambda code
Legacy lambda codeLegacy lambda code
Legacy lambda codePeter Lawrey
 
Scala Refactoring for Fun and Profit
Scala Refactoring for Fun and ProfitScala Refactoring for Fun and Profit
Scala Refactoring for Fun and ProfitTomer Gabel
 
(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?Tomasz Wrobel
 
Speaking Scala: Refactoring for Fun and Profit (Workshop)
Speaking Scala: Refactoring for Fun and Profit (Workshop)Speaking Scala: Refactoring for Fun and Profit (Workshop)
Speaking Scala: Refactoring for Fun and Profit (Workshop)Tomer Gabel
 
An introduction to functional programming with Swift
An introduction to functional programming with SwiftAn introduction to functional programming with Swift
An introduction to functional programming with SwiftFatih Nayebi, Ph.D.
 
Introduction à Scala - Michel Schinz - January 2010
Introduction à Scala - Michel Schinz - January 2010Introduction à Scala - Michel Schinz - January 2010
Introduction à Scala - Michel Schinz - January 2010JUG Lausanne
 
Lecture 5: Functional Programming
Lecture 5: Functional ProgrammingLecture 5: Functional Programming
Lecture 5: Functional ProgrammingEelco Visser
 
C++11 - A Change in Style - v2.0
C++11 - A Change in Style - v2.0C++11 - A Change in Style - v2.0
C++11 - A Change in Style - v2.0Yaser Zhian
 
Scala 3 Is Coming: Martin Odersky Shares What To Know
Scala 3 Is Coming: Martin Odersky Shares What To KnowScala 3 Is Coming: Martin Odersky Shares What To Know
Scala 3 Is Coming: Martin Odersky Shares What To KnowLightbend
 
Functional programming ii
Functional programming iiFunctional programming ii
Functional programming iiPrashant Kalkar
 
Extractors & Implicit conversions
Extractors & Implicit conversionsExtractors & Implicit conversions
Extractors & Implicit conversionsKnoldus Inc.
 
Introduction to idris
Introduction to idrisIntroduction to idris
Introduction to idrisConor Farrell
 
Let Us Learn Lambda Using C# 3.0
Let Us Learn Lambda Using C# 3.0Let Us Learn Lambda Using C# 3.0
Let Us Learn Lambda Using C# 3.0Sheik Uduman Ali
 
The Functional Programming Triad of Folding, Scanning and Iteration - a first...
The Functional Programming Triad of Folding, Scanning and Iteration - a first...The Functional Programming Triad of Folding, Scanning and Iteration - a first...
The Functional Programming Triad of Folding, Scanning and Iteration - a first...Philip Schwarz
 

Similaire à Scala Back to Basics: Type Classes (20)

Legacy lambda code
Legacy lambda codeLegacy lambda code
Legacy lambda code
 
Scala Refactoring for Fun and Profit
Scala Refactoring for Fun and ProfitScala Refactoring for Fun and Profit
Scala Refactoring for Fun and Profit
 
(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?
 
Java gets a closure
Java gets a closureJava gets a closure
Java gets a closure
 
Speaking Scala: Refactoring for Fun and Profit (Workshop)
Speaking Scala: Refactoring for Fun and Profit (Workshop)Speaking Scala: Refactoring for Fun and Profit (Workshop)
Speaking Scala: Refactoring for Fun and Profit (Workshop)
 
An introduction to functional programming with Swift
An introduction to functional programming with SwiftAn introduction to functional programming with Swift
An introduction to functional programming with Swift
 
Introduction à Scala - Michel Schinz - January 2010
Introduction à Scala - Michel Schinz - January 2010Introduction à Scala - Michel Schinz - January 2010
Introduction à Scala - Michel Schinz - January 2010
 
Lecture 5: Functional Programming
Lecture 5: Functional ProgrammingLecture 5: Functional Programming
Lecture 5: Functional Programming
 
C++11 - A Change in Style - v2.0
C++11 - A Change in Style - v2.0C++11 - A Change in Style - v2.0
C++11 - A Change in Style - v2.0
 
Scala 3 Is Coming: Martin Odersky Shares What To Know
Scala 3 Is Coming: Martin Odersky Shares What To KnowScala 3 Is Coming: Martin Odersky Shares What To Know
Scala 3 Is Coming: Martin Odersky Shares What To Know
 
Functional programming ii
Functional programming iiFunctional programming ii
Functional programming ii
 
Scala by Luc Duponcheel
Scala by Luc DuponcheelScala by Luc Duponcheel
Scala by Luc Duponcheel
 
Trafaret: monads and python
Trafaret: monads and pythonTrafaret: monads and python
Trafaret: monads and python
 
Extractors & Implicit conversions
Extractors & Implicit conversionsExtractors & Implicit conversions
Extractors & Implicit conversions
 
Introduction to idris
Introduction to idrisIntroduction to idris
Introduction to idris
 
Let Us Learn Lambda Using C# 3.0
Let Us Learn Lambda Using C# 3.0Let Us Learn Lambda Using C# 3.0
Let Us Learn Lambda Using C# 3.0
 
Scala
ScalaScala
Scala
 
Python basics
Python basicsPython basics
Python basics
 
The Functional Programming Triad of Folding, Scanning and Iteration - a first...
The Functional Programming Triad of Folding, Scanning and Iteration - a first...The Functional Programming Triad of Folding, Scanning and Iteration - a first...
The Functional Programming Triad of Folding, Scanning and Iteration - a first...
 
Functional object
Functional objectFunctional object
Functional object
 

Plus de Tomer Gabel

How shit works: Time
How shit works: TimeHow shit works: Time
How shit works: TimeTomer Gabel
 
Nondeterministic Software for the Rest of Us
Nondeterministic Software for the Rest of UsNondeterministic Software for the Rest of Us
Nondeterministic Software for the Rest of UsTomer Gabel
 
Slaying Sacred Cows: Deconstructing Dependency Injection
Slaying Sacred Cows: Deconstructing Dependency InjectionSlaying Sacred Cows: Deconstructing Dependency Injection
Slaying Sacred Cows: Deconstructing Dependency InjectionTomer Gabel
 
An Abridged Guide to Event Sourcing
An Abridged Guide to Event SourcingAn Abridged Guide to Event Sourcing
An Abridged Guide to Event SourcingTomer Gabel
 
How shit works: the CPU
How shit works: the CPUHow shit works: the CPU
How shit works: the CPUTomer Gabel
 
How Shit Works: Storage
How Shit Works: StorageHow Shit Works: Storage
How Shit Works: StorageTomer Gabel
 
Java 8 and Beyond, a Scala Story
Java 8 and Beyond, a Scala StoryJava 8 and Beyond, a Scala Story
Java 8 and Beyond, a Scala StoryTomer Gabel
 
The Wix Microservice Stack
The Wix Microservice StackThe Wix Microservice Stack
The Wix Microservice StackTomer Gabel
 
Scala Refactoring for Fun and Profit (Japanese subtitles)
Scala Refactoring for Fun and Profit (Japanese subtitles)Scala Refactoring for Fun and Profit (Japanese subtitles)
Scala Refactoring for Fun and Profit (Japanese subtitles)Tomer Gabel
 
Onboarding at Scale
Onboarding at ScaleOnboarding at Scale
Onboarding at ScaleTomer Gabel
 
Scala in the Wild
Scala in the WildScala in the Wild
Scala in the WildTomer Gabel
 
Put Your Thinking CAP On
Put Your Thinking CAP OnPut Your Thinking CAP On
Put Your Thinking CAP OnTomer Gabel
 
Leveraging Scala Macros for Better Validation
Leveraging Scala Macros for Better ValidationLeveraging Scala Macros for Better Validation
Leveraging Scala Macros for Better ValidationTomer Gabel
 
A Field Guide to DSL Design in Scala
A Field Guide to DSL Design in ScalaA Field Guide to DSL Design in Scala
A Field Guide to DSL Design in ScalaTomer Gabel
 
Functional Leap of Faith (Keynote at JDay Lviv 2014)
Functional Leap of Faith (Keynote at JDay Lviv 2014)Functional Leap of Faith (Keynote at JDay Lviv 2014)
Functional Leap of Faith (Keynote at JDay Lviv 2014)Tomer Gabel
 
5 Bullets to Scala Adoption
5 Bullets to Scala Adoption5 Bullets to Scala Adoption
5 Bullets to Scala AdoptionTomer Gabel
 
Nashorn: JavaScript that doesn’t suck (ILJUG)
Nashorn: JavaScript that doesn’t suck (ILJUG)Nashorn: JavaScript that doesn’t suck (ILJUG)
Nashorn: JavaScript that doesn’t suck (ILJUG)Tomer Gabel
 
Ponies and Unicorns With Scala
Ponies and Unicorns With ScalaPonies and Unicorns With Scala
Ponies and Unicorns With ScalaTomer Gabel
 
Lab: JVM Production Debugging 101
Lab: JVM Production Debugging 101Lab: JVM Production Debugging 101
Lab: JVM Production Debugging 101Tomer Gabel
 
DevCon³: Scala Best Practices
DevCon³: Scala Best PracticesDevCon³: Scala Best Practices
DevCon³: Scala Best PracticesTomer Gabel
 

Plus de Tomer Gabel (20)

How shit works: Time
How shit works: TimeHow shit works: Time
How shit works: Time
 
Nondeterministic Software for the Rest of Us
Nondeterministic Software for the Rest of UsNondeterministic Software for the Rest of Us
Nondeterministic Software for the Rest of Us
 
Slaying Sacred Cows: Deconstructing Dependency Injection
Slaying Sacred Cows: Deconstructing Dependency InjectionSlaying Sacred Cows: Deconstructing Dependency Injection
Slaying Sacred Cows: Deconstructing Dependency Injection
 
An Abridged Guide to Event Sourcing
An Abridged Guide to Event SourcingAn Abridged Guide to Event Sourcing
An Abridged Guide to Event Sourcing
 
How shit works: the CPU
How shit works: the CPUHow shit works: the CPU
How shit works: the CPU
 
How Shit Works: Storage
How Shit Works: StorageHow Shit Works: Storage
How Shit Works: Storage
 
Java 8 and Beyond, a Scala Story
Java 8 and Beyond, a Scala StoryJava 8 and Beyond, a Scala Story
Java 8 and Beyond, a Scala Story
 
The Wix Microservice Stack
The Wix Microservice StackThe Wix Microservice Stack
The Wix Microservice Stack
 
Scala Refactoring for Fun and Profit (Japanese subtitles)
Scala Refactoring for Fun and Profit (Japanese subtitles)Scala Refactoring for Fun and Profit (Japanese subtitles)
Scala Refactoring for Fun and Profit (Japanese subtitles)
 
Onboarding at Scale
Onboarding at ScaleOnboarding at Scale
Onboarding at Scale
 
Scala in the Wild
Scala in the WildScala in the Wild
Scala in the Wild
 
Put Your Thinking CAP On
Put Your Thinking CAP OnPut Your Thinking CAP On
Put Your Thinking CAP On
 
Leveraging Scala Macros for Better Validation
Leveraging Scala Macros for Better ValidationLeveraging Scala Macros for Better Validation
Leveraging Scala Macros for Better Validation
 
A Field Guide to DSL Design in Scala
A Field Guide to DSL Design in ScalaA Field Guide to DSL Design in Scala
A Field Guide to DSL Design in Scala
 
Functional Leap of Faith (Keynote at JDay Lviv 2014)
Functional Leap of Faith (Keynote at JDay Lviv 2014)Functional Leap of Faith (Keynote at JDay Lviv 2014)
Functional Leap of Faith (Keynote at JDay Lviv 2014)
 
5 Bullets to Scala Adoption
5 Bullets to Scala Adoption5 Bullets to Scala Adoption
5 Bullets to Scala Adoption
 
Nashorn: JavaScript that doesn’t suck (ILJUG)
Nashorn: JavaScript that doesn’t suck (ILJUG)Nashorn: JavaScript that doesn’t suck (ILJUG)
Nashorn: JavaScript that doesn’t suck (ILJUG)
 
Ponies and Unicorns With Scala
Ponies and Unicorns With ScalaPonies and Unicorns With Scala
Ponies and Unicorns With Scala
 
Lab: JVM Production Debugging 101
Lab: JVM Production Debugging 101Lab: JVM Production Debugging 101
Lab: JVM Production Debugging 101
 
DevCon³: Scala Best Practices
DevCon³: Scala Best PracticesDevCon³: Scala Best Practices
DevCon³: Scala Best Practices
 

Dernier

CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️anilsa9823
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfkalichargn70th171
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsAndolasoft Inc
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsJhone kinadey
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️Delhi Call girls
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...kellynguyen01
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerThousandEyes
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...MyIntelliSource, Inc.
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...Health
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxComplianceQuest1
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providermohitmore19
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...panagenda
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...harshavardhanraghave
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AIABDERRAOUF MEHENNI
 

Dernier (20)

CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.js
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS LiveVip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
 

Scala Back to Basics: Type Classes

  • 1. Back to Basics: Type Classes Tomer Gabel, Wix August, 2014
  • 2. THE EXPRESSION PROBLEM “Define a datatype by cases, where one can add new cases to the datatype and new functions over the datatype, without recompiling existing code, and while retaining static type safety (e.g., no casts).” -- Philip Wadler
  • 3. Let’s Build a Calculator • Operators: – Addition (+) – Subtraction (-) – Multiplication (*) – Division (/) – Remainder (%) • Types: – Integers (32-bit signed) – Longs (64-bit signed) – Floats (32-bit IEEE 754) – Longs (64-bit IEEE 754)
  • 4. Two Views of the Same Problem Pattern Matching sealed trait Operand case class Int32(value: Int) extends Operand case class Real32(value: Float) extends Operand // ... def addition[T <: Operand](left: T, right: T): T = (left, right) match { case (Int32 (l), Int32 (r)) => Int32 (l + r) case (Real32(l), Real32(r)) => Real32(l + r) // ... } Object Oriented sealed trait Operand case class Int32(value: Int) extends Operand { def add(other: Int) = Int32(value + other) def subtract(other: Int) = Int32(value - other) // ... } case class Real32(value: Float) extends Operand { def add(other: Float) = Real32(value + other) def subtract(other: Float) = Real32(value - other) // ... }
  • 5. Two Views of the Same Problem Pattern Matching sealed trait Operand case class Int32(value: Int) extends Operand case class Real32(value: Float) extends Operand // ... def addition[T <: Operand](left: T, right: T): T = (left, right) match { case (Int32 (l), Int32 (r)) => Int32 (l + r) case (Real32(l), Real32(r)) => Real32(l + r) // ... } Object Oriented sealed trait Operand case class Int32(value: Int) extends Operand { def add(other: Int) = Int32(value + other) def subtract(other: Int) = Int32(value - other) // ... } case class Real32(value: Float) extends Operand { def add(other: Float) = Real32(value + other) def subtract(other: Float) = Real32(value - other) // ... }
  • 6. Two Views of the Same Problem Pattern Matching sealed trait Operand case class Int32(value: Int) extends Operand case class Real32(value: Float) extends Operand // ... def addition[T <: Operand](left: T, right: T): T = (left, right) match { case (Int32 (l), Int32 (r)) => Int32 (l + r) case (Real32(l), Real32(r)) => Real32(l + r) // ... } Object Oriented sealed trait Operand case class Int32(value: Int) extends Operand { def add(other: Int) = Int32(value + other) def subtract(other: Int) = Int32(value - other) // ... } case class Real32(value: Float) extends Operand { def add(other: Float) = Real32(value + other) def subtract(other: Float) = Real32(value - other) // ... }
  • 7. TYPE CLASSES TO THE RESCUE
  • 8. What’s a Type Class? • A type class: – Enables ad-hoc polymorphism – Statically typed (i.e. type-safe) – Borrowed from Haskell • Solves the expression problem: – Behavior can be extended – … at compile-time – ... after the fact – … without changing/recompiling existing code
  • 9. Example #1: Equality • Scala inherits legacy aspects of Java – This includes AnyRef.equals: def equals( other: AnyRef ): Boolean – So the following compiles: 3.14159265359 == "pi" // Evaluates to false • What if we wanted to implement type-safe equality? – Let’s define a type-safe isEqual function: isEqual( 3.14159265359, "pi” ) // Does not compile!
  • 10. What’s in a Type Class? • Three components are required: – A signature – Implementations for supported types – A function that requires a type class This is where things get hairy.
  • 11. Slight Digression • A method in Scala can have multiple parameter lists: def someMethod( x: Int )( y: String )( z: Double ): Unit = { println( s"x=$x, y=$y, z=$z" ) } scala> someMethod( 10 )( "abc" )( scala.math.Pi ) x=10, y=abc, z=3.141592653589793 • There are multiple uses for this, but the most important is…
  • 12. Scala Implicits • The last parameter list of a method can be marked implicit • Implicit parameters are filled in by the compiler – In effect, you require evidence of the compiler – … such as the existence of a type class in scope – You can also specify parameters explicitly, if needed
  • 13. Putting it together • Let’s define our type class: trait Equality[ L, R ] { def equals( left: L, right: R ): Boolean } • … and our isEqual function: def isEqual[ L, R ]( left: L, right: R ) ( implicit ev: Equality[ L, R ] ): Boolean = ev.equals( left, right ) This is where the magic happens
  • 14. Still missing something! • We have no implementations of the Equality trait, so nothing works! scala> isEqual( 3, 3 ) <console>:10: error: could not find implicit value for parameter ev: Equality[Int,Int] • We need to implement Equality[ T, T ]: implicit def sameTypeEquality[ T ] = new Equality[ T, T ] { def equals( left: T, right: T ) = left.equals( right ) } • And now it works: scala> isEqual( 3, 3 ) res1: Boolean = true
  • 15. Ad-hoc Polymorphism • Now we’ve met our original goal: scala> isEqual( 3.14159265359, "pi" ) <console>:11: error: could not find implicit value for parameter ev: Equality[Double,String] • But what if we wanted to equate doubles and strings? • Well then, let’s add another implementation! implicit object DoubleEqualsString extends Equality[ Double, String ] { def equals( left: Double, right: String ) = left.toString == right } • Et voila, no recompilation or code changes needed: scala> isEqual( 3.14159265359, "pi" ) res5: Boolean = false
  • 17. Example #2: Sort Me, Maybe • Let’s implement a sort function (e.g. bubble sort) • With one caveat: – It should operate on any type – … for which an ordering exists • Obviously, we’ll use type classes!
  • 18. Possible Solution trait Ordering[ T ] { def isLessThan( left: T, right: T ): Boolean } def sort[ T ]( items: Seq[ T ] )( implicit ord: Ordering[ T ] ): Seq[ T ] = { val buffer = mutable.ArrayBuffer( items:_* ) for ( i <- 0 until items.size; j <- ( i + 1 ) until items.size ) if ( ord.isLessThan( buffer( j ), buffer( i ) ) ) { val temp = buffer( i ) buffer( i ) = buffer( j ) buffer( j ) = temp } buffer }
  • 19. Possible Solution, cont. • Sample implementation for integers: implicit object IntOrdering extends Ordering[ Int ] { def isLessThan( left: Int, right: Int ) = left < right } val numbers = Seq( 4, 1, 10, 8, 14, 2 ) Assert( sort( numbers ) == Seq( 1, 2, 4, 8, 10, 14 ) )
  • 20. Possible Solution, cont. • Sample implementation for a domain entity: case class Person( name: String, age: Int ) implicit object PersonOrdering extends Ordering[ Person ] { def isLessThan( left: Person, right: Person ) = left.age < right.age } val haim = Person( "Haim", 12 ) val dafna = Person( "Dafna", 20 ) val ofer = Person( "Ofer", 1 ) assert( sort( Seq( haim, dafna, ofer ) ) == Seq( ofer, haim, dafna ) )
  • 21. Implicit Search Order Current Scope • Defined implicits • Explicit imports • Wildcard imports Companion • … of T • … of supertypes of T Outer Scope • Enclosing class
  • 23. Example #3: Server Pipeline • REST is good, but annoying to write. Let’s simplify: case class DTO( message: String ) class MyServlet extends NicerHttpServlet { private val counter = new AtomicInteger( 0 ) get( "/service" ) { counter.incrementAndGet() DTO( "hello, world!" ) } get( "/count" ) { counter.get() } } Uses return value; no direct response manipulation
  • 24. Example #3: Server Pipeline • What’s in a server? – Routing – Rendering – Error handling • Let’s focus on rendering: trait ResponseRenderer[ T ] { def render( value : T, request : HttpServletRequest, response: HttpServletResponse ): Unit }
  • 25. Example #3: Server Pipeline • A couple of basic renderers: implicit object StringRenderer extends ResponseRenderer[ String ] { def render( value: String, request: HttpServletRequest, response: HttpServletResponse ) = { val w = response.getWriter try w.write( value ) finally w.close() } } implicit object IntRenderer extends ResponseRenderer[ Int ] { def render( value: Int, request: HttpServletRequest, response: HttpServletResponse ) = implicitly[ ResponseRenderer[ String ] ].render( value.toString, request, response ) }
  • 26. Example #3: Server Pipeline • Putting it together: trait NicerHttpServlet extends HttpServlet { private trait Handler { type Response def result: Response def renderer: ResponseRenderer[ Response ] } private var handlers: Map[ String, Handler ] = Map.empty protected def get[ T : ResponseRenderer ]( url: String )( thunk: => T ) = handlers += url -> new Handler { type Response = T def result = thunk def renderer = implicitly[ ResponseRenderer[ T ] ] }
  • 27. Example #3: Server Pipeline • And finally: override def doGet( req: HttpServletRequest, resp: HttpServletResponse ) = handlers.get( req.getRequestURI ) match { case None => resp.sendError( HttpServletResponse.SC_NOT_FOUND ) case Some( handler ) => try handler.renderer.render( handler.result, req, resp ) catch { case e: Exception => resp.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR ) } }
  • 29. Example #4: JSON Serialization • Assume we already have a good model for JSON • How do we add type-safe serialization? JsonValue JsonObject JsonArray JsonBoolean JsonInt JsonDouble JsonNull JsonField
  • 30. Example #4: JSON Serialization • Let’s start with a typeclass: trait JsonSerializer[ T ] { def serialize( value: T ): JsonValue def deserialize( value: JsonValue ): T } • And the corresponding library signature: def serialize[ T ]( instance: T )( implicit ser: JsonSerializer[ T ] ) = ser.serialize( instance ) def deserialize[ T ]( json: JsonValue )( implicit ser: JsonSerializer[ T ] ) = ser.deserialize( json )
  • 31. Example #4: JSON Serialization • Define a few basic serializers… implicit object BooleanSerializer extends JsonSerializer[ Boolean ] { def serialize( value: Boolean ) = JsonBoolean( value ) def deserialize( value: JsonValue ) = value match { case JsonBoolean( bool ) => bool case other => error( other ) } } implicit object StringSerializer extends JsonSerializer[ String ] { def serialize( value: String ) = JsonString( value ) def deserialize( value: JsonValue ) = value match { case JsonString( string ) => string case other => error( other ) } }
  • 32. Example #4: JSON Serialization • We can also handle nested structures – The compiler resolves typeclasses recursively! • For example, Option[ T ] : implicit def optionSerializer[ T ]( implicit ser: JsonSerializer[ T ] ) = new JsonSerializer[ Option[ T ] ] { def serialize( value: Option[ T ] ) = value map ser.serialize getOrElse JsonNull def deserialize( value: JsonValue ) = value match { case JsonNull => None case other => Some( ser.deserialize( other ) ) } } Require a serializer for T … and delegate to it
  • 33. Example #4: JSON Serialization • What about an arbitrary type? case class Person( name: String, surname: String, age: Int ) implicit object PersonSerializer extends JsonSerializer[ Person ] { def serialize( value: Person ) = JsonObject( JsonField( "name", serialize( value.name ) ), JsonField( "surname", serialize( value.surname ) ), JsonField( "age", serialize( value.age ) ) ) def deserialize( value: JsonValue ) = value match { case obj: JsonObject => Person( name = deserialize[ String ]( obj "name" ), surname = deserialize[ String ]( obj "surname" ), age = deserialize[ Int ]( obj "age" ) ) case _ => error( value ) } }
  • 34. Summary • We added serialization for Person after the fact without… – … modifying the serialization framework – … modifying the domain object • We did not compromise: – … type safety or performance – … modularity or encapsulation • This applies everywhere! clients of either are unaffected!
  • 35. … and we’re done • Thank you for your time! • Questions/comments? – tomer@tomergabel.com – @tomerg – http://www.tomergabel.com • Code samples: – http://git.io/aWc9eQ

Notes de l'éditeur

  1. Source: http://en.wikipedia.org/wiki/Expression_problem