Skip to content

Instantly share code, notes, and snippets.

@i-am-tom
Created May 15, 2017 19:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save i-am-tom/f7b6059ee739764b56e702a612090d27 to your computer and use it in GitHub Desktop.
Save i-am-tom/f7b6059ee739764b56e702a612090d27 to your computer and use it in GitHub Desktop.
The example code from the `Chain` post!
const Option = require('fantasy-options')
const Either = require('fantasy-eithers')
const Task = require('data.task')
const { tagged } = require('daggy')
const { Some, None } = Option
const { Left, Right } = Either
const Pair = tagged('_1', '_2')
//+ prop :: String -> StrMap a -> a
const prop = k => xs =>
k in xs ? Some(xs[k])
: None
const data = { a: { b: { c: 2 } } }
const map = f => xs => xs.map(f)
console.log(
// How do we get to the 2?
prop('a')(data) // Some({ b: { c: 2 } })
.map(prop('b')) // Some(Some({ c: 2 }))
.map(map(prop('c'))) // Some(Some(Some(2)))
)
console.log(
// And if we fail?
prop('a')(data) // Some({ b: { c: 2 }})
.map(prop('badger')) // Some(None)
.map(map(prop('c'))) // Some(None)
)
///////////////////////////
//+ join :: Option (Option a) ~> Option a
Option.prototype.join = function () {
return this.cata({
Some: x => x,
None: () => None
})
}
console.log(
prop('a')(data) // Just({ b: { c: 2 } })
.map(prop('b')).join() // Just({ c: 2 })
.map(prop('c')).join() // Just(2)
)
console.log(
prop('a')(data) // Just({ b: { c: 2 } })
.map(prop('badger')).join() // Nothing
.map(prop('c')).join() // Nothing
)
///////////////////////////
//+ chain :: Option a ~> (a -> Option b)
//+ -> Option b
Option.prototype.chain = function (f) {
return this.cata({
Some: f,
None: () => this // Do nothing
})
}
// Just like `sequence` is `traverse` with
// `id`, `join` is `chain` with `id`!
//+ join :: Chain m => m (m a) ~> m a
const join = xs => xs.chain(x => x)
console.log(
// Our example one more time...
prop('a')(data) // Just({ b: { c: 2 } })
.chain(prop('b')) // Just({ c: 2 })
.chain(prop('c')) // Just(2)
)
///////////////////////////
//+ chain :: Either e a
//+ ~> (a -> Either e b)
//+ -> Either e b
Either.prototype.chain = function (f) {
return this.cata({
Right: f,
Left: _ => this // Do nothing
})
}
const sqrt = x => x < 0
? Left('Hey, no!')
: Right(Math.sqrt(x))
console.log(
Right(16)
.chain(sqrt) // Right(4)
.chain(sqrt) // Right(2)
)
console.log(
Right(81)
.chain(sqrt) // Right(9)
.map(x => -x) // Right(-9) 😮
.chain(sqrt) // Left('Hey, no!')
.map(x => -x) // Left('Hey, no!')
)
console.log(
Left('eep')
.chain(sqrt) // Left('eep')
)
///////////////////////////
//+ chain :: Array a
//+ ~> (a -> Array b)
//+ -> Array b
Array.prototype.chain = function (f) {
// Map, then concat the results.
return [].concat(... this.map(f))
}
// NB: **totally** made up.
const flights = {
ATL: ['LAX', 'DFW'],
ORD: ['DEN'],
LAX: ['JFK', 'ATL'],
DEN: ['ATL', 'ORD', 'DFW'],
JFK: ['LAX', 'DEN']
}
//- Where can I go from airport X?
//+ whereNext :: String -> [String]
const whereNext = x => flights[x] || []
console.log(
// JFK, ATL
whereNext('LAX')
// LAX, DEN, LAX, DFW
.chain(whereNext)
// JFK, ATL, ATL, ORD, DFW, JFK, ATL
.chain(whereNext)
)
///////////////////////////
//- An ugly implementation for range.
//+ range :: (Int, Int) -> [Int]
const range = (from, to) =>
[... Array(to - from)]
.map((_, i) => i + from)
//- The example from that link in JS.
//+ factors :: Int -> [Pair Int Int]
const factors = n =>
range(1, n).chain(a =>
range(1, a).chain(b =>
a * b !== n
? []
: [ Pair(a, b) ]))
// (1, 20), (2, 10), (4, 5),
// (5, 4), (10, 2), (20, 1)
console.log(factors(20))
///////////////////////////
// A "sequential" async type.
const Promise = require('fantasy-promises')
//- Convert a Task to a Promise
//+ taskToPromise :: Task e a
//+ -> Promise (Either e a)
const taskToPromise = task => new Promise(
res => task.fork(e => res(Left(e)),
x => res(Right(x))))
//+ promiseToTask :: Promise (Either e a)
//+ -> Task e a
const promiseToTask = promise =>
new Task((rej, res) =>
promise.fork(either =>
either.cata({
Left: rej,
Right: res
})
)
)
//- Finally...
//+ andThen :: Task e a ~> (a -> Task e b)
//+ -> Task e b
Task.prototype.andThen = function (f) {
return promiseToTask(
taskToPromise(this)
.chain(either => either.cata({
// We "lift" failure using Promise'
// Applicative instance.
Left: _ => Promise.of(either),
Right: x => taskToPromise(f(x))
}))
)
}
// Some "stubs" to mock AJAX.
const getUser = email => Task.of(12)
const getFriends = id => Task.of([id + 1, id + 2])
//- ... which gives us:
getUser(12).andThen(getFriends)
.fork(console.error.bind(console),
console.log.bind(console))
///////////////////////////
//+ No intermediate type!
Task.prototype.andThen = function (f) {
return new Task((rej, res) =>
this.fork(rej, x =>
f(x).fork(rej, res))
)
}
//- ... which gives us:
getUser(12).andThen(getFriends)
.fork(console.error.bind(console),
console.log.bind(console))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment