Pft, dude whatever. This is HN. We don't want lame-o languages like Java, or boring monads like Maybe. We want crazy languages like JavaScript, and the mother of all monads, the continuation monad. Here's an equivalent to what's posted in this article, but more powerful, and in JavaScript:
// Haskell typeclass equivalent is >>=
// which has a signature of:
// m a -> (a -> m b) -> m b
// which in our case, is the more specific:
// Cont a b -> (b -> Cont a c) -> Cont a c
function bind(x, y) {
return function(k, end) {
return x(function(result) { return y(result)(k, end); }, end);
}
}
// 'yes' can also serve as identity for this monad. Haskell monad typeclass
// equivalent is 'return', not to be confused with JavaScript return. if
// this were an applicative functor, we would call it 'pure' instead, even
// though it does the same thing.
// b -> Cont a b
function yes(x) {
return function(k, end) { return k(x); };
}
// bail out.
// a -> Cont a b
function no(x) {
return function(k, end) { return end(x); };
}
// look up a user ID from a name string, in the context of a continuation
// monad. our type signature for this in Haskell would be:
// String -> Cont String UserId
// or something.
// 'UserId' would be a newtype wrapper around Integer, to prevent
// accidentally conflating it with account balance or another numeric type.
function userid_from_name(person_name) {
switch (person_name) {
case "Irek": return yes(1); // we have three user names in our system
case "John": return yes(2);
case "Alex": return yes(3);
case "Nick": return yes(1); // Nick is on the same acct as Irek
default: return no("No account associated with name " + person_name);
}
}
// UserId -> Cont String Balance
// 'Balance' would also be a wrapper around Integer type
function balance_from_userid(userid) {
switch (userid) {
case 1: return yes(1000000); // some amounts for a couple of accounts
case 2: return yes(75000);
default: return no("No balance associated with account #" + userid);
}
}
// Balance -> Cont String Balance
// we could do something fancier here if we liked, like pass the difference
// between the minimum required balance for a loan and the actual balance.
function balance_qualifies_for_loan(balance) {
if (balance > 200000) return yes(balance);
else return no("Insufficient funds for loan, current balance is " + balance);
}
// tada. put it all together.
// String -> String
function name_qualifies_for_loan(person_name) {
return bind(bind(userid_from_name(person_name), balance_from_userid),
balance_qualifies_for_loan)(
function(x) { return "This person qualifies for a loan. Their \
account has a balance of: " + x; },
function(x) { return "Do not issue loan, reason given: " + x; }
);
}
Test some output:
> name_qualifies_for_loan("Irek");
"This person qualifies for a loan. Their account has a balance of:
1000000"
> name_qualifies_for_loan("John");
"Do not issue loan, reason given: Insufficient funds for loan, current
balance is 75000"
> name_qualifies_for_loan("Alex");
"Do not issue loan, reason given: No balance associated with account #3"
> name_qualifies_for_loan("Nick");
"This person qualifies for a loan. Their account has a balance of:
1000000"
> name_qualifies_for_loan("Foo");
"Do not issue loan, reason given: No account associated with name Foo"
Pure functional. Holler at a player.