Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating custom fluent API

Tags:

java

fluent

dsl

I'm going to create custom fluent API for my application. I decided to check code of already existing libraries and found some monsters in at least 2 projects that using fluent api.

I saw that there're many classes and interfaces, that differ only in number of generic types.

For example: In jOOQ library DerivedColumnList22

In RxJava: Action9

What is the purpose of monsters like this? Is it only for performance cases? Or is it some commonalities and way to go in fluent apis?

It looks scary to implement own DSL in fluent api, when you see monsters like this.

like image 362
nowszy94 Avatar asked Dec 18 '22 13:12

nowszy94


1 Answers

Since you mentioned the jOOQ library, I'll give an authoritative answer here, being the author of jOOQ, and having written this post about designing fluent APIs (or rather: designing internal domain specific languages in Java): https://blog.jooq.org/2012/01/05/the-java-fluent-api-designer-crash-course

SomeType22 is just a nominal tuple type

What you've discovered is the common desire of API designers to work with tuples. A few languages have built-in support for structural tuple types, i.e. tuple types that can be created in an ad-hoc manner, rather than declaring them in advance and giving them names. The latter is called nominal typing. More details about nominal/structural typing here.

Examples of languages with structural tuple type support:

SQL is an excellent example of a language that allows for creating ad-hoc tuple types:

SELECT first_name, last_name, age
FROM people

The above query creates a table of tuples of degree 3 with types (string, string, number). JavaScript is another example, where I can quickly generate a tuple as such:

var x = {
    firstName: "Lukas",
    lastName: "Eder",
    age: undefined
};

There are other languages that have tuple support to some level. Ideally, tuples allow access by name and index to individual attributes. Sometimes, access is given only by name. In other cases, it is only given by index. But it's always the same thing, conceptually.

What does it mean for DerivedColumnList22 and Action9

The Java language, unfortunately, has no such tools to create ad-hoc structural tuple types. (Almost) everything in Java needs to be nominally typed. Even "anonymous functions" (lambda expressions) need to be assigned to a nominal SAM type:

    Runnable r = () -> { doSomething(); }
//               ^^^^^^^^^^^^^^^^^^^^^^^^ --- Syntactically looks like a structural type
//  ^^^^^^^^^^ ------------------------------ But it's really a nominal type

So, if an API wants to give its users the illusion of supporting real structural tuple types, it has to provide names for each supported type in advance. For instance, Action9:

Action9<T1, T2, T3, T4, T5, T6, T7, T8, T9> action = 
    (a, b, c, d, e, f, g, h, i) -> { doSomething(); }
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------------- Look ma! Almost a structural tuple type!

So, this technique is mostly implemented by libraries like RxJava (which encourages functional programming where structural tuple types really shine) or jOOQ (which encourages SQL where structural tuple types also really shine).

Other libraries / APIs include:

  • Scala's Tuple1 - Tuple22
  • .NET's Tuple1 - Tuple8

This doesn't mean that you need those types in your own DSL. Start simple. Eventually, you may feel you need to add them.

like image 85
Lukas Eder Avatar answered Jan 06 '23 21:01

Lukas Eder