Help me with the Spiderman Operator

From time to time, I encounter a silly syntax in Java that I silently dubbed the “spiderman operator” because of all the syntactically pointing that’s going on. My problem is that it’s not very readable, I don’t know an alternative syntax for it and my programming style leads me more often to it than I am willing to ignore.

The spiderman operator looks like this:

x -> () -> x

In its raw form, it means that you have a function that takes x and returns a Supplier of x:

Function<X, Supplier<X>> rawForm = x -> () -> x;

That in itself is not very useful or mysterious, but if you take into account that the Supplier<X> is just one possible type you can return, because in Java, as long as the signature fits, the thing sits, it gets funnier.

A possible use case

Let’s define a type that is an interface with just one method:

public interface DomainValue {
    BigDecimal value();
}

In Java, the @FunctionalInterface annotation is not required to let the interface be, in fact, a functional interface. It only needs to have one method without implementation. How can we provide methods with implementation in Java interfaces. Default methods are the way:

@FunctionalInterface
public interface DomainValue {
    BigDecimal value();

    default String denotation() {
        return getClass().getSimpleName();
    }
}

Let’s say that we want to load domain values from a key-value-store with the following access method:

Optional<Double> loadEntry(String key)

If there is no entry with the given key or the syntax is not suitable to be interpreted as a double, the method returns Optional.emtpy(). Else it returns the double value wrapped in an Optional shell. We can convert it to our domain value like this:

Optional<DomainValue> myValue = 
    loadEntry("current")
        .map(BigDecimal::new)
        .map(x -> () -> x);

And there it is, the spiderman operator. We convert from Double to BigDecimal and then to DomainValue by saying that we want to convert our BigDecimal to “something that can supply a BigDecimal”, which is exactly what our DomainValue can do.

A bigger use case

Right now, the DomainValue type is nothing more than a mantle around a numerical value. But we can expand our domain to have more specific types:

public interface Voltage extends DomainValue {
}
public interface Power extends DomainValue {
    @Override
    default String denotation() {
        return "Electric power";
    }
}

Boring!

public interface Current extends DomainValue {
    default Power with(Voltage voltage) {
	return () -> value().multiply(voltage.value());
    }
}

Ok, this is maybe no longer boring. We can implement a lot of domain functionality just in interfaces and then instantiate ad-hoc types:

Voltage europeanVoltage = () -> BigDecimal.valueOf(220);
Current powerSupply = () -> BigDecimal.valueOf(2);
Power usage = powerSupply.with(europeanVoltage);

Or we load the values from our key-value-store:

Optional<Voltage> maybeVoltage = 
    loadEntry("voltage")
        .map(BigDecimal::new)
        .map(x -> () -> x);

Optional<Current> maybeCurrent = 
    loadEntry("current")
        .map(BigDecimal::new)
        .map(x -> () -> x);

You probably see it already: We have some duplicated code! The strange thing is, it won’t go away so easily.

The first call for help

But first I want to sanitize the code syntactically. The duplication is bad, but the spiderman operator is just unreadable.

If you have an idea how the syntax of the second map() call can be improved, please comment below! Just one request: Make sure your idea compiles beforehands.

Failing to eliminate the duplication

There is nothing easier than eliminating the duplication above: The code is syntactically identical and only the string parameter is different – well, and the return type. We will see how this affects us.

What we cannot do:

<DV extends DomainValue> Optional<DV> loadFor(String entry) {
    Optional<BigDecimal> maybeValue = load(entry);
    return maybeValue.map(x -> () -> x);
}

Suddenly, the spiderman operator does not compile with the error message:

The target type of this expression must be a functional interface

I can see the problem: Subtypes of DomainValue are not required to stay compatible to the functional interface requirement (just one method without implementation).

Interestingly, if we work with a wildcard for the generic, it compiles:

Optional<? extends DomainValue> loadFor(String entry) {
    Optional<BigDecimal> maybeValue = load(entry);
    return maybeValue.map(x -> () -> x);
}

The problem is that we still need to downcast to our specific subtype afterwards. But we can use this insight and move the downcast into the method:

<DV extends DomainValue> Optional<DV> loadFor(
	String entry,
	Class<DV> type
) {
	Optional<BigDecimal> maybeValue = load(entry);
	return maybeValue.map(x -> type.cast(x));
}

Which makes our code readable enough, but at the price of using reflection:

Optional<Voltage> european = loadFor("voltage", Voltage.class);
Optional<Current> powerSupply = loadFor("current", Current.class);

I’m not a fan of this solution, because downcasts are dangerous and reflection is dangerous, too. Mixing two dangerous things doesn’t neutralize the danger most of the time. This code will fail during runtime sooner or later, without any compiler warning us about it. If you don’t believe me, add a second method without implementation to the Current interface and see if the compiler warns you. Hint: This is what you will see at runtime:

java.lang.ClassCastException: Cannot cast java.math.BigDecimal to Current

But, to our surprise, it doesn’t even need a second method. The code above doesn’t work. Even if we reintroduce our spiderman operator (with an additional assignment to help the type inference), the cast won’t work:

<DV extends DomainValue> Optional<DV> loadFor(
    String entry,
    Class<DV> type
) {
    Optional<BigDecimal> maybeValue = load(entry);
    Optional<DomainValue> maybeDomainValue = maybeValue.map(x -> () -> x);
    return maybeDomainValue.map(x -> type.cast(x));
}

The ClassCastException just got a lot more mysterious:

java.lang.ClassCastException: Cannot cast Loader$$Lambda$8/0x00000008000028c0 to Current

My problem is that I am stuck. There is working code that uses the spiderman operator and produces code duplication, but there is no way around the duplication that I can think of. I can get objects for the supertype (DomainValue), but not for a specific subtype of it. If I want that, I have to accept duplication. Or am I missing something?

The second call for help

If you can think about a way to eliminate the duplication, please tell me (or us) in the comments. This problem doesn’t need to be solved for my peace of mind or the sanity of my code – the duplication is confined to a particular place.

Being used to roam nearly without boundaries in the Java syntax (25 years of thinking in Java will do that to you), this particular limitation hit hard. If you can give me some ideas, I would be grateful.

2 thoughts on “Help me with the Spiderman Operator”

  1. If I had such a setup and really wanted to change it, I’d go one of two ways:

    1) Give the Load Method a Function argument, which handles the wrapping
    That would at least allow us to get rid of the `BigDecimal::new` call in your examples.
    So now it would be like
    “`
    Current current = loadEntry(“current”, x -> () -> x); // No more BigDecimal::new
    “`

    We’d still have some duplication but that could be extracted to a Method reference or a static Field somewhere.
    Something like
    “`
    // as static method inside the interface
    Current current = loadValue(“current”, Current::of);

    // or as static field inside the interface
    Current current = loadValue(“current”, Current.of);

    //or as static field inside another class
    Current current = loadValue(“current”, SomeClass.CURRENT);

    //or as field and imported statically?
    import static SomeClass.CURRENT; // With SomeClass containing one field per subtype
    Current current = loadValue(“current”, CURRENT);
    “`
    You’d still have to provide the actual class/mapper in each call, but you’d be more typesafe than with reflection.
    Still, you’d need to implement this “spiderman method” once per subtype. Your call whether to do that inside the interface definition or a separate class.

    ===================

    2) Wrap your DataSource in another class that provides your specialized types
    Probably by delegating to the original data source.
    Then have that class injected wherever you use it instead of the original data class?

    Something like
    “`
    private final DataSource originalDataSource;

    public Optional loadCurrent(String name){
    return originalDataSource.load(name).map(BigDecimal::new).map(x -> () -> x);
    }

    public Optional loadVoltage(String name) {/* … */}
    // etc.
    “`
    At least now you have the duplication centralized in one codefile
    =====================

    Further Thought: Have code generation

    If we can’t get rid of code duplication, have that duplication automated!
    Why is duplication bad in the first place? Since we need to adapt multiple places if something changes. If we don’t write the code ourselves and the code magically changes in case we need it to change, then I wouldn’t say that duplication is bad per sé.

    Both examples above can easily be generated using an Annotation Processor (AP), as long as you are fine with it creating another class for you (so the example of static methods/fields inside your interfaces would not work here, but the examples of using a new class would).

    Though, using an AP has other challenges:
    -> Generated classes normally aren’t checked in, so when only looking at the sourcecode repository, the the code might be harder to understand
    -> You add complexity to the build step, since you need to tell your build-tool to use an AP.
    -> You need to maintain another piece of software (the AP itself).
    -> More challenges in designing the AP if your Domain Value Types are split across multiple projects

    ==================

    What do you think of these approaches?
    Both help you to limit the places where the duplication occur, but you still have duplication in place.

    Also, have you worked with Annotation Processors or other tools for code generation before and do you have some experiences to share?

  2. > But first I want to sanitize the code syntactically. The duplication is bad, but the spiderman operator is just unreadable.

    I agree that the spiderman operator is quite hard to read. Therefore, I simply suggest to avoid it and fall back to static factory methods on the functional interfaces. However, this does not solve the problem of code duplication:

    Optional maybeVoltage = loadEntry(“voltage”).map(Voltage::create);
    Optional maybeCurrent = loadEntry(“current”).map(Current::create);

    > If you can think about a way to eliminate the duplication, please tell me (or us) in the comments.

    For the sake of brevity I won’t describe in this comment why the suggestions in the article do not work.

    To make the extraction of the spiderman operator work, we need to create an interface extending all domain value interfaces:

    interface AllDomainValues extends Voltage, Power, Current { }

    With the help of this interface we can define a method that can be used instead of the spiderman operator `map(fakeSpider())`:

    private Function fakeSpider() {
    return x -> () -> x;
    }

    However, this has probably not the indented semantics, since the domain value objects can now be casted safely to all types of domain values.
    The solution is comparable to a record construction like in `map(FakeSpider::new)`:

    record FakeSpider(BigDecimal value) implements Voltage, Power, Current { }

    We can provide a better implementation for a spider method by using a type parameter as already suggested in the article. The spider method can be used for the Voltage or the Current class by using `map(spider(Voltage.class))` or `map(spider(Current.class))` respectively. We make use of the LambdaMetafactory to create a function object factory for the desired interface. The created function object factory can be bound to one BigDecimal value and produces a function object that has no parameters and returns the bound BigDecimal:

    private Function spider(Class domainValueType) {
    var factoryType = methodType(domainValueType, Object.class);
    var methodType = methodType(BigDecimal.class);
    try {
    var callSite = LambdaMetafactory.metafactory(MethodHandles.lookup(), “value”, factoryType, methodType, requireNonNull, methodType);
    var factory = callSite.getTarget();
    return value -> {
    try {
    return domainValueType.cast(factory.bindTo(value).invoke());
    } catch (Throwable throwable) {
    throw new IllegalArgumentException(throwable);
    }
    };
    } catch (LambdaConversionException exception) {
    throw new IllegalArgumentException(exception);
    }
    }

    The field `requireNonNull` is, thereby, a method handle to the `Objects.requireNonNull` method. It is used as an implementation for the function object factory and provides us with an additional null check:

    private static final MethodHandle requireNonNull;
    static {
    try {
    requireNonNull = MethodHandles.publicLookup()
    .findStatic(Objects.class, “requireNonNull”, methodType(Object.class, Object.class));
    } catch (NoSuchMethodException | IllegalAccessException exception) {
    throw new AssertionError(exception);
    }
    }

    Best Regards
    Sören

    PS: I am looking forward to the discussion of the spiderman operator in a future dev brunch.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.