Upload
vukhuong
View
222
Download
1
Embed Size (px)
Citation preview
Max Bureck, 08. June 2016
XTEND – API AND DSL DESIGN PATTERNS
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
©M
atth
ias
Hey
de /
Fra
unho
fer
FO
KU
S©
Mat
thia
s H
eyde
/ F
raun
hofe
r F
OK
US
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
©M
atth
ias
Hey
de /
Fra
unho
fer
FO
KU
S©
Mat
thia
s H
eyde
/ F
raun
hofe
r F
OK
US
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
©M
atth
ias
Hey
de /
Fra
unho
fer
FO
KU
S©
Mat
thia
s H
eyde
/ F
raun
hofe
r F
OK
US
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
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
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
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
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
Fluent Case Distinction, Example: Object Decomposit ion 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
11
Signal /
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
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
Fluent Case Distinction, Pattern Matching: Short De scription
− Comparable to switch statement in C like languages
− Matches a structural pattern of an object and it‘s fields
− Expression of first matching pattern will be executed
− Allows binding of field values to variable names (e.g. ref mother in example)
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
14
Fluent Case Distinction – Intro
− Generic pattern matching with type-matching, decomposition and variable binding?
− Xtend switch expression “only” has instance check, no decomposition
− A library solution would be best
− But readable solution seems to be impossible without language support
− Next best thing are data type specific solutions
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
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
�
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
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
18
Fluent Case Distinction, Summary
− Fluent Case APIs can encapsulate reusable object decompositions
− They are an alternative to language-level pattern matching
− Come with implementation overhead
− Depending on usage (capturing in lambdas), may have runtime and memory overhead
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
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
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 in3 slides
21
Immutable Data Structure Patterns: Object Instantia tion
− 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
�λ
22
Immutable Data Structure Patterns: Lambda Builder E xplained
− Static create method taking lambda
− Create method instantiates builder and calls lambda with it
− Lambda calls setters on builder
− Create method uses configured builder to create object and returns it
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
23
Immutable Data Structure Patterns: Object Manipulat ion
− 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
24
Immutable Object Patterns: Object Manipulation Exam ple
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
25
Immutable Data Structure Patterns: Object Manipulat ion - Problem
− Cyclic references will come back to bite you on man ipulation!
− Especially when automatically generating manipulators
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
26
Immutable Data Structure Patterns: Object Manipulat ion - 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
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
28
Immutable Data Structure Patterns: Manipulation Spe cifics
− Always needs manipulation from "root" object to ensure consistency
− Pushing side effects to boundaries
− Advantage of “update” methods: Manipulation may be nested
− Cyclic references must be set lazy (same as in constructor)
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
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
30
API Xtendification, Builder Extension Method; Examp le 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
31
API Xtendification, Builder Extension Method; Probl em Statement
− When not using variables, long parameter lists are hard to understand
− When using variables
− It is possible to mess up parameter order
− Wrong variables might be used
− Code will compile, but might be wrong
− Solution: Use a builder class …
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
32
API Xtendification, Builder Extension Method; Examp le 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
33
API Xtendification, Builder Extension Method; Examp le 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
�λ
34
API Xtendification, Builder Extension Method Explai ned
− Example shows alternative to overloaded constructors with different parameter lists
− It is supposed to look like a create method is called in class ThreadPoolExecutor
− Actual static method is located in different class
− Extension method call with class object of class ThreadPoolExecutor as first param
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
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
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
37© Fraunhofer FOKUS
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
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
40
My Xtend Most Wanted Whish List
− Method references
− Default methods
− Compile auto-lambda-type-conversions to Java 8 method references, where possible
− Overloading call operator (also allow as extension method)
val nomnom = ["banana"]
nomnom()
− Pattern matching with decomposition
− More flexible active annotations
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
41© Fraunhofer FOKUS
Fraunhofer FOKUSKaiserin-Augusta-Allee 3110589 Berlin, Germany
www.fokus.fraunhofer.de
Max Bureck
Senior Researcher
Phone +49 (0)30 3463-7321
CONTACT