41
Max Bureck, 08. June 2016 XTEND – API AND DSL DESIGN PATTERNS

Xtend API and DSL Design Patterns EclipseConFrance2016

Embed Size (px)

Citation preview

Page 1: Xtend API and DSL Design Patterns EclipseConFrance2016

Max Bureck, 08. June 2016

XTEND – API AND DSL DESIGN PATTERNS

Page 2: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Mat

thia

s H

eyde

/ F

raun

hofe

r F

OK

US

Page 3: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Mat

thia

s H

eyde

/ F

raun

hofe

r F

OK

US

Page 4: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Mat

thia

s H

eyde

/ F

raun

hofe

r F

OK

US

Page 5: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 6: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 7: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 8: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 9: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 10: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 11: Xtend API and DSL Design Patterns EclipseConFrance2016

11

Signal /

© Fraunhofer FOKUS

XTEND – API AND DSL DESIGN PATTERNS

Page 12: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 13: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 14: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 15: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 16: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 17: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 18: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 19: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 20: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 21: Xtend API and DSL Design Patterns EclipseConFrance2016

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

�λ

Page 22: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 23: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 24: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 25: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 26: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 27: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 28: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 29: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 30: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 31: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 32: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 33: Xtend API and DSL Design Patterns EclipseConFrance2016

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

�λ

Page 34: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 35: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 36: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 37: Xtend API and DSL Design Patterns EclipseConFrance2016

37© Fraunhofer FOKUS

Page 38: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 39: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 40: Xtend API and DSL Design Patterns EclipseConFrance2016

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

Page 41: Xtend API and DSL Design Patterns EclipseConFrance2016

41© Fraunhofer FOKUS

Fraunhofer FOKUSKaiserin-Augusta-Allee 3110589 Berlin, Germany

www.fokus.fraunhofer.de

Max Bureck

Senior Researcher

[email protected]

Phone +49 (0)30 3463-7321

CONTACT