| > This has the benefit of giving you the ability to refer to a case as its own type. A case of a sum-type is an expression (of the variety so-called a type constructor), of course it has a type. datatype shape =
Circle of real
| Rectangle of real * real
| Point
Circle : real -> shape
Rectangle : real * real -> shape
Point : () -> shape
A case itself isn't a type, though it has a type. Thanks to pattern matching, you're already unwrapping the parameter to the type-constructor when handling the case of a sum-type. It's all about declaration locality. (real * real) doesn't depend on the existence of shape.The moment you start ripping cases as distinct types out of the sum-type, you create the ability to side-step exhaustiveness and sum-types become useless in making invalid program states unrepresentable. They're also no longer sum-types. If you have a sum-type of nominally distinct types, the sum-type is contingent on the existence of those types. In a class hierarchy, this relationship is bizarrely reversed and there are knock-on effects to that. > You declare the sum type once, and use it many times. And you typically write many sum-types. They're disposable. And more to the point, you also have to read the code you write. The cost of verbosity here is underestimated. > Slightly more verbose sum type declaration is worth it when it makes using the cases cleaner. C#/Java don't actually have sum-types. It's an incompatible formalism with their type systems. Anyways, let's look at these examples: C#: public abstract record Shape;
public sealed record Circle(double Radius) : Shape;
public sealed record Rectangle(double Width, double Height) : Shape;
public sealed record Point() : Shape;
double Area(Shape shape) => shape switch
{
Circle c => Math.PI * c.Radius * c.Radius,
Rectangle r => r.Width * r.Height,
Point => 0.0,
_ => throw new ArgumentException("Unknown shape", nameof(shape))
};
ML: datatype shape =
Circle of real
| Rectangle of real * real
| Point
val result =
case shape of
Circle r => Math.pi * r * r
| Rectangle (w, h) => w * h
| Point => 0.0
They're pretty much the same outside of C#'s OOP quirkiness getting in it's own way. |
Quite the opposite, that gives me the ability to explicitly express what kinds of values I might return. With your shape example, you cannot express in the type system "this function won't return a point". But with sum type as sealed inheritance hierarchy I can.
> C#/Java don't actually have sum-types.
> They're pretty much the same
Not sure about C#, but in Java if you write `sealed` correctly you won't need the catch-all throw.
If they're not actual sum types but are pretty much the same, what good does the "actually" do?