Created
November 16, 2016 04:50
-
-
Save mrosata/fdf722eb83d63a1f05fe3b22d4fdc2bd to your computer and use it in GitHub Desktop.
Functional Programming with IO, Maybe, Just, Nothing -- Touch of Reactive
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
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