@TikhonJelvis: Another way to look at it: in Haskell, the empty list is a polymorphic value, as you point out. AFAIK, neither C# nor Java allow polymorphic values, so you simply can't have an empty list of type Empty<T>, it has to be of some concrete type Empty<Something>. Since you want that type to be substitutable for every empty list, it needs to be a subtype of all lists, which can be achieved if Something is a subtype of all types, i.e. a bottom type.
–
Jörg W MittagAug 8 '12 at 16:23

2

@Euphoric, paradigm, schmaradigm, who cares? ADTs are orthogonal to the functional programming (or OOP or whatever else). Encoding an AST of any language is quite a pain without decent ADTs support, and compiling that language is a pain without another paradigm-agnostic feature, pattern matching.
–
SK-logicSep 7 '12 at 17:53

Sealing is achieved by the Visitor class. Each of its methods declares how to deconstruct one of the subclasses. You could add more subclasses, but it would have to implement accept and by calling one of the visit... methods, so it would either have to behave like Cons or like Nil.

The problem with null is that it is too general: it represents the absence of anything, i.e. emptiness in general, but I want to represent the absence of list elements, i.e. an empty list in particular. An empty list and an empty tree should have distinct types. Also, the empty list needs to be an actual value because it still has behavior of its own, so it needs to have its own methods. To construct the list [1, 2, 3], I want to say Empty.prepend(3).prepend(2).prepend(1) (or in a language with right-associative operators 1 :: 2 :: 3 :: Empty), but I can't say null.prepend ….
–
Jörg W MittagAug 8 '12 at 16:19

@JörgWMittag: The nulls do have distinct types. You can also easily create typed constant with value null for the purpose. But it's true you can't call methods on it. Your approach with methods does not work without element-type-specific Empty anyway.
–
Jan HudecAug 9 '12 at 7:42

You can have an Empty and an Empty<> and abuse implicit conversion operators to allow a fairly practical simulation, if you want. Essentially, you use Empty in code, but all type signatures etc only use the generic variants.
–
Eamon NerbonneMay 28 '14 at 10:51

I've seen a Java version of this technique before, but lambdas and named parameters make it so much readable. +1!
–
DovalFeb 7 '14 at 18:37

1

I think the problem here is that Right isn't generic over the type of error. Something like: class Right<R> : Either<Bot,R>, where Either is changed to an interface with covariant (out) type parameters, and Bot is the bottom type (subtype of every other type, opposite of Object). I don't think C# has a bottom type.
–
croydFeb 24 at 16:33

The only thing needed beyond naive subclassing is a way to seal classes, i.e. a way to make it impossible to add subclasses to a hierarchy.

In Java you can't. But you can declare the base class as package private, which means that all direct subclasses have to belong to the same package as the base class. If you then declare the subclasses as final, they can't be subclassed any further.

I don't have a real problem, or I would have posted this on StackOverflow, not here :-) An important property of Algebraic Data Types is that they can be closed, which means that the number of cases is fixed: in this example, a list is either empty or it isn't. If I can statically ensure that this is the case, then I can make dynamic casts or dynamic intanceof checks "pseudo-type-safe" (i.e.: I know it's safe, even if the compiler doesn't), by simply ensuring that I always check those two cases. If, however, someone else adds a new subclass, then I can get runtime errors I didn't expect.
–
Jörg W MittagAug 8 '12 at 14:19

@JörgWMittag - Well Java clearly doesn't support that ... in the strong sense that you seem to be wanting. Of course, you can do various things to block unwanted subtyping at runtime, but then you get "runtime errors that you don't expect".
–
Stephen CAug 8 '12 at 23:33

The only thing needed beyond naive subclassing is a way to seal classes, i.e. a way to make it impossible to add subclasses to a hierarchy.

How would you approach this problem in a language like C# or Java?

There isn't a good way to do this, but if you're willing to live with a hideous hack then you can add some explicit type checking to the abstract base class' constructor. In Java, this would be something like