Skip to content

Instantly share code, notes, and snippets.

@moatorres
Created June 27, 2021 14:22
Show Gist options
  • Save moatorres/49581c8c3cc6ec189ff2198509d6ccad to your computer and use it in GitHub Desktop.
Save moatorres/49581c8c3cc6ec189ff2198509d6ccad to your computer and use it in GitHub Desktop.
Option monad in JS/Node (without classes)
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
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,
}
)
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