0% found this document useful (0 votes)
54 views41 pages

Xtend API and DSL Design Patterns EclipseConFrance2016

desarrollo de DSLs con xtends
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
54 views41 pages

Xtend API and DSL Design Patterns EclipseConFrance2016

desarrollo de DSLs con xtends
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 41

XTEND – API AND DSL DESIGN PATTERNS

Max Bureck, 08. June 2016


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

MatthiasHeyde
©Matthias
© FOKUS
FraunhoferFOKUS
Heyde//Fraunhofer
2
© Fraunhofer FOKUS
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

MatthiasHeyde
©Matthias
© FOKUS
FraunhoferFOKUS
Heyde//Fraunhofer
3
© Fraunhofer FOKUS
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

FOKUS
FraunhoferFOKUS
operator_plus(1e15bd, 1e-4bd) ⇨ 1e15bd + 1e-4bd

Heyde//Fraunhofer
MatthiasHeyde
− Active annotations

©Matthias
©
4
© Fraunhofer FOKUS
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

5
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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.

6
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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"));
});
});
});

7
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

Nested Block Syntax, Callback API Example


Method with lambda argument
default argument it
server [ Assignment to setter on default argument
port = 80
Mapping operator
get("/hello?name=$name") [
header("Content-Type" -> "text/html")
html [ Extension method
h1("Hello " + param('name'))
]
]
]
Implicit return of last expression result

8
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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

9
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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");
}
}

10
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

Signal /

11
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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"),
_ => {}
}

12
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

Fluent Case Distinction, Pattern Matching: Short Description

− 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)

13
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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

14
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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")
]

15
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

Fluent Case Distinction, Downsides

− Complex to implement, only makes sense if used multiple times

− No flexible nested decomposition and variable binding by caller

16
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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

17
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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

18
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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)

19
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

Immutable Data Structure Patterns

− Immutable objects are tricky in some cases

What??? You said immutable!


− Especially demanding are:

− Object manipulation and

− Circular references Bear with me, explanation in


3 slides

20
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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"
]

21
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

Immutable Data Structure Patterns: Lambda Builder Explained

− 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

22
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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

23
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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]

24
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

Immutable Data Structure Patterns: Object Manipulation - Problem

− Cyclic references will come back to bite you on manipulation!

− Especially when automatically generating manipulators

25
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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

26
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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"
]

27
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

Immutable Data Structure Patterns: Manipulation Specifics

− 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)

28
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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!

29
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

API Xtendification, Builder Extension Method; Example Before

Example calling constructor of type from JDK:

val queue = new LinkedBlockingDeque Whoops, wrong


val corePoolSize = 1 parameter order
val maximumPoolSize = 5
val keepAliveTime = 200
val keepAliveTimeUnit = TimeUnit.MILLISECONDS

val pool = new ThreadPoolExecutor(maximumPoolSize, corePoolSize,


keepAliveTime, keepAliveTimeUnit, queue)

30
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

API Xtendification, Builder Extension Method; Problem 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 …

31
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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

32
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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
]

33
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

API Xtendification, Builder Extension Method Explained

− 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

34
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

Further Xtend Patterns

10 Java Idioms Stomped with Xtend


https://www.youtube.com/watch?v=n7LUgXX_3cE

35
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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

36
© Fraunhofer FOKUS
37
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

Feedback and Opinions?

− Examples repository:
https://github.com/Boereck/eclipsecon_france_2016-xtend_patterns

− Useful?

− Interesting?

− Impractical?

− Too obvious?

− What are your favorite patterns?

38
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

Image Sources

− Max Power:
http://25.media.tumblr.com/tumblr_lxxowbwXTs1qhkm9yo1_400.gif

− Joda Pug:
https://unsplash.com/photos/2Ts5HnA67k8

39
© Fraunhofer FOKUS
XTEND – API AND DSL DESIGN PATTERNS

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

40
© Fraunhofer FOKUS
CONTACT

Fraunhofer FOKUS
Kaiserin-Augusta-Allee 31
10589 Berlin, Germany
www.fokus.fraunhofer.de

Max Bureck
Senior Researcher
[email protected]
Phone +49 (0)30 3463-7321

41
© Fraunhofer FOKUS

You might also like