Update: August 2019
Pleased you asked this question, I was also surprised initially at the difference in behaviour. As others have responded, it comes down to the way the Frisby Mostly Adequate Guide implementation was coded. The "irregular" implementation detail is related to the way in which isNothing
s function implementation is shielding the null or undefined value
passed in using Maybe.of
:
get isNothing() {
return this.$value === null || this.$value === undefined;
}
If you refer to other implementations - then using Maybe.of()
to create your Maybe
does allow you to pass in a null
or undefined
for the Just
case's value and actually print for example Maybe.Just({ value: null })
Instead, when using Folktale, create the Maybe
using Maybe.fromNullable()
which will allocate a Just
or Nothing
according to the value input.
Here's a working version of the code provided:
const Maybe = require("folktale/maybe");
const {
flip,
concat,
toUpper,
path,
pathOr,
match,
prop,
add
} = require("ramda");
console.log(Maybe.of("Malkovich Malkovich").map(match(/a/gi)));
//-> folktale:Maybe.Just({ value: ["a", "a"] })
console.log(Maybe.fromNullable(null).map(match(/a/gi)));
//-> folktale:Maybe.Nothing({ })
Finally, here's a demonstration implementation of Maybe, codified to use fromNullable
(similar to the Folktale implementation). I took this reference implementation from what I consider a highly recommended book - Functional Programming In JavaScript by Luis Atencio. He spends much of Chapter 5 explaining this clearly.
/**
* Custom Maybe Monad used in FP in JS book written in ES6
* Author: Luis Atencio
*/
exports.Maybe = class Maybe {
static just(a) {
return new exports.Just(a);
}
static nothing() {
return new exports.Nothing();
}
static fromNullable(a) {
return a !== null ? Maybe.just(a) : Maybe.nothing();
}
static of(a) {
return Maybe.just(a);
}
get isNothing() {
return false;
}
get isJust() {
return false;
}
};
// Derived class Just -> Presence of a value
exports.Just = class Just extends exports.Maybe {
constructor(value) {
super();
this._value = value;
}
get value() {
return this._value;
}
map(f) {
return exports.Maybe.fromNullable(f(this._value));
}
chain(f) {
return f(this._value);
}
getOrElse() {
return this._value;
}
filter(f) {
exports.Maybe.fromNullable(f(this._value) ? this._value : null);
}
get isJust() {
return true;
}
toString () {
return `Maybe.Just(${this._value})`;
}
};
// Derived class Empty -> Abscense of a value
exports.Nothing = class Nothing extends exports.Maybe {
map(f) {
return this;
}
chain(f) {
return this;
}
get value() {
throw new TypeError("Can't extract the value of a Nothing.");
}
getOrElse(other) {
return other;
}
filter() {
return this._value;
}
get isNothing() {
return true;
}
toString() {
return 'Maybe.Nothing';
}
};