I have only a theoretical idea of Haskell's sum types. And yet I sense that they really matter in Haskell and change the way you model your data in a fundamental way. Since I believe that they can be useful in dynamically typed languages as well, I tried to implement a sensible approximation in Javascript (I have only a superficial knowledge of Haskell).
Here is a more or less useful example of a Name
sum type, that is able to handle various name formats. I know that Haskell distinguishes between type and data constructors and probably has good reasons to make such distinction. However, I guess that it is not possible to map this concept to Javascript. And pattern matching neither.
Anyway, my actual question is: Does the following implementation embody the nature of sum types and the way they are applied in Haskell?
Please note: I am not sure if this kind of cross-language questions are welcome on SO. Please let me know in case I should avoid them.
// auxiliary functions
const A = f => x => f(x);
const show = api => api.show;
// the type constructor
const Name = (...xs) => A(({length: len}) => {
switch (len) {
case 1: {
let [{length: len}] = xs; // no pattern matching but destructuring
if (len > 1) { // no Char type in JS
return k => k({show: () => xs[0]}); // returns the API
}
break;
}
case 2: {
let [{length: len}, {length: len2}] = xs;
if (len > 1 && len2 > 1) {
return k => k({show: () => `${xs[0]} ${xs[1]}`});
}
if (len === 1 && len2 > 1) {
return k => k({show: () => `${xs[0]}. ${xs[1]}`});
}
break;
}
case 3: {
let [{length: len}, {length: len2}, {length: len3}] = xs;
if (len > 1 && len2 > 1 && len3 > 1) {
return k => k({show: () => `${xs[0]} ${xs[1]} ${xs[2]}`});
}
if (len > 1 && len2 === 1 && len3 > 1) {
return k => k({show: () => `${xs[0]} ${xs[1]}. ${xs[2]}`});
}
if (len === 1 && len2 === 1 && len3 > 1) {
return k => k({show: () => `${xs[0]}. ${xs[1]}. ${xs[2]}`});
}
}
default: throw new TypeError();
}
}) (xs);
// run
console.log(Name("Kerouac") (show) ());
console.log(Name("Hans", "Hölzel") (show) ());
console.log(Name("H", "Curry") (show) ());
console.log(Name("Jean", "Luc", "Godard") (show) ());
console.log(Name("William", "S", "Burroughs") (show) ());
console.log(Name("E", "W", "Dijkstra") (show) ());
[EDIT]
Sorry, I should have provided some Haskell code:
type FirstName = String
type LastName = String
type MiddleName = String
data Name = FullName FirstName LastName
| NameWithMiddle FirstName MiddleName LastName
| NameWithMiddleInitial FirstName Char LastName
| TwoInitialsWithLast Char Char LastName
| OneInitialWithLast Char LastName
| LastNameOnly LastName
I am not sure if this is valid though.
I guess the problem with my approach is that I try to implement the type constructor Name
, while I should implement the value constructors, right?
Either Char String
, but this is much easier to write as(Bool, String)
especially in JS. While I see how this is 'technically' a sum type, it's not used like this in Haskell (esp. embedding smart constructors in the type). – user2407038daggy
, perhaps? It uses a kind of Church encoding, with some syntactic niceties. – phipsgabler