SlideShare a Scribd company logo
Leveraging Scala 
Macros for Better 
Validation 
Tomer Gabel, Wix 
September 2014
I Have a Dream 
• Definition: 
case class Person( 
firstName: String, 
lastName: String 
) 
implicit val personValidator = 
validator[Person] { p ⇒ 
p.firstName is notEmpty 
p.lastName is notEmpty 
}
I Have a Dream 
• Usage: 
validate(Person("Wernher", "von Braun”)) 
== Success 
validate(Person("", "No First Name”)) 
== Failure(Set(RuleViolation( 
value = "", 
constraint = "must not be empty", 
description = "firstName" 
)))
ENTER: ACCORD.
Basic Architecture 
API 
Combinator Library 
DSL 
Macro Transformation
The Accord API 
• Validation can succeed or fail 
• A failure comprises one or more violations 
sealed trait Result 
case object Success extends Result 
case class Failure(violations: Set[Violation]) 
extends Result 
• The validator typeclass: 
trait Validator[-T] extends (T ⇒ Result)
Why Macros? 
• Quick refresher: 
implicit val personValidator = 
validator[Person] { p ⇒ 
p.firstName is notEmpty 
p.lastName is notEmpty 
} 
Implicit “and” 
Automatic description 
generation
Full Disclosure 
Macros are experimental 
Macros are hard 
I will gloss over a lot of details 
… and simplify a lot of things
Abstract Syntax Trees 
• An intermediate representation of code 
– Structure (semantics) 
– Metadata (e.g. types) – optional! 
• Provided by the reflection API 
• Alas, mutable 
– Until Dotty comes along
Abstract Syntax Trees 
def method(param: String) = param.toUpperCase
Abstract Syntax Trees 
def method(param: String) = param.toUpperCase 
Apply( 
Select( 
Ident(newTermName("param")), 
newTermName("toUpperCase") 
), 
List() 
)
Abstract Syntax Trees 
def method(param: String) = param.toUpperCase 
ValDef( 
Modifiers(PARAM), 
newTermName("param"), 
Select( 
Ident(scala.Predef), 
newTypeName("String") 
), 
EmptyTree // Value 
)
Abstract Syntax Trees 
def method(param: String) = param.toUpperCase 
DefDef( 
Modifiers(), 
newTermName("method"), 
List(), // Type parameters 
List( // Parameter lists 
List(parameter) 
), 
TypeTree(), // Return type 
implementation 
)
Def Macro 101 
• Looks and acts like a normal function 
def radix(s: String, base: Int): Long 
val result = radix("2710", 16) 
// result == 10000L 
• Two fundamental differences: 
– Invoked at compile time instead of runtime 
– Operates on ASTs instead of values
Def Macro 101 
• Needs a signature & implementation 
def radix(s: String, base: Int): Long = 
macro radixImpl 
def radixImpl 
Values 
(c: Context) 
(s: c.Expr[String], base: c.Expr[Int]): 
c.Expr[Long] 
ASTs
Def Macro 101 
• What’s in a context? 
– Enclosures (position) 
– Error handling 
– Logging 
– Infrastructure
Basic Architecture 
API 
Combinator Library 
DSL 
Macro Transformation
Overview 
implicit val personValidator = 
validator[Person] { p ⇒ 
p.firstName is notEmpty 
p.lastName is notEmpty 
} 
• The validator macro: 
Macro Application 
Validation Rules 
– Rewrites each rule by addition a description 
– Aggregates rules with an and combinator
Signature 
def validator[T](v: T ⇒ Unit): Validator[T] = 
macro ValidationTransform.apply[T] 
def apply[T : c.WeakTypeTag] 
(c: Context) 
(v: c.Expr[T ⇒ Unit]): 
c.Expr[Validator[T]]
Brace 
yourselves 
Here be dragons
Walkthrough 
Search for rule 
Process rule 
Generate description 
Rewrite rule
Walkthrough 
Search for rule 
Process rule 
Generate description 
Rewrite rule
Search for Rule 
• A rule is an expression of type Validator[_] 
• We search by: 
– Recursively pattern matching over an AST 
– On match, apply a function on the subtree 
– Encoded as a partial function from Tree to R
Search for Rule 
def collectFromPattern[R] 
(tree: Tree) 
(pattern: PartialFunction[Tree, R]): List[R] = { 
var found: Vector[R] = Vector.empty 
new Traverser { 
override def traverse(subtree: Tree) { 
if (pattern isDefinedAt subtree) 
found = found :+ pattern(subtree) 
else 
super.traverse(subtree) 
} 
}.traverse(tree) 
found.toList 
}
Search for Rule 
• Putting it together: 
case class Rule(ouv: Tree, validation: Tree) 
def processRule(subtree: Tree): Rule = ??? 
def findRules(body: Tree): Seq[Rule] = { 
val validatorType = typeOf[Validator[_]] 
collectFromPattern(body) { 
case subtree if subtree.tpe <:< validatorType ⇒ 
processRule(subtree) 
} 
}
Walkthrough 
Search for rule 
Process rule 
Generate description 
Rewrite rule
Process Rule 
• The user writes: 
p.firstName is notEmpty 
• The compiler emits: 
Type: Validator[_] 
Contextualizer(p.firstName).is(notEmpty) 
Object Under Validation 
(OUV) 
Validation
Process Rule 
Contextualizer(p.firstName).is(notEmpty) 
• This is effectively an Apply AST node 
• The left-hand side is the OUV 
• The right-hand side is the validation 
– But we can use the entire expression! 
• Contextualizer is our entry point
Process Rule 
Contextualizer(p.firstName).is(notEmpty) 
Apply 
Select 
Apply 
TypeApply 
Contextualizer 
String 
Select 
Ident(“p”) 
firstName 
is 
notEmpty
Process Rule 
Contextualizer(p.firstName).is(notEmpty) 
Apply 
Select 
Apply 
TypeApply 
Contextualizer 
String 
Select 
Ident(“p”) 
firstName 
is 
notEmpty
Process Rule 
Apply 
TypeApply 
Contextualizer 
String 
Select 
Ident(“p”) 
firstName
Process Rule 
Apply 
TypeApply 
Contextualizer 
Φ 
Select 
Ident(“p”) 
firstName
Process Rule 
Apply 
TypeApply 
Contextualizer 
Φ 
Select 
Ident(“p”) 
firstName
Process Rule 
Apply 
TypeApply 
Contextualizer 
Φ 
OUV 
Φ 
Φ 
case Apply(TypeApply(Select(_, `term`), _), ouv :: Nil) ⇒
Process Rule 
• Putting it together: 
val term = newTermName("Contextualizer") 
def processRule(subtree: Tree): Rule = 
extractFromPattern(subtree) { 
case Apply(TypeApply(Select(_, `term`), _), ouv :: Nil) ⇒ 
Rule(ouv, subtree) 
} getOrElse abort(subtree.pos, "Not a valid rule")
Walkthrough 
Search for rule 
Process rule 
Generate description 
Rewrite rule
Generate Description 
Contextualizer(p.firstName).is(notEmpty) 
• Consider the object under validation 
• In this example, it is a field accessor 
• The function prototype is the entry point 
Select 
Ident(“p”) 
firstName 
validator[Person] { p ⇒ 
... 
}
Generate Description 
• How to get at the prototype? 
• The macro signature includes the rule block: 
def apply[T : c.WeakTypeTag] 
(c: Context) 
(v: c.Expr[T ⇒ Unit]): 
c.Expr[Validator[T]] 
• To extract the prototype: 
val Function(prototype :: Nil, body) = 
v.tree // prototype: ValDef
Generate Description 
• Putting it all together: 
def describeRule(rule: ValidationRule) = { 
val para = prototype.name 
val Select(Ident(`para`), description) = 
rule.ouv 
description.toString 
}
Walkthrough 
Search for rule 
Process rule 
Generate description 
Rewrite rule
Rewrite Rule 
• We’re constructing a Validator[Person] 
• A rule is itself a Validator[T]. For example: 
Contextualizer(p.firstName).is(notEmpty) 
• We need to: 
– Lift the rule to validate the enclosing type 
– Apply the description to the result
Quasiquotes 
• Provide an easy way to construct ASTs: 
Apply( 
Select( 
Ident(newTermName"x"), 
newTermName("$plus") 
), 
List( 
Ident(newTermName("y")) 
) 
) 
q"x + y"
Quasiquotes 
• Quasiquotes also let you splice trees: 
def greeting(whom: c.Expr[String]) = 
q"Hello "$whom"!" 
• And can be used in pattern matching: 
val q"$x + $y" = tree
Rewrite Rule 
Contextualizer(p.firstName).is(notEmpty) 
new Validator[Person] { 
def apply(p: Person) = { 
val validation = 
Contextualizer(p.firstName).is(notEmpty) 
validation(p.firstName) withDescription "firstName" 
} 
}
Rewrite Rule 
• Putting it all together: 
def rewriteRule(rule: ValidationRule) = { 
val desc = describeRule(rule) 
val tree = Literal(Constant(desc)) 
q""" 
new com.wix.accord.Validator[${weakTypeOf[T]}] { 
def apply($prototype) = { 
val validation = ${rule.validation} 
validation(${rule.ouv}) withDescription $tree 
} 
} 
""" 
}
The Last Mile
Epilogue 
• The finishing touch: and combinator 
def apply[T : c.WeakTypeTag] 
(c: Context) 
(v: c.Expr[T ⇒ Unit]): c.Expr[Validator[T]] = { 
val Function(prototype :: Nil, body) = v.tree 
// ... all the stuff we just discussed 
val rules = findRules(body) map rewriteRule 
val result = 
q"new com.wix.accord.combinators.And(..$rules)" 
c.Expr[Validator[T]](result) 
}
tomer@tomergabel.com 
@tomerg 
http://il.linkedin.com/in/tomergabel 
Check out Accord at: 
http://github.com/wix/accord 
Thank you for listening 
WE’RE DONE 
HERE!

More Related Content

What's hot

Gorm
GormGorm
The Ring programming language version 1.8 book - Part 41 of 202
The Ring programming language version 1.8 book - Part 41 of 202The Ring programming language version 1.8 book - Part 41 of 202
The Ring programming language version 1.8 book - Part 41 of 202
Mahmoud Samir Fayed
 
Postgres rules
Postgres rulesPostgres rules
Postgres rules
gisborne
 
Java Cheat Sheet
Java Cheat SheetJava Cheat Sheet
Java Cheat Sheet
Saeid Zebardast
 
The Ring programming language version 1.5.1 book - Part 34 of 180
The Ring programming language version 1.5.1 book - Part 34 of 180The Ring programming language version 1.5.1 book - Part 34 of 180
The Ring programming language version 1.5.1 book - Part 34 of 180
Mahmoud Samir Fayed
 
Python : Dictionaries
Python : DictionariesPython : Dictionaries
Groovy unleashed
Groovy unleashed Groovy unleashed
Groovy unleashed
Isuru Samaraweera
 
The Ring programming language version 1.5.1 book - Part 33 of 180
The Ring programming language version 1.5.1 book - Part 33 of 180The Ring programming language version 1.5.1 book - Part 33 of 180
The Ring programming language version 1.5.1 book - Part 33 of 180
Mahmoud Samir Fayed
 
Grails GORM - You Know SQL. You Know Queries. Here's GORM.
Grails GORM - You Know SQL. You Know Queries. Here's GORM.Grails GORM - You Know SQL. You Know Queries. Here's GORM.
Grails GORM - You Know SQL. You Know Queries. Here's GORM.
Ted Vinke
 
Clojure vs Design Patterns
Clojure vs Design PatternsClojure vs Design Patterns
Clojure vs Design Patterns
Ron Toland
 
Json and SQL DB serialization Introduction with Play! and Slick
Json and SQL DB serialization Introduction with Play! and SlickJson and SQL DB serialization Introduction with Play! and Slick
Json and SQL DB serialization Introduction with Play! and SlickStephen Kemmerling
 
What is new in Java 8
What is new in Java 8What is new in Java 8
What is new in Java 8
Sandeep Kr. Singh
 
Scala 2013 review
Scala 2013 reviewScala 2013 review
Scala 2013 review
Sagie Davidovich
 
An introduction to property-based testing
An introduction to property-based testingAn introduction to property-based testing
An introduction to property-based testing
Vincent Pradeilles
 
MCE^3 - Hannes Verlinde - Let The Symbols Do The Work
MCE^3 - Hannes Verlinde - Let The Symbols Do The WorkMCE^3 - Hannes Verlinde - Let The Symbols Do The Work
MCE^3 - Hannes Verlinde - Let The Symbols Do The Work
PROIDEA
 
Python programming : Standard Input and Output
Python programming : Standard Input and OutputPython programming : Standard Input and Output
Python programming : Standard Input and Output
Emertxe Information Technologies Pvt Ltd
 

What's hot (17)

Gorm
GormGorm
Gorm
 
The Ring programming language version 1.8 book - Part 41 of 202
The Ring programming language version 1.8 book - Part 41 of 202The Ring programming language version 1.8 book - Part 41 of 202
The Ring programming language version 1.8 book - Part 41 of 202
 
Postgres rules
Postgres rulesPostgres rules
Postgres rules
 
Java Cheat Sheet
Java Cheat SheetJava Cheat Sheet
Java Cheat Sheet
 
The Ring programming language version 1.5.1 book - Part 34 of 180
The Ring programming language version 1.5.1 book - Part 34 of 180The Ring programming language version 1.5.1 book - Part 34 of 180
The Ring programming language version 1.5.1 book - Part 34 of 180
 
Python : Dictionaries
Python : DictionariesPython : Dictionaries
Python : Dictionaries
 
Groovy unleashed
Groovy unleashed Groovy unleashed
Groovy unleashed
 
The Ring programming language version 1.5.1 book - Part 33 of 180
The Ring programming language version 1.5.1 book - Part 33 of 180The Ring programming language version 1.5.1 book - Part 33 of 180
The Ring programming language version 1.5.1 book - Part 33 of 180
 
Grails GORM - You Know SQL. You Know Queries. Here's GORM.
Grails GORM - You Know SQL. You Know Queries. Here's GORM.Grails GORM - You Know SQL. You Know Queries. Here's GORM.
Grails GORM - You Know SQL. You Know Queries. Here's GORM.
 
Clojure vs Design Patterns
Clojure vs Design PatternsClojure vs Design Patterns
Clojure vs Design Patterns
 
Json and SQL DB serialization Introduction with Play! and Slick
Json and SQL DB serialization Introduction with Play! and SlickJson and SQL DB serialization Introduction with Play! and Slick
Json and SQL DB serialization Introduction with Play! and Slick
 
What is new in Java 8
What is new in Java 8What is new in Java 8
What is new in Java 8
 
JS OO and Closures
JS OO and ClosuresJS OO and Closures
JS OO and Closures
 
Scala 2013 review
Scala 2013 reviewScala 2013 review
Scala 2013 review
 
An introduction to property-based testing
An introduction to property-based testingAn introduction to property-based testing
An introduction to property-based testing
 
MCE^3 - Hannes Verlinde - Let The Symbols Do The Work
MCE^3 - Hannes Verlinde - Let The Symbols Do The WorkMCE^3 - Hannes Verlinde - Let The Symbols Do The Work
MCE^3 - Hannes Verlinde - Let The Symbols Do The Work
 
Python programming : Standard Input and Output
Python programming : Standard Input and OutputPython programming : Standard Input and Output
Python programming : Standard Input and Output
 

Similar to Leveraging Scala Macros for Better Validation

Java gets a closure
Java gets a closureJava gets a closure
Java gets a closure
Tomasz Kowalczewski
 
PhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown Parts
Bastian Feder
 
Ti1220 Lecture 7: Polymorphism
Ti1220 Lecture 7: PolymorphismTi1220 Lecture 7: Polymorphism
Ti1220 Lecture 7: PolymorphismEelco Visser
 
Scala in Places API
Scala in Places APIScala in Places API
Scala in Places API
Łukasz Bałamut
 
Test in action week 2
Test in action   week 2Test in action   week 2
Test in action week 2Yi-Huan Chan
 
Php unit the-mostunknownparts
Php unit the-mostunknownpartsPhp unit the-mostunknownparts
Php unit the-mostunknownparts
Bastian Feder
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
Java 8 presentation
Java 8 presentationJava 8 presentation
Java 8 presentation
Van Huong
 
API first with Swagger and Scala by Slava Schmidt
API first with Swagger and Scala by  Slava SchmidtAPI first with Swagger and Scala by  Slava Schmidt
API first with Swagger and Scala by Slava Schmidt
JavaDayUA
 
Icsug dev day2014_road to damascus - conversion experience-lotusscript and @f...
Icsug dev day2014_road to damascus - conversion experience-lotusscript and @f...Icsug dev day2014_road to damascus - conversion experience-lotusscript and @f...
Icsug dev day2014_road to damascus - conversion experience-lotusscript and @f...
ICS User Group
 
Java 8 Workshop
Java 8 WorkshopJava 8 Workshop
Java 8 Workshop
Mario Fusco
 
An excuse to Try, Either, folding, and Future. sequence
An excuse to Try, Either, folding, and Future. sequenceAn excuse to Try, Either, folding, and Future. sequence
An excuse to Try, Either, folding, and Future. sequence
Germán Ferrari
 
Session 08 - Arrays and Methods
Session 08 - Arrays and MethodsSession 08 - Arrays and Methods
Session 08 - Arrays and Methods
SiddharthSelenium
 
Java SE 8
Java SE 8Java SE 8
ScalaBlitz
ScalaBlitzScalaBlitz

Similar to Leveraging Scala Macros for Better Validation (20)

Java gets a closure
Java gets a closureJava gets a closure
Java gets a closure
 
Scala
ScalaScala
Scala
 
PhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown Parts
 
Ti1220 Lecture 7: Polymorphism
Ti1220 Lecture 7: PolymorphismTi1220 Lecture 7: Polymorphism
Ti1220 Lecture 7: Polymorphism
 
Scala in Places API
Scala in Places APIScala in Places API
Scala in Places API
 
Test in action week 2
Test in action   week 2Test in action   week 2
Test in action week 2
 
Php unit the-mostunknownparts
Php unit the-mostunknownpartsPhp unit the-mostunknownparts
Php unit the-mostunknownparts
 
Php & my sql
Php & my sqlPhp & my sql
Php & my sql
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
Java 8 presentation
Java 8 presentationJava 8 presentation
Java 8 presentation
 
API first with Swagger and Scala by Slava Schmidt
API first with Swagger and Scala by  Slava SchmidtAPI first with Swagger and Scala by  Slava Schmidt
API first with Swagger and Scala by Slava Schmidt
 
Icsug dev day2014_road to damascus - conversion experience-lotusscript and @f...
Icsug dev day2014_road to damascus - conversion experience-lotusscript and @f...Icsug dev day2014_road to damascus - conversion experience-lotusscript and @f...
Icsug dev day2014_road to damascus - conversion experience-lotusscript and @f...
 
Java 8 Workshop
Java 8 WorkshopJava 8 Workshop
Java 8 Workshop
 
An excuse to Try, Either, folding, and Future. sequence
An excuse to Try, Either, folding, and Future. sequenceAn excuse to Try, Either, folding, and Future. sequence
An excuse to Try, Either, folding, and Future. sequence
 
Session 08 - Arrays and Methods
Session 08 - Arrays and MethodsSession 08 - Arrays and Methods
Session 08 - Arrays and Methods
 
Java SE 8
Java SE 8Java SE 8
Java SE 8
 
ScalaBlitz
ScalaBlitzScalaBlitz
ScalaBlitz
 

More from Tomer Gabel

How shit works: Time
How shit works: TimeHow shit works: Time
How shit works: Time
Tomer 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 Us
Tomer Gabel
 
Slaying Sacred Cows: Deconstructing Dependency Injection
Slaying Sacred Cows: Deconstructing Dependency InjectionSlaying Sacred Cows: Deconstructing Dependency Injection
Slaying Sacred Cows: Deconstructing Dependency Injection
Tomer Gabel
 
An Abridged Guide to Event Sourcing
An Abridged Guide to Event SourcingAn Abridged Guide to Event Sourcing
An Abridged Guide to Event Sourcing
Tomer Gabel
 
How shit works: the CPU
How shit works: the CPUHow shit works: the CPU
How shit works: the CPU
Tomer Gabel
 
How Shit Works: Storage
How Shit Works: StorageHow Shit Works: Storage
How Shit Works: Storage
Tomer 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 Story
Tomer Gabel
 
The Wix Microservice Stack
The Wix Microservice StackThe Wix Microservice Stack
The Wix Microservice Stack
Tomer 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
 
Scala Refactoring for Fun and Profit
Scala Refactoring for Fun and ProfitScala Refactoring for Fun and Profit
Scala Refactoring for Fun and Profit
Tomer Gabel
 
Onboarding at Scale
Onboarding at ScaleOnboarding at Scale
Onboarding at Scale
Tomer Gabel
 
Scala in the Wild
Scala in the WildScala in the Wild
Scala in the Wild
Tomer Gabel
 
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
 
Put Your Thinking CAP On
Put Your Thinking CAP OnPut Your Thinking CAP On
Put Your Thinking CAP On
Tomer 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 Scala
Tomer 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
 
Scala Back to Basics: Type Classes
Scala Back to Basics: Type ClassesScala Back to Basics: Type Classes
Scala Back to Basics: Type Classes
Tomer Gabel
 
5 Bullets to Scala Adoption
5 Bullets to Scala Adoption5 Bullets to Scala Adoption
5 Bullets to Scala Adoption
Tomer 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 Scala
Tomer Gabel
 

More from 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)
 
Scala Refactoring for Fun and Profit
Scala Refactoring for Fun and ProfitScala Refactoring for Fun and Profit
Scala Refactoring for Fun and Profit
 
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
 
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)
 
Put Your Thinking CAP On
Put Your Thinking CAP OnPut Your Thinking CAP On
Put Your Thinking CAP On
 
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)
 
Scala Back to Basics: Type Classes
Scala Back to Basics: Type ClassesScala Back to Basics: Type Classes
Scala Back to Basics: Type Classes
 
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
 

Recently uploaded

Navigating the Metaverse: A Journey into Virtual Evolution"
Navigating the Metaverse: A Journey into Virtual Evolution"Navigating the Metaverse: A Journey into Virtual Evolution"
Navigating the Metaverse: A Journey into Virtual Evolution"
Donna Lenk
 
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Mind IT Systems
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
Globus
 
How to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good PracticesHow to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good Practices
Globus
 
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.ILBeyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Natan Silnitsky
 
First Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User EndpointsFirst Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User Endpoints
Globus
 
A Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of PassageA Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of Passage
Philip Schwarz
 
Globus Compute Introduction - GlobusWorld 2024
Globus Compute Introduction - GlobusWorld 2024Globus Compute Introduction - GlobusWorld 2024
Globus Compute Introduction - GlobusWorld 2024
Globus
 
BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024
Ortus Solutions, Corp
 
Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604
Fermin Galan
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownload
vrstrong314
 
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
Juraj Vysvader
 
Graphic Design Crash Course for beginners
Graphic Design Crash Course for beginnersGraphic Design Crash Course for beginners
Graphic Design Crash Course for beginners
e20449
 
How Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptxHow Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptx
wottaspaceseo
 
Cyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdfCyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdf
Cyanic lab
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar
 
Understanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSageUnderstanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSage
Globus
 
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoamOpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
takuyayamamoto1800
 
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Globus
 
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data AnalysisProviding Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Globus
 

Recently uploaded (20)

Navigating the Metaverse: A Journey into Virtual Evolution"
Navigating the Metaverse: A Journey into Virtual Evolution"Navigating the Metaverse: A Journey into Virtual Evolution"
Navigating the Metaverse: A Journey into Virtual Evolution"
 
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
Custom Healthcare Software for Managing Chronic Conditions and Remote Patient...
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
 
How to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good PracticesHow to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good Practices
 
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.ILBeyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
 
First Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User EndpointsFirst Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User Endpoints
 
A Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of PassageA Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of Passage
 
Globus Compute Introduction - GlobusWorld 2024
Globus Compute Introduction - GlobusWorld 2024Globus Compute Introduction - GlobusWorld 2024
Globus Compute Introduction - GlobusWorld 2024
 
BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024
 
Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604Orion Context Broker introduction 20240604
Orion Context Broker introduction 20240604
 
top nidhi software solution freedownload
top nidhi software solution freedownloadtop nidhi software solution freedownload
top nidhi software solution freedownload
 
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
In 2015, I used to write extensions for Joomla, WordPress, phpBB3, etc and I ...
 
Graphic Design Crash Course for beginners
Graphic Design Crash Course for beginnersGraphic Design Crash Course for beginners
Graphic Design Crash Course for beginners
 
How Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptxHow Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptx
 
Cyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdfCyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdf
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBroker
 
Understanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSageUnderstanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSage
 
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoamOpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
 
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
Innovating Inference - Remote Triggering of Large Language Models on HPC Clus...
 
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data AnalysisProviding Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
 

Leveraging Scala Macros for Better Validation

  • 1. Leveraging Scala Macros for Better Validation Tomer Gabel, Wix September 2014
  • 2. I Have a Dream • Definition: case class Person( firstName: String, lastName: String ) implicit val personValidator = validator[Person] { p ⇒ p.firstName is notEmpty p.lastName is notEmpty }
  • 3. I Have a Dream • Usage: validate(Person("Wernher", "von Braun”)) == Success validate(Person("", "No First Name”)) == Failure(Set(RuleViolation( value = "", constraint = "must not be empty", description = "firstName" )))
  • 5. Basic Architecture API Combinator Library DSL Macro Transformation
  • 6. The Accord API • Validation can succeed or fail • A failure comprises one or more violations sealed trait Result case object Success extends Result case class Failure(violations: Set[Violation]) extends Result • The validator typeclass: trait Validator[-T] extends (T ⇒ Result)
  • 7. Why Macros? • Quick refresher: implicit val personValidator = validator[Person] { p ⇒ p.firstName is notEmpty p.lastName is notEmpty } Implicit “and” Automatic description generation
  • 8. Full Disclosure Macros are experimental Macros are hard I will gloss over a lot of details … and simplify a lot of things
  • 9. Abstract Syntax Trees • An intermediate representation of code – Structure (semantics) – Metadata (e.g. types) – optional! • Provided by the reflection API • Alas, mutable – Until Dotty comes along
  • 10. Abstract Syntax Trees def method(param: String) = param.toUpperCase
  • 11. Abstract Syntax Trees def method(param: String) = param.toUpperCase Apply( Select( Ident(newTermName("param")), newTermName("toUpperCase") ), List() )
  • 12. Abstract Syntax Trees def method(param: String) = param.toUpperCase ValDef( Modifiers(PARAM), newTermName("param"), Select( Ident(scala.Predef), newTypeName("String") ), EmptyTree // Value )
  • 13. Abstract Syntax Trees def method(param: String) = param.toUpperCase DefDef( Modifiers(), newTermName("method"), List(), // Type parameters List( // Parameter lists List(parameter) ), TypeTree(), // Return type implementation )
  • 14. Def Macro 101 • Looks and acts like a normal function def radix(s: String, base: Int): Long val result = radix("2710", 16) // result == 10000L • Two fundamental differences: – Invoked at compile time instead of runtime – Operates on ASTs instead of values
  • 15. Def Macro 101 • Needs a signature & implementation def radix(s: String, base: Int): Long = macro radixImpl def radixImpl Values (c: Context) (s: c.Expr[String], base: c.Expr[Int]): c.Expr[Long] ASTs
  • 16. Def Macro 101 • What’s in a context? – Enclosures (position) – Error handling – Logging – Infrastructure
  • 17. Basic Architecture API Combinator Library DSL Macro Transformation
  • 18. Overview implicit val personValidator = validator[Person] { p ⇒ p.firstName is notEmpty p.lastName is notEmpty } • The validator macro: Macro Application Validation Rules – Rewrites each rule by addition a description – Aggregates rules with an and combinator
  • 19. Signature def validator[T](v: T ⇒ Unit): Validator[T] = macro ValidationTransform.apply[T] def apply[T : c.WeakTypeTag] (c: Context) (v: c.Expr[T ⇒ Unit]): c.Expr[Validator[T]]
  • 20. Brace yourselves Here be dragons
  • 21. Walkthrough Search for rule Process rule Generate description Rewrite rule
  • 22. Walkthrough Search for rule Process rule Generate description Rewrite rule
  • 23. Search for Rule • A rule is an expression of type Validator[_] • We search by: – Recursively pattern matching over an AST – On match, apply a function on the subtree – Encoded as a partial function from Tree to R
  • 24. Search for Rule def collectFromPattern[R] (tree: Tree) (pattern: PartialFunction[Tree, R]): List[R] = { var found: Vector[R] = Vector.empty new Traverser { override def traverse(subtree: Tree) { if (pattern isDefinedAt subtree) found = found :+ pattern(subtree) else super.traverse(subtree) } }.traverse(tree) found.toList }
  • 25. Search for Rule • Putting it together: case class Rule(ouv: Tree, validation: Tree) def processRule(subtree: Tree): Rule = ??? def findRules(body: Tree): Seq[Rule] = { val validatorType = typeOf[Validator[_]] collectFromPattern(body) { case subtree if subtree.tpe <:< validatorType ⇒ processRule(subtree) } }
  • 26. Walkthrough Search for rule Process rule Generate description Rewrite rule
  • 27. Process Rule • The user writes: p.firstName is notEmpty • The compiler emits: Type: Validator[_] Contextualizer(p.firstName).is(notEmpty) Object Under Validation (OUV) Validation
  • 28. Process Rule Contextualizer(p.firstName).is(notEmpty) • This is effectively an Apply AST node • The left-hand side is the OUV • The right-hand side is the validation – But we can use the entire expression! • Contextualizer is our entry point
  • 29. Process Rule Contextualizer(p.firstName).is(notEmpty) Apply Select Apply TypeApply Contextualizer String Select Ident(“p”) firstName is notEmpty
  • 30. Process Rule Contextualizer(p.firstName).is(notEmpty) Apply Select Apply TypeApply Contextualizer String Select Ident(“p”) firstName is notEmpty
  • 31. Process Rule Apply TypeApply Contextualizer String Select Ident(“p”) firstName
  • 32. Process Rule Apply TypeApply Contextualizer Φ Select Ident(“p”) firstName
  • 33. Process Rule Apply TypeApply Contextualizer Φ Select Ident(“p”) firstName
  • 34. Process Rule Apply TypeApply Contextualizer Φ OUV Φ Φ case Apply(TypeApply(Select(_, `term`), _), ouv :: Nil) ⇒
  • 35. Process Rule • Putting it together: val term = newTermName("Contextualizer") def processRule(subtree: Tree): Rule = extractFromPattern(subtree) { case Apply(TypeApply(Select(_, `term`), _), ouv :: Nil) ⇒ Rule(ouv, subtree) } getOrElse abort(subtree.pos, "Not a valid rule")
  • 36. Walkthrough Search for rule Process rule Generate description Rewrite rule
  • 37. Generate Description Contextualizer(p.firstName).is(notEmpty) • Consider the object under validation • In this example, it is a field accessor • The function prototype is the entry point Select Ident(“p”) firstName validator[Person] { p ⇒ ... }
  • 38. Generate Description • How to get at the prototype? • The macro signature includes the rule block: def apply[T : c.WeakTypeTag] (c: Context) (v: c.Expr[T ⇒ Unit]): c.Expr[Validator[T]] • To extract the prototype: val Function(prototype :: Nil, body) = v.tree // prototype: ValDef
  • 39. Generate Description • Putting it all together: def describeRule(rule: ValidationRule) = { val para = prototype.name val Select(Ident(`para`), description) = rule.ouv description.toString }
  • 40. Walkthrough Search for rule Process rule Generate description Rewrite rule
  • 41. Rewrite Rule • We’re constructing a Validator[Person] • A rule is itself a Validator[T]. For example: Contextualizer(p.firstName).is(notEmpty) • We need to: – Lift the rule to validate the enclosing type – Apply the description to the result
  • 42. Quasiquotes • Provide an easy way to construct ASTs: Apply( Select( Ident(newTermName"x"), newTermName("$plus") ), List( Ident(newTermName("y")) ) ) q"x + y"
  • 43. Quasiquotes • Quasiquotes also let you splice trees: def greeting(whom: c.Expr[String]) = q"Hello "$whom"!" • And can be used in pattern matching: val q"$x + $y" = tree
  • 44. Rewrite Rule Contextualizer(p.firstName).is(notEmpty) new Validator[Person] { def apply(p: Person) = { val validation = Contextualizer(p.firstName).is(notEmpty) validation(p.firstName) withDescription "firstName" } }
  • 45. Rewrite Rule • Putting it all together: def rewriteRule(rule: ValidationRule) = { val desc = describeRule(rule) val tree = Literal(Constant(desc)) q""" new com.wix.accord.Validator[${weakTypeOf[T]}] { def apply($prototype) = { val validation = ${rule.validation} validation(${rule.ouv}) withDescription $tree } } """ }
  • 47. Epilogue • The finishing touch: and combinator def apply[T : c.WeakTypeTag] (c: Context) (v: c.Expr[T ⇒ Unit]): c.Expr[Validator[T]] = { val Function(prototype :: Nil, body) = v.tree // ... all the stuff we just discussed val rules = findRules(body) map rewriteRule val result = q"new com.wix.accord.combinators.And(..$rules)" c.Expr[Validator[T]](result) }
  • 48. tomer@tomergabel.com @tomerg http://il.linkedin.com/in/tomergabel Check out Accord at: http://github.com/wix/accord Thank you for listening WE’RE DONE HERE!

Editor's Notes

  1. Image source: http://en.wikipedia.org/wiki/File:Martin_Luther_King_-_March_on_Washington.jpg
  2. Image source: https://www.flickr.com/photos/leo-gruebler/6347903993
  3. Image source: https://www.flickr.com/photos/wwarby/11271811524/in/photostream/
  4. Number image source: http://pixabay.com/en/count-numbers-digits-display-147393/
  5. Number image source: http://pixabay.com/en/count-numbers-digits-display-147393/
  6. Number image source: http://pixabay.com/en/count-numbers-digits-display-147393/
  7. Number image source: http://pixabay.com/en/count-numbers-digits-display-147393/
  8. Number image source: http://pixabay.com/en/count-numbers-digits-display-147393/
  9. Image source: https://www.flickr.com/photos/bevgoodwin/8608320577