Upload
max-bureck
View
146
Download
1
Embed Size (px)
Citation preview
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 /
Frau
nhof
er F
OK
US
© M
atth
ias
Hey
de /
Frau
nhof
er 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 /
Frau
nhof
er F
OK
US
© M
atth
ias
Hey
de /
Frau
nhof
er 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 /
Frau
nhof
er F
OK
US
© M
atth
ias
Hey
de /
Frau
nhof
er 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 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
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
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
Not the original I am
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
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 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
🔨
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
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
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
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
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
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; Example Before
Example calling constructor of type from JDK:
val queue = new LinkedBlockingDequeval corePoolSize = 1val maximumPoolSize = 5val keepAliveTime = 200val keepAliveTimeUnit = TimeUnit.MILLISECONDS
val pool = new ThreadPoolExecutor(maximumPoolSize, corePoolSize, keepAliveTime, keepAliveTimeUnit, queue)
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
Whoops, wrong parameter order
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
???
33
API Xtendification, Builder Extension Method; Example Usage
Example using extension method
val pool = ThreadPoolExecutor.create [corePoolSize = 1maximumPoolSize = 5keepAliveTime = 200keepAliveTimeUnit = TimeUnit.MILLISECONDS
workQueue = new LinkedBlockingDeque]
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS
🔨
35
Further Xtend Patterns
10 Java Idioms Stomped with Xtendhttps://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
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
41© Fraunhofer FOKUS
Fraunhofer FOKUSKaiserin-Augusta-Allee 3110589 Berlin, Germanywww.fokus.fraunhofer.de
Max BureckSenior [email protected] +49 (0)30 3463-7321
CONTACT