Created
June 27, 2021 14:22
-
-
Save moatorres/49581c8c3cc6ec189ff2198509d6ccad to your computer and use it in GitHub Desktop.
Option monad in JS/Node (without classes)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Option } from './Option' | |
// let's say we have a function that returns Some or None | |
function divide(num, den) { | |
if (den === 0) return Option() | |
else return Option(num / den) | |
} | |
// this will return a Some type object | |
const resultsSome = divide(4, 1) | |
console.log(resultsSome.and(divide(100,10).unwrap())) | |
console.log(resultsSome.map(v => v + 1).unwrap()) | |
// this will return a None type object | |
const resultsNone = divide(4, 0) | |
console.log(resultsNone.unwrapOr('Oops!')) // => 'Oops!' | |
console.log(resultsNone.andThen((v) => v + 18972398173)) // → None | |
// our matcher object | |
const matcher = { | |
some: (res) => res + 1, | |
none: divide(10, 1).unwrap(), // => 10 | |
} | |
console.log(resultsSome.match(matcher)) // => 5 | |
console.log(resultsNone.match(matcher)) // => 10 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { isValid } from './utils' | |
const OptionType = { | |
Some: Symbol(':some'), | |
None: Symbol(':none'), | |
} | |
const makeSome = (val) => ({ | |
type: OptionType.Some, | |
isSome: () => true, | |
isNone: () => false, | |
match: (fn) => fn.some(val), | |
map: (fn) => makeSome(fn(val)), | |
andThen: (fn) => fn(val), | |
and: (optb) => optb, | |
unwrapOr: (_) => val, | |
unwrap: () => val, | |
or(_) { | |
return this | |
}, | |
}) | |
const makeNone = () =>({ | |
type: OptionType.None, | |
isSome: () => false, | |
isNone: () => true, | |
or: (_) => _, | |
map: (_) => None, | |
andThen: (_) => None, | |
and: (_) => None, | |
match(matchObject) { | |
const { none } = matchObject | |
if (typeof none === 'function') { | |
return none() | |
} | |
return none | |
}, | |
unwrapOr(def) { | |
if (!def) { | |
throw new Error('Cannot call unwrapOr with a missing value.') | |
} | |
return def | |
}, | |
unwrap() { | |
throw new ReferenceError('Trying to unwrap None.') | |
}, | |
}) | |
const Some = (val) => isValid(val) ? makeSome(val) : makeNone() | |
const None = makeNone() | |
export const Option = Object.assign( | |
(val) => (isValid(val) ? makeSome(val) : makeNone()), | |
{ | |
some: (val) => Some(val), | |
none: () => None, | |
} | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const isNumberZero = (value) => typeof value === 'number' && value === 0 | |
const isFalse = (value) => typeof value === 'boolean' && !value | |
const isEmptyString = (value) => typeof value === 'string' && value === '' | |
function notNullOrUndefined(value) { | |
if (isEmptyString(value)) return true | |
if (isNumberZero(value)) return true | |
if (isFalse(value)) return true | |
if (!value) return false | |
return typeof value !== null && typeof value !== undefined | |
} | |
export function isValid(value) { | |
return ( | |
!!value || | |
isNumberZero(value) || | |
isFalse(value) || | |
isEmptyString(value) || | |
notNullOrUndefined(value) | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment