Function encoded types (i.e. nested curried functions) have some drawbacks in Javascript:
- Their representation in the dev console is obfuscated (e.g.
[Some(5), None]
is displayed as[f, f]
) - Nothing stops you from applying a combinator to the wrong type (e.g.
eitherMap(f) (Some(5))
) - You cannot inspect their free variables
- You cannot even duck type them
These drawbacks render them useless in real world applications.
I was wondering if there is a way to overcome these shortcomings and came up with the following sketch:
const tag = (name, x, k) => {
const Cons =
Function(`return function ${name}() {}`) ();
Cons.prototype[Symbol.toStringTag] = name;
Cons.prototype["run" + name] = k;
Cons.prototype.tag = name;
const o = new Cons();
Object.defineProperty(o, "value", {get: () => x});
return o;
};
const Some = x =>
tag("Option", x, def =>
tag("Option", x, k => k(x)));
const None = tag("Option", null, def =>
tag("Option", def, k => def));
const option = def => k => fx =>
fx.runOption(def).runOption(k);
const safeInc = option(0) (n => n + 1);
safeInc(Some(5)); // 6
safeInc(None); // 0
const xs = [Some("foo"), None]; // [Option, Option]
/*
expanded dev console display:
1: Option {value: ...} --> expands to "foo"
2: Otpion {value: ...} --> expands to null
*/
Please note that I am not interested in prototypal inheritance at all.
This approach is both tedious and probably slow, because I apply the Function
constructor, which makes the code less predictable. Is there a better way to give curried functions a type (or rather a tag in JS), so that the listed shortcomings are eliminated?
I slightly improved my approach and got rid of the
Function
call and the repeated creation of a constructor function. It's appropriate for my specific use case (function encoded types) but I failed to address the more general case of arbitrary functions in curried form, because it is still too tedious. Anyway, here it is: