Skip to content

Instantly share code, notes, and snippets.

@radmen
Last active March 6, 2018 08:34
Show Gist options
  • 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()`)
1. find specific element (`findElement()`)
1. grab and parse contents as JSON (`extractJson()`)
# stage 1
Easy way: grab page and return result
```js
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` !
```js
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.
```js
// loadPage :: String -> Task Error Page
```
# stage 4
Hmm.. should `Task` return data wrapped in `Result`? Sounds quite logical!
```js
// 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