10

I'm building a simulator which parses some events from STDIN and "runs" them. My background is mostly functional programming these days, so it seemed natural to do something like this:

data Event = Thing1 String Int | Thing2 Int | Thing3 String String Int
Parse :: String -> [Event]
Simulate :: [Event] -> [Result]

where simulate would be

case event
  of Thing1 a b => compute for thing one
   | Thing2 a => compute for thing two

etc. What is the idiomatic way to do this sort of thing in Java? Googling has pointed me in the direction of nested classes and the visitor pattern, but that seems rather heavyweight in my attempt. Type erasure seems to be fighting me, hard. Could you show me an outline of what that would look like done correctly?

Christophe
  • 81,699

5 Answers5

12

The author of 'Functional Programming in Scala' gives a nice illustration of the best that can be achieved in Java in a type safe manner:

http://blog.higher-order.com/blog/2009/08/21/structural-pattern-matching-in-java/

Essentially, it uses a Church-encoding of the cases to ensure that the compiler will complain if any are missing.

The details are not readily summarized and indeed are so well covered in the article that there's no point in reproducing them here (that's what hyperlinks are for right?).

4

What is the idiomatic way to do this sort of thing in Java?

There isn't really such a thing, given that Java (the language) is fundamentally imperative.

If you can run on the JVM, but not restricted to the Java language, you could investigate Scala, which would achieve something like the above using pattern matching.

Otherwise I think you're reduced to manually matching your various cases and calling methods as appropriate, or perhaps defining subtypes of 'Event' and using polymorphism to invoke particular methods for each subtype.

Brian Agnew
  • 4,686
1

Take a look at https://github.com/johnlcox/motif which is a Scala-like "pattern matching" library for Java 8.

Not nearly as nice as ML / Erlang / Haskell, but still looks much more declarative than most.

9000
  • 24,342
0

You could use an enum and interface, overriding the simulate method, like this:

interface Event {
  void simulate()
}

enum MyEvents implements Event {
  THING1 {
    @Override
    void simulate() {
    //...
    }
  },
  THING2 {
    @Override
    void simulate() {
    //...
    }
  },
}

Let's say you have Event event. Then you can use it in one of two ways:

event.simulate();

or

switch(event) {
  case THING1:
    //..
    break;
  case THING2:
    break;
}

But the constructor gets called automatically by the JVM, so to also store the parameters there you're going to have to add properties with accessors etc.

Alternatively, you could code up your events as constant strings and use the switch construct, in which case you'd do

string event = args[0];
switch(event){
  case THING1:
    int a = args[1];
    //...
    break;
  case THING2:
    int a = args[1];
    int b = args[2];
    break;
}

etc. But yes, there's nothing native that directly mimics pattern matching :(

0

Visitor pattern or its church encoding equivalent is the way to go. It is rather verbose in Java, but hopefully tools like Derive4J (an annotation processor I maintain) or Adt4J can generate the boilerplate. Using such a tool your example become:

import java.util.function.Function;
import org.derive4j.Data;

@Data
public abstract class Event {

  interface Cases<X> {
    X Thing1(String s, int i);
    X Thing2(int i);
    X Thing3(String s, String s2, int i);
  }

  abstract <X> X match(Cases<X> cases);

  static Function<Event, Result> Simulate =
      Events.cases().
          Thing1( (s, i    ) -> computeForThingOne(s, i)       ).
          Thing2( (i       ) -> computeForThingTwo(i)          ).
          Thing3( (s, s2, i) -> computeForThingThree(s, s2, i) );

}

Derive4J generate the Events class that provide a fluent pattern matching syntax (with exhaustive check that all cases are handled).

JbGi
  • 1