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