Leveraging Scala Macros for Better Validation

Tomer Gabel
Tomer GabelConsulting Engineer at Substrate Software Services
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!
1 of 48

Recommended

The Ring programming language version 1.5.4 book - Part 34 of 185 by
The Ring programming language version 1.5.4 book - Part 34 of 185The Ring programming language version 1.5.4 book - Part 34 of 185
The Ring programming language version 1.5.4 book - Part 34 of 185Mahmoud Samir Fayed
10 views10 slides
What are arrays in java script by
What are arrays in java scriptWhat are arrays in java script
What are arrays in java scriptMiguel Silva Loureiro
89 views9 slides
Pandas Cheat Sheet by
Pandas Cheat SheetPandas Cheat Sheet
Pandas Cheat SheetACASH1011
13.4K views2 slides
JavaScript Arrays by
JavaScript Arrays JavaScript Arrays
JavaScript Arrays Reem Alattas
2.1K views61 slides
The Ring programming language version 1.2 book - Part 24 of 84 by
The Ring programming language version 1.2 book - Part 24 of 84The Ring programming language version 1.2 book - Part 24 of 84
The Ring programming language version 1.2 book - Part 24 of 84Mahmoud Samir Fayed
29 views10 slides
CREATE INDEX … USING VODKA. VODKA CONNECTING INDEXES, Олег Бартунов, Александ... by
CREATE INDEX … USING VODKA. VODKA CONNECTING INDEXES, Олег Бартунов, Александ...CREATE INDEX … USING VODKA. VODKA CONNECTING INDEXES, Олег Бартунов, Александ...
CREATE INDEX … USING VODKA. VODKA CONNECTING INDEXES, Олег Бартунов, Александ...Ontico
3.6K views86 slides

More Related Content

What's hot

Gorm by
GormGorm
GormNexThoughts Technologies
770 views30 slides
The Ring programming language version 1.8 book - Part 41 of 202 by
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 202Mahmoud Samir Fayed
7 views10 slides
Postgres rules by
Postgres rulesPostgres rules
Postgres rulesgisborne
2.8K views40 slides
Java Cheat Sheet by
Java Cheat SheetJava Cheat Sheet
Java Cheat SheetSaeid Zebardast
7K views1 slide
The Ring programming language version 1.5.1 book - Part 34 of 180 by
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 180Mahmoud Samir Fayed
11 views10 slides

What's hot(17)

The Ring programming language version 1.8 book - Part 41 of 202 by Mahmoud Samir Fayed
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 by gisborne
Postgres rulesPostgres rules
Postgres rules
gisborne2.8K views
The Ring programming language version 1.5.1 book - Part 34 of 180 by Mahmoud Samir Fayed
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
The Ring programming language version 1.5.1 book - Part 33 of 180 by Mahmoud Samir Fayed
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. by Ted Vinke
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 Vinke10.5K views
Clojure vs Design Patterns by Ron Toland
Clojure vs Design PatternsClojure vs Design Patterns
Clojure vs Design Patterns
Ron Toland622 views
Json and SQL DB serialization Introduction with Play! and Slick by Stephen Kemmerling
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
Stephen Kemmerling9.2K views
MCE^3 - Hannes Verlinde - Let The Symbols Do The Work by PROIDEA
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
PROIDEA273 views

Similar to Leveraging Scala Macros for Better Validation

Java gets a closure by
Java gets a closureJava gets a closure
Java gets a closureTomasz Kowalczewski
1.6K views56 slides
Scala by
ScalaScala
Scalasuraj_atreya
778 views21 slides
PhpUnit - The most unknown Parts by
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsBastian Feder
4.3K views27 slides
Ti1220 Lecture 7: Polymorphism by
Ti1220 Lecture 7: PolymorphismTi1220 Lecture 7: Polymorphism
Ti1220 Lecture 7: PolymorphismEelco Visser
1.2K views67 slides
Scala in Places API by
Scala in Places APIScala in Places API
Scala in Places APIŁukasz Bałamut
629 views22 slides
Test in action week 2 by
Test in action   week 2Test in action   week 2
Test in action week 2Yi-Huan Chan
1.4K views31 slides

Similar to Leveraging Scala Macros for Better Validation(20)

PhpUnit - The most unknown Parts by Bastian Feder
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown Parts
Bastian Feder4.3K views
Ti1220 Lecture 7: Polymorphism by Eelco Visser
Ti1220 Lecture 7: PolymorphismTi1220 Lecture 7: Polymorphism
Ti1220 Lecture 7: Polymorphism
Eelco Visser1.2K views
Test in action week 2 by Yi-Huan Chan
Test in action   week 2Test in action   week 2
Test in action week 2
Yi-Huan Chan1.4K views
Php unit the-mostunknownparts by Bastian Feder
Php unit the-mostunknownpartsPhp unit the-mostunknownparts
Php unit the-mostunknownparts
Bastian Feder2.6K views
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf by Hiroshi Ono
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono370 views
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf by Hiroshi Ono
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono406 views
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf by Hiroshi Ono
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono524 views
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf by Hiroshi Ono
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
Hiroshi Ono760 views
Java 8 presentation by Van Huong
Java 8 presentationJava 8 presentation
Java 8 presentation
Van Huong6.1K views
API first with Swagger and Scala by Slava Schmidt by JavaDayUA
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
JavaDayUA2.3K views
Icsug dev day2014_road to damascus - conversion experience-lotusscript and @f... by ICS User Group
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 Group539 views
Java 8 Workshop by Mario Fusco
Java 8 WorkshopJava 8 Workshop
Java 8 Workshop
Mario Fusco7.4K views
An excuse to Try, Either, folding, and Future. sequence by Germán Ferrari
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 Ferrari871 views

More from Tomer Gabel

How shit works: Time by
How shit works: TimeHow shit works: Time
How shit works: TimeTomer Gabel
342 views53 slides
Nondeterministic Software for the Rest of Us by
Nondeterministic Software for the Rest of UsNondeterministic Software for the Rest of Us
Nondeterministic Software for the Rest of UsTomer Gabel
329 views39 slides
Slaying Sacred Cows: Deconstructing Dependency Injection by
Slaying Sacred Cows: Deconstructing Dependency InjectionSlaying Sacred Cows: Deconstructing Dependency Injection
Slaying Sacred Cows: Deconstructing Dependency InjectionTomer Gabel
1.3K views34 slides
An Abridged Guide to Event Sourcing by
An Abridged Guide to Event SourcingAn Abridged Guide to Event Sourcing
An Abridged Guide to Event SourcingTomer Gabel
1K views32 slides
How shit works: the CPU by
How shit works: the CPUHow shit works: the CPU
How shit works: the CPUTomer Gabel
1.8K views38 slides
How Shit Works: Storage by
How Shit Works: StorageHow Shit Works: Storage
How Shit Works: StorageTomer Gabel
914 views44 slides

More from Tomer Gabel(20)

How shit works: Time by Tomer Gabel
How shit works: TimeHow shit works: Time
How shit works: Time
Tomer Gabel342 views
Nondeterministic Software for the Rest of Us by Tomer Gabel
Nondeterministic Software for the Rest of UsNondeterministic Software for the Rest of Us
Nondeterministic Software for the Rest of Us
Tomer Gabel329 views
Slaying Sacred Cows: Deconstructing Dependency Injection by Tomer Gabel
Slaying Sacred Cows: Deconstructing Dependency InjectionSlaying Sacred Cows: Deconstructing Dependency Injection
Slaying Sacred Cows: Deconstructing Dependency Injection
Tomer Gabel1.3K views
An Abridged Guide to Event Sourcing by Tomer Gabel
An Abridged Guide to Event SourcingAn Abridged Guide to Event Sourcing
An Abridged Guide to Event Sourcing
Tomer Gabel1K views
How shit works: the CPU by Tomer Gabel
How shit works: the CPUHow shit works: the CPU
How shit works: the CPU
Tomer Gabel1.8K views
How Shit Works: Storage by Tomer Gabel
How Shit Works: StorageHow Shit Works: Storage
How Shit Works: Storage
Tomer Gabel914 views
Java 8 and Beyond, a Scala Story by Tomer Gabel
Java 8 and Beyond, a Scala StoryJava 8 and Beyond, a Scala Story
Java 8 and Beyond, a Scala Story
Tomer Gabel747 views
The Wix Microservice Stack by Tomer Gabel
The Wix Microservice StackThe Wix Microservice Stack
The Wix Microservice Stack
Tomer Gabel1.7K views
Scala Refactoring for Fun and Profit (Japanese subtitles) by 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)
Tomer Gabel6.6K views
Scala Refactoring for Fun and Profit by Tomer Gabel
Scala Refactoring for Fun and ProfitScala Refactoring for Fun and Profit
Scala Refactoring for Fun and Profit
Tomer Gabel985 views
Onboarding at Scale by Tomer Gabel
Onboarding at ScaleOnboarding at Scale
Onboarding at Scale
Tomer Gabel1.5K views
Scala in the Wild by Tomer Gabel
Scala in the WildScala in the Wild
Scala in the Wild
Tomer Gabel2.8K views
Speaking Scala: Refactoring for Fun and Profit (Workshop) by 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)
Tomer Gabel765 views
Put Your Thinking CAP On by Tomer Gabel
Put Your Thinking CAP OnPut Your Thinking CAP On
Put Your Thinking CAP On
Tomer Gabel3.5K views
A Field Guide to DSL Design in Scala by Tomer Gabel
A Field Guide to DSL Design in ScalaA Field Guide to DSL Design in Scala
A Field Guide to DSL Design in Scala
Tomer Gabel6.5K views
Functional Leap of Faith (Keynote at JDay Lviv 2014) by 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)
Tomer Gabel1.5K views
Scala Back to Basics: Type Classes by Tomer Gabel
Scala Back to Basics: Type ClassesScala Back to Basics: Type Classes
Scala Back to Basics: Type Classes
Tomer Gabel3.7K views
5 Bullets to Scala Adoption by Tomer Gabel
5 Bullets to Scala Adoption5 Bullets to Scala Adoption
5 Bullets to Scala Adoption
Tomer Gabel2.7K views
Nashorn: JavaScript that doesn’t suck (ILJUG) by Tomer Gabel
Nashorn: JavaScript that doesn’t suck (ILJUG)Nashorn: JavaScript that doesn’t suck (ILJUG)
Nashorn: JavaScript that doesn’t suck (ILJUG)
Tomer Gabel5.9K views
Ponies and Unicorns With Scala by Tomer Gabel
Ponies and Unicorns With ScalaPonies and Unicorns With Scala
Ponies and Unicorns With Scala
Tomer Gabel961 views

Recently uploaded

Copilot Prompting Toolkit_All Resources.pdf by
Copilot Prompting Toolkit_All Resources.pdfCopilot Prompting Toolkit_All Resources.pdf
Copilot Prompting Toolkit_All Resources.pdfRiccardo Zamana
8 views4 slides
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J... by
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...Deltares
9 views24 slides
Unleash The Monkeys by
Unleash The MonkeysUnleash The Monkeys
Unleash The MonkeysJacob Duijzer
7 views28 slides
DSD-INT 2023 Exploring flash flood hazard reduction in arid regions using a h... by
DSD-INT 2023 Exploring flash flood hazard reduction in arid regions using a h...DSD-INT 2023 Exploring flash flood hazard reduction in arid regions using a h...
DSD-INT 2023 Exploring flash flood hazard reduction in arid regions using a h...Deltares
5 views31 slides
SUGCON ANZ Presentation V2.1 Final.pptx by
SUGCON ANZ Presentation V2.1 Final.pptxSUGCON ANZ Presentation V2.1 Final.pptx
SUGCON ANZ Presentation V2.1 Final.pptxJack Spektor
22 views34 slides
20231129 - Platform @ localhost 2023 - Application-driven infrastructure with... by
20231129 - Platform @ localhost 2023 - Application-driven infrastructure with...20231129 - Platform @ localhost 2023 - Application-driven infrastructure with...
20231129 - Platform @ localhost 2023 - Application-driven infrastructure with...sparkfabrik
5 views46 slides

Recently uploaded(20)

Copilot Prompting Toolkit_All Resources.pdf by Riccardo Zamana
Copilot Prompting Toolkit_All Resources.pdfCopilot Prompting Toolkit_All Resources.pdf
Copilot Prompting Toolkit_All Resources.pdf
Riccardo Zamana8 views
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J... by Deltares
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...
Deltares9 views
DSD-INT 2023 Exploring flash flood hazard reduction in arid regions using a h... by Deltares
DSD-INT 2023 Exploring flash flood hazard reduction in arid regions using a h...DSD-INT 2023 Exploring flash flood hazard reduction in arid regions using a h...
DSD-INT 2023 Exploring flash flood hazard reduction in arid regions using a h...
Deltares5 views
SUGCON ANZ Presentation V2.1 Final.pptx by Jack Spektor
SUGCON ANZ Presentation V2.1 Final.pptxSUGCON ANZ Presentation V2.1 Final.pptx
SUGCON ANZ Presentation V2.1 Final.pptx
Jack Spektor22 views
20231129 - Platform @ localhost 2023 - Application-driven infrastructure with... by sparkfabrik
20231129 - Platform @ localhost 2023 - Application-driven infrastructure with...20231129 - Platform @ localhost 2023 - Application-driven infrastructure with...
20231129 - Platform @ localhost 2023 - Application-driven infrastructure with...
sparkfabrik5 views
DSD-INT 2023 European Digital Twin Ocean and Delft3D FM - Dols by Deltares
DSD-INT 2023 European Digital Twin Ocean and Delft3D FM - DolsDSD-INT 2023 European Digital Twin Ocean and Delft3D FM - Dols
DSD-INT 2023 European Digital Twin Ocean and Delft3D FM - Dols
Deltares7 views
Myths and Facts About Hospice Care: Busting Common Misconceptions by Care Coordinations
Myths and Facts About Hospice Care: Busting Common MisconceptionsMyths and Facts About Hospice Care: Busting Common Misconceptions
Myths and Facts About Hospice Care: Busting Common Misconceptions
Fleet Management Software in India by Fleetable
Fleet Management Software in India Fleet Management Software in India
Fleet Management Software in India
Fleetable11 views
2023-November-Schneider Electric-Meetup-BCN Admin Group.pptx by animuscrm
2023-November-Schneider Electric-Meetup-BCN Admin Group.pptx2023-November-Schneider Electric-Meetup-BCN Admin Group.pptx
2023-November-Schneider Electric-Meetup-BCN Admin Group.pptx
animuscrm14 views
Headless JS UG Presentation.pptx by Jack Spektor
Headless JS UG Presentation.pptxHeadless JS UG Presentation.pptx
Headless JS UG Presentation.pptx
Jack Spektor7 views
DSD-INT 2023 Leveraging the results of a 3D hydrodynamic model to improve the... by Deltares
DSD-INT 2023 Leveraging the results of a 3D hydrodynamic model to improve the...DSD-INT 2023 Leveraging the results of a 3D hydrodynamic model to improve the...
DSD-INT 2023 Leveraging the results of a 3D hydrodynamic model to improve the...
Deltares6 views
DSD-INT 2023 Simulation of Coastal Hydrodynamics and Water Quality in Hong Ko... by Deltares
DSD-INT 2023 Simulation of Coastal Hydrodynamics and Water Quality in Hong Ko...DSD-INT 2023 Simulation of Coastal Hydrodynamics and Water Quality in Hong Ko...
DSD-INT 2023 Simulation of Coastal Hydrodynamics and Water Quality in Hong Ko...
Deltares14 views
DSD-INT 2023 Machine learning in hydraulic engineering - Exploring unseen fut... by Deltares
DSD-INT 2023 Machine learning in hydraulic engineering - Exploring unseen fut...DSD-INT 2023 Machine learning in hydraulic engineering - Exploring unseen fut...
DSD-INT 2023 Machine learning in hydraulic engineering - Exploring unseen fut...
Deltares7 views
Sprint 226 by ManageIQ
Sprint 226Sprint 226
Sprint 226
ManageIQ5 views
Software evolution understanding: Automatic extraction of software identifier... by Ra'Fat Al-Msie'deen
Software evolution understanding: Automatic extraction of software identifier...Software evolution understanding: Automatic extraction of software identifier...
Software evolution understanding: Automatic extraction of software identifier...
Airline Booking Software by SharmiMehta
Airline Booking SoftwareAirline Booking Software
Airline Booking Software
SharmiMehta6 views
Advanced API Mocking Techniques by Dimpy Adhikary
Advanced API Mocking TechniquesAdvanced API Mocking Techniques
Advanced API Mocking Techniques
Dimpy Adhikary19 views

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