An Interest In:
Web News this Week
- February 16, 2025
- February 15, 2025
- February 14, 2025
- February 13, 2025
- February 12, 2025
- February 11, 2025
- February 10, 2025
Sum Types in JavaScript
Write You Some FP Maths
In this series I'll be looking at some mathematical concepts and functional programming stuff in JavaScript to help solidify the concepts and just generally gain experience and muscle memory. I also hope it's useful to readers.
FP FTW!
Sum Types
A Sum type is also known as a tagged union, variant, variant record, choice type, discriminated union, disjoint union, or coproduct. more
You may have heard of union types from languages like TypeScript, but sum types are a bit different (actually quite different but lets not get into that now). A union type uses something called type refinement to get defined types and depending on the language will fail to compile if you create an instance where an unexpected type is used.
In the case of sum types pattern matching is used to do the same thing as type refinement, and can also be used to handle a null or unexpected type case. Both solve similar issues of avoiding runtime errors with a type mismatch or an undefined/null value.
Sum types can be thought of as a datatype that can be one of a number of types (known as products) that have their own definition or shape. Each of these types has a tag
that is used to identify the type in pattern matching. Except this is JavaScript so we can't actually do pattern matching. We can either do a series of if
statements, or a switch statement. A switch looks cleaner, so let's make that easier by adding a tag
to the product types.
For example, if we define some money types:
const Coin = (int, qty, unit) => ({ tag: 'Coin', value: int * (unit === 'cent' ? 0.01 : 1.0) * qty,});const Note = (int, qty) => ({ tag: 'Note', value: int * qty,});
Then the sum type would be Money
:
// Money :: Coin | Noteconst Money = { 'Coin': Coin, 'Note': Note,}
We can assign some Money
to a variable and pass it into a pattern matching (simple switch thanks to the tag
) function that will give us the value based on the Money type (Coin
or Note
).
function value(m) { switch(m.tag) { case 'Coin': case 'Note': return `Value: $${m.value}`; default: return `Value: $0`; }}const coins = Money.Coin(10, 5, 'cent');value(coins); // => Value: $0.5const cash = Money.Note(6, 20);value(cash); // => Value: $120
There you go, you've successfully built your own sum type complete with products. Well done
What next...?
Well this is all very good, but wouldn't it be amazing if we could define these sum types without having to define the tag
property on every product? Say maybe input a bunch of identifier tags and their corresponding data structures when we define the sum type?
The answer is yes, yes it would. So lets do that!
In this example we're going to borrow from the Elixir language and their awesome pattern matching over function arguments. But still using the money example.
const defineProducts = (fields) => fields.reduce( (acc, T) => ({ // This order means first come first called [T.length]: T, ...acc, }), {}, );const SumType = (types, failure) => { const products = defineProducts(types); return (...args) => { const len = String(args.length); if (products[len]) { return products[len](...args); } return failure(...args); }};
defineProducts
generates an object with the type's parameter number as the key and the type (product) as the value. The SumType
function takes an array of types and a type (in this case an inline function) if no match can be found.
Now to define the sum type:
const Money = SumType( [Coin, Note], (_) => { throw new TypeError('Invalid data structure provided'); });
Now time to give it a go:
const coins = Money(10, 5, 'cent');`Value: $${coins.value}`; // => Value: $0.5const cash = Money(6, 20);`Value: $${cash.value}`; // => Value: $120
With this structure there's no need to call Money.Coin
or Money.Note
because the parameters supplied are matching the type.
Conclusion
Sum types can come in super useful, even in JavaScript, and can be configured in lots of different ways for difference use cases. Luckily some smart folks have come up with libraries to generate them easily.
Check out Daggy or sum-type.
If you have any questions of feedback feel free to comment
Happy coding
Original Link: https://dev.to/moosch/sum-types-in-javascript-15il

Dev To

More About this Source Visit Dev To