Skip to content

Instantly share code, notes, and snippets.

@radmen
Last active March 6, 2018 08:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save radmen/22139e20d0bdb2f9d95bb98f093a521d to your computer and use it in GitHub Desktop.
Save radmen/22139e20d0bdb2f9d95bb98f093a521d to your computer and use it in GitHub Desktop.
Promise/Future Result Maybe object!

The task

  1. get something from a remote site (loadPage())
  2. find specific element (findElement())
  3. grab and parse contents as JSON (extractJson())

stage 1

Easy way: grab page and return result

const task = async url => {
  // loadPage :: String -> Promise Page
  const page = await loadPage(url)
  
  // findElement :: Page -> DomElement|Null
  const element = findElement(page)
  
  // extractJson :: DomElement -> Object|Null
  return element ? extractJson(element) : null
}

stage 2

findElement and extractJson can return null. It's a perfect case for Maybe !

const task = async url => {
  const page = await loadPage(url)
  
  // findElement :: Page -> Maybe DomElement
  // extractJson :: DomElement -> Maybe Object
  return findElement(page)
    .chain(extractJson)
}

stage 3

why should I stop on Maybe. There're more fancy things!

Looks like Task is the right one for the job.

// loadPage :: String -> Task Error Page

stage 4

Hmm.. should Task return data wrapped in Result? Sounds quite logical!

// loadPage :: String -> Task Error (Result ?? Page)

stage 5

Does Task should return Future... Don't think so. Actually Promise is a Future so I'm replacing Task with Future.

stage 6

So, I have a Future which should return Result. Value of Result will be parsed and wrapped in Maybe.

Doest it mean that my function returns Future (Result Error (Maybe object))? :o

stage 7

The complexity of this thing weighs heavily on my shoulders...

stage 8

Ahh! forgot that task() should return a promise. Way more complicated..

stage 9 - finale

Fuck it! Promise returning Maybe is all I need.
going back to stage 2

@i-am-tom
Copy link

i-am-tom commented Mar 5, 2018

This definitely does not compile (just written freehand), but here's how I'd go about it:

const Future = require('fluture')
const Maybe = require('fantasy-maybe')

//+ fromNullable :: Nullable a -> Maybe a
const fromNullable = x =>
  x != null
    ? Maybe.Just(x)
    : Maybe.Nothing

//+ maybeToFuture :: e -> Maybe a -> Future e a
const maybeToFuture = error => xs =>
  xs.fold(Future.reject(error), Future.of)

//+ prop :: { (key :: String) :: a | ... } -> key -> Maybe a
const prop = xs => k => fromNullable(xs[k])



//+ getJSON :: Page -> Maybe String
const getJSON = page =>
  prop('path')(page)
  .chain(prop('to'))
  .chain(prop('your'))
  .chain(prop('json'))

//+ extractJson :: Element -> Maybe Data
const extractJson = data =>
  fromNullable(JSON.parse(data))

//+ loadPage :: String -> Future Error Page
const loadPage = url =>
  new Future((rej, res) =>
    fetch(url)
    .then(res)
    .catch(rej))

//+ main :: String -> Future Error Data
const main = url =>
  loadPage(url)
  .map(getJSON)                      // Future Error (Maybe String)
  .chain(maybeToFuture('Bad data!')) // Future Error String
  .map(extractJson)                  // Future Error (Maybe Data)
  .chain(maybeToFuture('Bad JSON!')) // Future Error Data

main.fork(
  e => console.error(e),
  x => console.log(x))

@radmen
Copy link
Author

radmen commented Mar 6, 2018

@i-am-tom : Thank you! I was sure that I should hold Maybe as a Data of Future (or, actually, Promise). Now it makes more sense. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment