この利点は、ケースを独自の型として参照できるようになることだ。和集合型のケースは、もちろん型を持つ表現(いわゆる型コンストラクタの種類)だ。 datatype shape = Circle of real | Rectangle of real * real | Point Circle : real -> shape Rectangle : real * real -> shape Point : () -> shape ケース自体は型ではないけど、型は持っている。パターンマッチングのおかげで、和集合型のケースを扱うときに型コンストラクタのパラメータをすでに展開している。すべては宣言の局所性に関することだ。(real * real)はshapeの存在に依存しない。和集合型からケースを異なる型として取り出し始めると、網羅性を回避する能力が生まれ、和集合型は無効なプログラム状態を表現できなくなる。もう和集合型ではなくなる。名目上異なる型の和集合型がある場合、その和集合型はそれらの型の存在に依存している。クラス階層では、この関係が奇妙に逆転していて、影響が出る。 > 和集合型を一度宣言して、何度も使う。そして通常は多くの和集合型を書く。使い捨てだし、もっと言えば、自分が書いたコードを読む必要もある。ここでの冗長性のコストは過小評価されている。 > 少し冗長な和集合型の宣言は、ケースを使いやすくするために価値がある。C#/Javaには実際には和集合型がない。彼らの型システムとは互換性のない形式主義だ。とにかく、これらの例を見てみよう: 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 ほとんど同じだよね、C#のOOPの奇妙さが邪魔してるけど。