Making assumptions

Most internal DSLs are outdated

Lukas Eder

Java 8 will bring a lot of new traction into last decade’s DSL debate. Lukas Eder wades in to the fray.

This post was originally published over at as part of a special series
focusing on all things Java 8, including how take advantage of
lambda expressions, extension methods, and other great
stuff. You’ll find the
source code on GitHub

That’s quite a statement from a vendor
one of the most
advanced internal DSLs currently on the market
. Let me

Languages are hard

Learning a new language (or API) is hard. You have to understand
all the keywords, the constructs, the statement and expression
types, etc. This is true both for external DSLs, internal DSLs and
“regular” APIs, which are essentially internal DSLs with less

When using JUnit, people have grown used to using hamcrest matchers. The fact that they’re
available in six languages (Java, Python, Ruby, Objective-C, PHP,
Erlang) makes them somewhat of a sound choice. As a domain-specific
language, they have established idioms that are easy to read,

assertThat(theBiscuit, equalTo(myBiscuit));
assertThat(theBiscuit, is(equalTo(myBiscuit)));
assertThat(theBiscuit, is(myBiscuit));

When you read this code, you will immediately “understand” what
is being asserted, because the API reads like prosa. But learning
to write code in this API is harder. You will have to

  • Where all of these methods are coming from
  • What sorts of methods exist
  • Who might have extended hamcrest with custom Matchers
  • What are best practices when extending the DSL

For instance, in the above example, what exactly is the
difference between the three? When should I use one and when the
other? Is is() checking for object identity?
Is equalTo() checking for object

The hamcrest tutorial goes on with examples like these:

public void testSquareRootOfMinusOneIsNotANumber() {
    assertThat(Math.sqrt(-1), is(notANumber()));

You can see that notANumber() apparently
is a custom matcher, implemented some place in a utility:

public class IsNotANumber
extends TypeSafeMatcher<Double> {
  public boolean matchesSafely(Double number) {
    return number.isNaN();
  public void describeTo(Description description) {
    description.appendText("not a number");
  public static <T> Matcher<Double> notANumber() {
    return new IsNotANumber();

While this sort of DSL is very easy to create, and probably also
a bit fun, it is dangerous to start delving into writing and
enhancing custom DSLs for a simple reason. They’re in no way better
than their general-purpose, functional counterparts – but they’re
harder to maintain. Consider the above examples in Java 8:

Replacing DSLs with Functions

Let’s assume we have a very simple testing API:

static <T> void assertThat(
    T actual, 
    Predicate<T> expected
) {
    assertThat(actual, expected, "Test failed");
static <T> void assertThat(
    T actual, 
    Predicate<T> expected, 
    String message
) {
    assertThat(() -> actual, expected, message);
static <T> void assertThat(
    Supplier<T> actual, 
    Predicate<T> expected
) {
    assertThat(actual, expected, "Test failed");
static <T> void assertThat(
    Supplier<T> actual, 
    Predicate<T> expected, 
    String message
) {
    if (!expected.test(actual.get()))
        throw new AssertionError(message);

Now, compare the hamcrest matcher expressions with their
functional equivalents:

// ---------------------------------------------
assertThat(theBiscuit, equalTo(myBiscuit));
assertThat(theBiscuit, is(equalTo(myBiscuit)));
assertThat(theBiscuit, is(myBiscuit));
assertThat(Math.sqrt(-1), is(notANumber()));
// ---------------------------------------------
assertThat(theBiscuit, b -> b == myBiscuit);
assertThat(Math.sqrt(-1), n -> Double.isNaN(n));

With lambda expressions, and a
well-designed assertThat() API, I’m pretty
sure that you won’t be looking for the right way to express your
assertions with matchers any longer.

Note that unfortunately, we cannot use
the Double::isNaN method reference, as that
would not be compatible with Predicate<Double>.
For that, we’d have to do some primitive type magic in the
assertion API, e.g.

static void assertThat(
    double actual, 
    DoublePredicate expected
) { ... }

Which can then be used as such:

assertThat(Math.sqrt(-1), Double::isNaN);

Yeah, but…

… you may hear yourself saying, “but we can combine matchers
with lambdas and streams”. Yes, of course we can. I’ve just done so
now in the jOOQ integration
. I want to skip the integration tests for all SQL
dialects that are not in a list of dialects
supplied as a system property:

String dialectString = 
// The string must not be "empty"
assumeThat(dialectString, not(isOneOf("", null)));
// And we check if the current dialect() is
// contained in a comma or semi-colon separated
// list of allowed dialects, trimmed and lowercased
    // Another matcher here

… and that’s pretty neat, too, right?

But why don’t I just simply write:

// Using Apache Commons, here
assumeThat(dialectString, StringUtils::isNotEmpty);
    d -> stream(dialectString.split("[,;]"))

No Hamcrest needed, just plain old lambdas and streams!

Now, readability is a matter of taste, of course. But the above
example clearly shows that there is no longer
any need for Hamcrest matchers and for the
Hamcrest DSL. Given that within the next 2-3 years, the majority of
all Java developers will be very used to using
the Streams API in every day work, but
not very used to using the Hamcrest API, I urge
you, JUnit maintainers, to deprecate the use of Hamcrest in favour
of Java 8 APIs.

Is Hamcrest now considered bad?

Well, it has served its purpose in the past, and people have
grown somewhat used to it. But as we’ve already pointed out in
post about Java 8 and JUnit Exception matching
, yes, we do
believe that we Java folks have been barking up the wrong tree in
the last 10 years.

The lack of lambda expressions has lead to a variety of
completely bloated and now also slightly
useless libraries
. Many internal DSLs or annotation-magicians
are also affected. Not because they’re no longer solving the
problems they used to, but because they’re not Java-8-ready.
Hamcrest’s Matcher type
is not a functional interface, although it would
be quite easy to transform it into one. In fact,
Hamcrest’s CustomMatcher logic
should be pulled up to the Matcher interface, into default

Things dont’ get better with alternatives, like AssertJ, which
create an alternative DSL that is now rendered obsolete (in terms
of call-site code verbosity) through lambdas and the Streams

If you insist on using a DSL for testing, then
probably Spock would
be a far better choice anyway.

Other examples

Hamcrest is just one example of such a DSL. This article has
shown how it can be almost completely removed from your stack by
using standard JDK 8 constructs and a couple of utility methods,
which you might have in JUnit some time soon, anyway.

Java 8 will bring a lot of new traction into last decade’s DSL
debate, as also the Streams API will greatly improve the way we
look at transforming or building data. But many current DSLs are
not ready for Java 8, and have not been designed in a functional
way. They have too many keywords for things and concepts that are
hard to learn, and that would be better modelled using

An exception to this rule are DSLs like jOOQ or jRTF, which are modelling
actual pre-existing external DSLs in a 1:1
fashion, inheriting all the existing keywords and syntax elements,
which makes them much easier to learn in the first place.

What’s your take?

What is your take on the above assumptions? What
is your favourite internal DSL, that might
vanish or that might be completely transformed in the next five
years because it has been obsoleted by Java 8?

Lukas Eder
Lukas is a Java and SQL aficionado. He’s the founder and head of R&D at Data Geekery GmbH, the company behind jOOQ, the best way to write SQL in Java.
comments powered by Disqus