More Related Content Similar to Xtend api and_dsl_design_patterns_eclipse_confrance2016 (20) Xtend api and_dsl_design_patterns_eclipse_confrance20162. 2
XTEND – API AND DSL DESIGN PATTERNS
Intro – Xtend
Xtend is a general purpose programming language transpiling to Java source
Its syntax is flexible allowing definition of internal DSLs and interesting APIs
This presentation will show some ways how the syntax can be utilized
No detailed explanation of Xtend‘s features though
© Fraunhofer FOKUS
©MatthiasHeyde/FraunhoferFOKUS©MatthiasHeyde/FraunhoferFOKUS
3. 3
XTEND – API AND DSL DESIGN PATTERNS
Intro – Patterns
Based on some observations from designing Xtend APIs
Some ideas inspired by other languages (e.g. Scala, F#)
Some patterns may or should be implemented via active annotations in future
© Fraunhofer FOKUS
©MatthiasHeyde/FraunhoferFOKUS©MatthiasHeyde/FraunhoferFOKUS
4. 4
XTEND – API AND DSL DESIGN PATTERNS
Intro – The Tools Provided By Xtend
Lambdas
Call with lambda as last parameter: place after brackets; omit empty brackets
strProv.apply([String s | println(s)]) ⇨ strProv.apply [println(it)]
Setter call can be written as assignment
button.setText("Press Me") ⇨ button.text = "Press Me"
Extension methods
emphasize("boo") ⇨ "boo".emphasize
Operator overloading
operator_plus(1e15bd, 1e-4bd) ⇨ 1e15bd + 1e-4bd
Active annotations
© Fraunhofer FOKUS
©MatthiasHeyde/FraunhoferFOKUS©MatthiasHeyde/FraunhoferFOKUS
5. 5
XTEND – API AND DSL DESIGN PATTERNS
Pattern Overview
Nested Block Syntax
Fluent Case Distinction
Immutable Data Structure Patterns
Implicit Parameter Values
Type Providers
API Xtendification
© Fraunhofer FOKUS
6. 6
Nested Block Syntax, Use Case
Lambda as last argument looks like a named block
Can be exploited to create internal DSLs that look like nested blocks
Declarative look, while being imperative
Especially useful when building up object trees, e.g.
UI elements
Configuration
Etc.
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
7. 7
Nested Block Syntax, Callback API Example in Java 8
server( (conf) -> {
conf.setPort(80);
conf.get("/hello?name=$name", (response) -> {
response.header(Pair.of("content", "text/html"));
return HtmlBuilder.html(response, (builder) -> {
builder.h1("Hello " + builder.param("name"));
});
});
});
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
8. 8
Nested Block Syntax, Callback API Example
server [
port = 80
get("/hello?name=$name") [
header("Content-Type" -> "text/html")
html [
h1("Hello " + param('name'))
]
]
]
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
Assignment to setter on default argument
default argument it
Implicit return of last expression result
Extension method
Method with lambda argument
Mapping operator
9. 9
Nested Block Syntax, Summary
Nested block APIs reflect logical containment structures in code
Xtend reduces visual noise and enables declarative look
Can improve maintainability due to clear intent and readability of code
"Traditional" APIs may be used as nested blocks, using => operator
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
10. 10
Fluent Case Distinction, Example: Object Decomposition in Java 8
ParentalStatus parentalStatus = bob.getParentalStatus();
if(parentalStatus instanceof Parents) {
Parents parents = (Parents) parentalStatus;
Optional<Person> momOpt = parents.getMom();
Optional<Person> dadOpt = parents.getDad();
momOpt.ifPresent((mom) -> dadOpt.ifPresent((dad) -> {
System.out.println("Mother: "+mom.getName()+", Father: "+ dad.getName());
}));
} else {
if(parentalStatus instanceof Orphan) {
String orphanage = ((Orphan) parentalStatus).getOrphanage();
System.out.println("Orphanage: "+orphanage);
} else {
System.out.println("Unknown parental status");
}
}
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
12. 12
Fluent Case Distinction, Example: Pattern Matching in Rust
match bob.parental_status {
Parents { mom: Some(ref mother), dad: Some(ref father) }
=> println!("Mother: {:?}, Father: {:?}", father.name, mother.name),
Orphan { orphanage: ref institute }
=> println!("Orphanage: {:?}", institute),
Unknown
=> println!("Parental status unknown"),
_ => {}
}
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
13. 15
Fluent Case Distinction, Example
val daredevil = Person::orphan("Matt Murdock", "St Agnes Orphanage")
daredevil.parentalStatus
.caseParents [ mom, dad |
println('''Mother: «mom.name», Father: «dad.name»''')
].caseOrphan [ orphanage |
println("Orphanage: " + orphanage)
].caseUnknown [
println("Unknown parental status")
]
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
👿
14. 16
Fluent Case Distinction, Downsides
Complex to implement, only makes sense if used multiple times
No flexible nested decomposition and variable binding by caller
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
15. 17
Fluent Case Distinction, Summary / Use Cases
Most times the powerful switch statement or multiple dispatch is good enough
Still, this pattern can be useful for several use cases:
Short notation for reoccurring, non trivial object decomposition
Null-safe data access
Can enforce exhaustive case handling or at least default case
Alternative to inheritance hierarchies: No looking for all possible subclasses
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
16. 19
Immutable Data Structure Patterns – Intro
Immutable objects are easier to reason about
No unexpected changes when passed to methods
Can safely be shared between threads
Interestingly better for Java GC (according to Brian Goetz)
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
17. 20
Immutable Data Structure Patterns
Immutable objects are tricky in some cases
Especially demanding are:
Object manipulation and
Circular references
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
What??? You said immutable!
Bear with me, explanation in
3 slides
18. 21
Immutable Data Structure Patterns: Object Instantiation
Initialization using mutable builder objects
Especially nice: Lambda builder pattern
Example:
val p = ImmutablePerson.create [
firstName = "Mack"
lastName = "The Knife"
]
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
🔨
19. 23
Immutable Data Structure Patterns: Object Manipulation
So called “persistent data structures“
For simple structures: Fields may have ”update” method
Takes lambda parameter mapping old field value to new value
Returns new immutable updated object
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
20. 24
Immutable Object Patterns: Object Manipulation Example
class ImmutablePerson {
// ...
def ImmutablePerson firstName((String)=>String mapper) {
val newFirstName = mapper.apply(firstName)
new ImmutablePerson(newFirstName, lastName)
}
}
var p = ImmutablePerson.create […]
p = p.firstName["max"]
p = p.firstName[toFirstUpper]
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
21. 25
Immutable Data Structure Patterns: Object Manipulation - Problem
Cyclic references will come back to bite you on manipulation!
Especially when automatically generating manipulators
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
22. 26
Immutable Data Structure Patterns: Object Manipulation - Alternative
Manipulation by builder
Create pre-filled builder from existing object
Add some sugar for ease of use, similar to lambda builder
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
23. 27
Immutable Data Structure Patterns: Manipulation By Builder Example
var homer = ImmutablePerson.create [
firstName = "Homer"
lastName = "Simpson"
town = "Springfield"
]
homer = homer.with [
firstName = "Max"
lastName = "Power"
]
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
24. 29
API Xtendification – Intro
Java APIs are not written with Xtend in mind
Some language features of Xtend only shine when API is shaped in a certain way
Extension methods to the rescue!
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
25. 30
API Xtendification, Builder Extension Method; Example Before
Example calling constructor of type from JDK:
val queue = new LinkedBlockingDeque
val corePoolSize = 1
val maximumPoolSize = 5
val keepAliveTime = 200
val keepAliveTimeUnit = TimeUnit.MILLISECONDS
val pool = new ThreadPoolExecutor(maximumPoolSize, corePoolSize,
keepAliveTime, keepAliveTimeUnit, queue)
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
Whoops, wrong
parameter order
26. 32
API Xtendification, Builder Extension Method; Example Definition
Let’s define an extension method:
static def ThreadPoolExecutor create(Class<ThreadPoolExecutor> clazz,
(ThreadPoolExecutorBuilder)=>void config) {
val builder = new ThreadPoolExecutorBuilder
config.apply(builder)
builder.build
}
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
27. 33
API Xtendification, Builder Extension Method; Example Usage
Example using extension method
val pool = ThreadPoolExecutor.create [
corePoolSize = 1
maximumPoolSize = 5
keepAliveTime = 200
keepAliveTimeUnit = TimeUnit.MILLISECONDS
workQueue = new LinkedBlockingDeque
]
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
🔨
28. 35
Further Xtend Patterns
10 Java Idioms Stomped with Xtend
https://www.youtube.com/watch?v=n7LUgXX_3cE
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
29. 36
Summary
Xtend language is pretty flexible, due to its syntax features
Declarative looking internal DSLs are possible
Enables new types of API patterns
Patterns can be used to make Java APIs friendlier to use in Xtend
Some patterns can be automated with Active Annotations
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
31. 38
Feedback and Opinions?
Examples repository:
https://github.com/Boereck/eclipsecon_france_2016-xtend_patterns
Useful?
Interesting?
Impractical?
Too obvious?
What are your favorite patterns?
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
32. 39
Image Sources
Max Power:
http://25.media.tumblr.com/tumblr_lxxowbwXTs1qhkm9yo1_400.gif
Joda Pug:
https://unsplash.com/photos/2Ts5HnA67k8
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
33. 41
© Fraunhofer FOKUS
Fraunhofer FOKUS
Kaiserin-Augusta-Allee 31
10589 Berlin, Germany
www.fokus.fraunhofer.de
Max Bureck
Senior Researcher
max.bureck@fokus.fraunhofer.de
Phone +49 (0)30 3463-7321
CONTACT
Editor's Notes If not xperienced: hopefully learn on the go
Example ideas inspired by pop culture Library to xtend some Java 8 types Lambda: if only one argument: implicit argument it Leaving out how imparative APIs can be used in block style Silly example (stringly typed) designed to show off Xtend features
See active annotation version talk referenced later
„It“ can be used same as „this“ (as in leaving out)
Exact same API !! Xtend: Switch only on types;
Generic library solution not possible
Datatype specific APIs Checks which fields are null
Just hiding boilerplate
Direct apporoach can be optimized to never allocate:
Initially returning immutable object for state „not found“
Always returning same obj when match not found yet
Returning a simple NoOp singleton after match found
IMG src: https://unsplash.com/photos/2Ts5HnA67k8
http://creativecommons.org/publicdomain/zero/1.0/ Exhaustiveness could also be checked at runtime, if no default case enforced
Short notation could even be implemented via extension methods
Direct version: exhaustion works best when cases return value Working with them can come with memory and/or runtime overhead
Use where appropriate Not a person who won‘t stop talking
Looks like constuctor call with named params
Syntactic sugar for builder use
Drawbacks:
Assignment only if setter does not returns void. Incompatible with fluent builder pattern.
Compiler cannot ensure all mandatory fields are set
What is the problem with cyclic references? Alternative „with“ method, simply pass in value: Have a look at Java 8 DateTime classes.
Why lambda? Next slide!
Property access is variation of “over” combinator of “lense” pattern in Haskell.
See creating new ImmutablePerson, how cyclic reference?
We need info how to update field holding cyclic ref
Apart from cycles: shallow copies can be used if all objects are immutable. Only flat copy, if only immutable types (again: cyclic references are a problem)
Imparative code style possible in block
IMG src: http://25.media.tumblr.com/tumblr_lxxowbwXTs1qhkm9yo1_400.gif Think about a containment hierarchy
Root object: appearently Domain Driven Design practice
Nesting alternative: monatic data types Context first, lambda last Builder is the answer Basically named parameters
Looks like static method call: Lambda builder pattern
Can have default values for each param