4 ms·
Ok, it's perhaps a true Scotsman argument but saying that Java as a syntax for ADT is like saying JS (pre 2015) has classes because it can be simulated by funct
by _old_dude_ 5y ago
Ok, it's perhaps a true Scotsman argument but saying that Java as a syntax for ADT is like saying JS (pre 2015) has classes because it can be simulated by function + prototype or that Java (pre 8) has lambdas because it can be simulated by anonymous classes.
Java misses the syntactic sugar to define ADT in one place like in Standard ML, OCaml or F#.
The closest you get is to declare records inside a sealed interface, but once you add generics in the mix, it starts to get messy.
I was not able to define a classical list with cons and nil in Java.
sealed interface List<T> {
record Cons<T>(T car, List<T> cdr) implements List<T> {}
record Nil() implements List {}
}
The compiler give you warnings, Cons<T> or Nil are not a top level type and you can create several instances of Nil.
- pron 5y ago> Java misses the syntactic sugar to define ADT in one place like in Standard ML, OCaml or F#. I don't think so. > I was not able to define a classical list with cons and nil in Java. If you want to restrict Nil to a single instance, don't make it a record class: sealed interface List<T> { static <T> List<T> cons(T car, List<? extends T> cdr) { return new Cons<T>(car, cdr); } static <T> List<T> nil() { return (List<T>)Nil.INSTANCE; } record Cons<T>(T car, List<? extends T> cdr) implements List<T> {} enum Nil implements List<Void> { INSTANCE } } You can statically import if you mind them not being "top level": import static List.*; And then write, say: var x = cons("a", nil()); var y = cons(1, x); Not as succinct as ML, but workable. Although, in this particular case, record List<T>(T car, List<? extends T> cdr){} would suffice.
- _old_dude_ 5y agoPattern matching does not work if you use an enum List<String> list = cons("a", nil()); switch(list) { case Cons<String>(var car, var cdr) -> ... case Nil -> ... } Nil is typed List<Void> which is not compatible with List<String>. You may be able to use extractors as the inverse of the static methods cons() and nil() but that adds more boilerplate. Disjunctive enums like in Rust or Scala 3 is a simpler way to define ADT.
- pron 5y agoPattern matching will work fine with an additional explicit nil() pattern. I agree that in this particular case it's not quite as succinct as in more functional languages, but virtually all cases in ordinary business applications are not like that, and the additional code, if any, is O(1). Of course, Java could add rules for the special case of zero-argument records, making them more enum-like, but I really think the value in doing that is negative. Other than being able to directly translate ML examples, it would add complexity that isn't worth it. In fact, in this particular case I'd do it like so: record List<T>(T car, List<? extends T> cdr) { public List { if (car == null) throw new IllegalArgumentException("null element"); } } Which would give you: switch(list) { case List<String>(var car, var cdr) -> ... case null -> ... } It might not be exactly what people who like Scala might prefer, but it is very much a good representation of ADTs for those who prefer Java.