Skip to content

Instantly share code, notes, and snippets.

@mrosata
Created November 16, 2016 04:50
Show Gist options
  • Save mrosata/fdf722eb83d63a1f05fe3b22d4fdc2bd to your computer and use it in GitHub Desktop.
Save mrosata/fdf722eb83d63a1f05fe3b22d4fdc2bd to your computer and use it in GitHub Desktop.
Functional Programming with IO, Maybe, Just, Nothing -- Touch of Reactive
console.clear()
// The Ramda Library functions you need to breathe.
const Maybe = iJustMetYouThisIsCrazyImAMonadSoCallMeMaybe()
const {
map, chain, compose, reduce, filter, prop, curry
} = R
// fromEvent :: Str -> Elem -> Obs
const fromEvent = curry((eventType, elem) => {
return Rx.Observable.fromEvent(elem, eventType)
})
// updateDOM :: Str -> Null
const updateDOM = compose( runIO, writeHTML('#read-out') )
// getTargetVal Maybe m :: EventObj -> m Str
const getTargetVal = compose( prop('value'), prop('target') )
// readInputStream :: Maybe m => Obj -> m Str
const readInputStream = compose(map(updateDOM), map(getTargetVal), maybe )
// createStream$ :: Maybe m => m Elem -> m Obs
const createStream$ = compose( debounceTime(25), fromEvent('keyup') )
// readFromStream :: Maybe m => m Elem -> m Stream
const readFromStream = compose( subscribe(readInputStream), createStream$ )
// (type to input to display value, debouncing 25ms (which I'm aware is pointless))
runIO(
ioMapElemToFn(map(readFromStream), '#my-input'))
// querySelector :: Str -> IO Elem
function querySelector(sel) {
return io(() => document.querySelector(sel))
}
// writeHTML :: Str -> IO(Maybe)
function writeHTML(selector) {
return (htmlStr) => {
return io(() => {
try {
document.querySelector(selector).innerHTML = htmlStr
}
catch(e) {
console.log('eeeee', e)
}
})
}
}
// ioMapElemToFn :: (Str -> (a -> b)) -> IO
function ioMapElemToFn (composeFn, selector) {
// Run the IO which grabs elem from DOM and maps to fn
return map(compose(composeFn, maybe), querySelector(selector))
}
/** RxJS Specific subscribe :: Fn -> Stream -> Subscriber **/
function subscribe(fn) {
return (stream_$) => stream_$.subscribe(fn)
}
/** RxJS Specific debounceTime :: Int -> Stream -> Observable **/
function debounceTime(ms) {
return (stream_$) => stream_$.debounceTime(ms)
}
/*****
* Utils
*/
// log :: Str -> a -> a (side effect wrapped in IO (but is that really needed here?))
function tap(label) {
return (val) => {
console.log(label, val)
return val
}
}
/* Helper:: kickstart an obfuscated I/O operation */
function runIO(_io) {
return _io.run()
}
/* Helper:: maybe fromNullable() (data.maybe) with fallback to monetjs fromNull() */
function maybe(val) {
return !!Maybe.fromNullable ? Maybe.fromNullable(val) : Maybe.fromNull(val)
}
/* Helper:: Either monad factory */
function either(val) {
return throw "Either not implemented ya dope" // Either.of(val)
}
/* Helper:: IO functor factory */
function io(computation) {
return new IO(computation)
}
/* Class:: My rigg of an IO Functor */
function IO(computation) {
this.run = computation
this.map = (fn) => {
this.run = compose(fn, computation)
return this
}
}
/**
* These are my poor implementations of Maybe, Just, Nothing.
* Not only are they untested... but all missing methods too.
* Some people might argue that if a functor has a chain
* method.. then it is a monad regardless if it works or not.
* Those people, just like my implementation... are wrong.
*/
function iJustMetYouThisIsCrazyImAMonadSoCallMeMaybe() {
function Maybe() {}
Object.setPrototypeOf(Maybe, {
constructor(val) {
this._val = val
},
of(val) {
return new Just(val)
},
fromNullable(val) {
return typeof val === undefined || val === null ? new Nothing() : new Just(val)
},
fromEither() {/* if you call this method... you will be dissapointed */},
isJust() {return false},
isNothing() {return false}
})
class Nothing {
getOrElse(val) {
return val
}
get() {
throw "What's wrong with you?.. Nothing()"
}
map(fn) {
return this
}
isJust() {return false}
isNothing() {return true}
isEqual(monadB) {return !!monadB && monadB.isNothing()}
toString() {return 'Nothing()'}
}
class Just {
constructor(val) {
this._val = val
}
getOrElse(val) {
return this._val
}
get() {
return this._val
}
map(fn) {
return new Just(fn(this._val))
}
isJust() {return true}
isNothing() {return false}
isEqual(monadB) {
return !!monadB && monadB.isJust() && monadB.get() === this._val
}
ap() {/* if you call this method... you will be dissapointed */}
chain() {/* if you call this method... you will be dissapointed */}
toString() {return `Just(${this._val})`}
}
return Maybe
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment