Initial data commit
This commit is contained in:
parent
72d218235f
commit
f23f22d71c
199087 changed files with 3378941 additions and 0 deletions
109
Task/Monads-Maybe-monad/JavaScript/monads-maybe-monad.js
Normal file
109
Task/Monads-Maybe-monad/JavaScript/monads-maybe-monad.js
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
// START WITH SOME SIMPLE (UNSAFE) PARTIAL FUNCTIONS:
|
||||
|
||||
// Returns Infinity if n === 0
|
||||
function reciprocal(n) {
|
||||
return 1 / n;
|
||||
}
|
||||
|
||||
// Returns NaN if n < 0
|
||||
function root(n) {
|
||||
return Math.sqrt(n);
|
||||
}
|
||||
|
||||
// Returns -Infinity if n === 0
|
||||
// Returns NaN if n < 0
|
||||
function log(n) {
|
||||
return Math.log(n);
|
||||
}
|
||||
|
||||
|
||||
// NOW DERIVE SAFE VERSIONS OF THESE SIMPLE FUNCTIONS:
|
||||
// These versions use a validity test, and return a wrapped value
|
||||
// with a boolean .isValid property as well as a .value property
|
||||
|
||||
function safeVersion(f, fnSafetyCheck) {
|
||||
return function (v) {
|
||||
return maybe(fnSafetyCheck(v) ? f(v) : undefined);
|
||||
}
|
||||
}
|
||||
|
||||
var safe_reciprocal = safeVersion(reciprocal, function (n) {
|
||||
return n !== 0;
|
||||
});
|
||||
|
||||
var safe_root = safeVersion(root, function (n) {
|
||||
return n >= 0;
|
||||
});
|
||||
|
||||
|
||||
var safe_log = safeVersion(log, function (n) {
|
||||
return n > 0;
|
||||
});
|
||||
|
||||
|
||||
// THE DERIVATION OF THE SAFE VERSIONS USED THE 'UNIT' OR 'RETURN'
|
||||
// FUNCTION OF THE MAYBE MONAD
|
||||
|
||||
// Named maybe() here, the unit function of the Maybe monad wraps a raw value
|
||||
// in a datatype with two elements: .isValid (Bool) and .value (Number)
|
||||
|
||||
// a -> Ma
|
||||
function maybe(n) {
|
||||
return {
|
||||
isValid: (typeof n !== 'undefined'),
|
||||
value: n
|
||||
};
|
||||
}
|
||||
|
||||
// THE PROBLEM FOR FUNCTION NESTING (COMPOSITION) OF THE SAFE FUNCTIONS
|
||||
// IS THAT THEIR INPUT AND OUTPUT TYPES ARE DIFFERENT
|
||||
|
||||
// Our safe versions of the functions take simple numeric arguments, but return
|
||||
// wrapped results. If we feed a wrapped result as an input to another safe function,
|
||||
// it will choke on the unexpected type. The solution is to write a higher order
|
||||
// function (sometimes called 'bind' or 'chain') which handles composition, taking a
|
||||
// a safe function and a wrapped value as arguments,
|
||||
|
||||
// The 'bind' function of the Maybe monad:
|
||||
// 1. Applies a 'safe' function directly to the raw unwrapped value, and
|
||||
// 2. returns the wrapped result.
|
||||
|
||||
// Ma -> (a -> Mb) -> Mb
|
||||
function bind(maybeN, mf) {
|
||||
return (maybeN.isValid ? mf(maybeN.value) : maybeN);
|
||||
}
|
||||
|
||||
// Using the bind function, we can nest applications of safe_ functions,
|
||||
// without their choking on unexpectedly wrapped values returned from
|
||||
// other functions of the same kind.
|
||||
var rootOneOverFour = bind(
|
||||
bind(maybe(4), safe_reciprocal), safe_root
|
||||
).value;
|
||||
|
||||
// -> 0.5
|
||||
|
||||
|
||||
// We can compose a chain of safe functions (of any length) with a simple foldr/reduceRight
|
||||
// which starts by 'lifting' the numeric argument into a Maybe wrapping,
|
||||
// and then nests function applications (working from right to left)
|
||||
function safeCompose(lstFunctions, value) {
|
||||
return lstFunctions
|
||||
.reduceRight(function (a, f) {
|
||||
return bind(a, f);
|
||||
}, maybe(value));
|
||||
}
|
||||
|
||||
// TEST INPUT VALUES WITH A SAFELY COMPOSED VERSION OF LOG(SQRT(1/X))
|
||||
|
||||
var safe_log_root_reciprocal = function (n) {
|
||||
return safeCompose([safe_log, safe_root, safe_reciprocal], n).value;
|
||||
}
|
||||
|
||||
return [-2, -1, -0.5, 0, 1 / Math.E, 1, 2, Math.E, 3, 4, 5].map(
|
||||
safe_log_root_reciprocal
|
||||
);
|
||||
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue