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
  • 74,672
  • 10
  • 115
  • 187
closeparen
  • 646
  • 4
  • 9
  • 1
    Probably depends on the type to some extent. Could you briefly describe what Event and its int/string members mean? For instance, is the `Event` type conceptually equivalent to having one `Int` and two `Maybe Strings`? – Ixrec May 10 '16 at 15:45
  • 1
    Is Java the language you want or you have to work with? – Thomas Junk May 10 '16 at 18:56
  • Pattern matching may be a future feature in Java 1x which is described in [JEP 305](http://openjdk.java.net/jeps/305). – tsh Apr 09 '18 at 03:16

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?).

NietzscheanAI
  • 256
  • 1
  • 5
  • Thanks, this is more or less what I was looking for. Visitor ended up working out reasonably well for this case, but I will likely use the Church example in the future. – closeparen May 11 '16 at 00:05
  • 2
    I think that's clever, but I'm not sure it's *idiomatic* – Brian Agnew May 12 '16 at 12:26
  • The idiomatic equivalent is double dispatch, where you pass one object to `t.match` with the three methods rather than passing three functions. ( the linked article confuses Visitor pattern with double dispatch - visitor is a pattern for abstracting the iteration over a network, not for selecting on patterns ) – Pete Kirkham Sep 19 '16 at 10:36
  • Double dispatch is one way of *implementing* the Visitor pattern. I've no idea what you mean by 'abstracting the iteration over a network' - that's not a phrase used in the GoF definition of Visitor. – NietzscheanAI Sep 19 '16 at 11:49
  • I'm struggling with this blog/article because the he starts with this in his 'bad' example: `public static int depth(Tree t)` where he uses chained if an instanceof when the 'right' way to do this in Java is to define an interface with the method: `public int depth()` and use polymorphism. It seems like a straw man. – JimmyJames Sep 19 '16 at 13:44
  • Simply imagine that polymorphism is not applicable/possible (e.g. third party object code). – NietzscheanAI Sep 19 '16 at 13:46
  • @NietzscheanAI What happens when your link dies? Your answer drops from 100% useful to 5% useful. – Chris Cirefice Sep 19 '16 at 14:13
  • Fair point (though I think that particular link is good for a while). When I get better connectivity, I'll try and address it... – NietzscheanAI Sep 19 '16 at 14:15
  • @JimmyJames with that you have to define every operation on the type in the interface upfront. Imagine next week being asked for a `int meanDepth(Tree t)`, `int medianDepth(Tree t)`, the week after for `List preorderElements(Tree t)`, `postorderElements(Tree t)` etc. You end up with a giant `interface Tree` – Caleth Sep 19 '16 at 14:26
  • @Caleth 1. Using polymorphism in one method doesn't preclude the use of other approaches in other methods. 2. Whether or not that is the case, it is still the canonical way to do this in Java. – JimmyJames Sep 19 '16 at 16:57
  • @NietzscheanAI OK but that doesn't really apply here does it? Also, you can always wrap other APIs. In such a case where someone has built an Java API that doesn't implement proper OO, I would recommend doing so since Java really only has one trick and that's polymorphism. You can't blame a language for your choice to not use it as it was designed to be used. – JimmyJames Sep 19 '16 at 17:01
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,676
  • 24
  • 19
  • I think your last paragraph hits the nail on the head. The idiomatic approach to this in Java is to use polymorphism. Have Event be an interface with an appropriate method, and Thing1, Thing2 and Thing3 be implementations of that interface. It splits the code by type rather than by function, but then that's basically the point of OOP, isn't it? – Jules May 10 '16 at 20:35
  • Java being imperative is immaterial. Java could (and should) integrate sum types, but it just doesn't. – gardenhead Sep 19 '16 at 23:26
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,162
  • 4
  • 51
  • 79
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 :(

jasiek.miko
  • 176
  • 3
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
  • 1
  • Could you clarify your affiliation with the project in your post (see, http://stackoverflow.com/help/promotion). – Benni Sep 17 '16 at 20:39