enum Bool {
True,
False
}
enum Status {
Waiting,
Successful,
Failed
}
We can combine them into a product type, like a tuple, or a sum type, like a union. type Product = (Bool, Status)
type Sum = Bool | Status
Now, we ask ourselves, what are the valid values of type Product? (True, Waiting)
(True, Successful)
(True, Failed)
(False, Waiting)
(False, Successful)
(False, Failed)
There are two Bool values, and three Status values, so there are 2×3=6 values for the product of Bool and Status. Now, what about the Sum type? True
False
Waiting
Successful
Failed
We have 2+3=5 total values for the sum of Bool and Product.Now, obviously, this math doesn't quite work out for types with infinite values, like strings or bigints or arrays, but that's where the analogy comes from.
If you extend this further, you can even figure out what an exponential type would be: the exponential type Bool^Status is a function that takes a Status as its argument and returns a Bool.
In the switch statement it becomes a Sum value (as shown in the article).
In ReScript (shown as well, which uses OCaml under the hood) the syntax is that of a Variant, in which each element is a different nominal type.
type NotAProduct = Bool | Bool
only has 2 values, not 4. For a real product type, we need type Product = {tag: "Left", value: Bool} | {tag: "Right", value: Bool}
which has the requisite 4 values.Fff
Tff
Ftf
Fft
Ttf
Tft
Ftt
Ttt
All as possible implementations of that function. Neat...
Indeed it would drive the point home if instead of simply mentioning product they referred to Cartesian product.
I guess sum types imply adding together two domain, but unless there's a subtlety then union would be clearer as well.
That said, I wrote this[1] a year and a half ago for some colleagues just as I was getting into it myself. I hope it helps, and if not let me know if there's anything more specific I could help with.
We use another class based concept on top at work where you can compose multiple pieces of remote data together, but it really only works well for the happy path.
Would you prefer that it's a if-elseif-else based structure instead? I guess that would work too but I feel like it could be easier to write it in a way where you accidentally forget to handle some case (since your else / last return is a potential catch-all).
I personally don't mind switch-case because I'm so accustomed to it in ReasonML/ReScript already.