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.
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
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.
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.
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:
Tuple1
- Tuple22
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With