Skip to content

Instantly share code, notes, and snippets.

@Fyzu
Last active July 7, 2017 21:32
Show Gist options
  • Save Fyzu/36bd34b6e59689a01145c6e418beb7ee to your computer and use it in GitHub Desktop.
Save Fyzu/36bd34b6e59689a01145c6e418beb7ee to your computer and use it in GitHub Desktop.
Noladius FP
const R = require('ramda')
const { isPromise } = require('./utils/promise')
class Task {
constructor(computation, cleanup) {
this.fork = computation
this.cleanup = cleanup
}
map(f) {
const computation = (reject, resolve) => this.fork(
reject,
([payload1, actions1]) => {
resolve([f(payload1), actions1])
}
)
return new Task(computation, this.cleanup)
}
chain(f) {
const computation = (reject, resolve) => this.fork(
reject,
([payload1, actions1]) => {
f(payload1).fork(
reject,
([payload2, actions2]) => {
resolve([payload2, actions1.concat(actions2)])
}
)
}
)
return new Task(computation, this.cleanup)
}
ap(b) {
return this.chain(a => b.map(a))
}
concat(that) {
const cleanupBoth = () => {
if (this.cleanup) {
this.cleanup()
}
if (that.cleanup) {
that.cleanup()
}
}
const computation = (reject, resolve) => {
let resolved = false
let resolvedResult
let rejected = false
let rejectedActions = []
const rejectWrapper = actions => {
if (rejected) {
reject(rejectedActions.concat(actions))
} else if (resolved) {
reject(actions)
} else {
rejectedActions = actions
rejected = true
}
}
const resultMapper = ([payload1, actions1], [payload2, actions2]) => ([
[payload1, payload2], actions1.concat(actions2),
])
const createResolveWrapper = mapper => result => {
if (rejected) {
reject(rejectedActions)
} else if (resolved) {
resolve(mapper(result, resolvedResult))
} else {
resolved = true
resolvedResult = result
}
}
this.fork(
rejectWrapper,
createResolveWrapper(resultMapper)
)
that.fork(
rejectWrapper,
createResolveWrapper(R.flip(resultMapper))
)
}
return new Task(computation, cleanupBoth)
}
}
Task.result = (payload, actions = []) => ([payload, actions])
Task.actions = act => ([null, Array.isArray(act) ? act : [act]])
Task.of = (value, cleanup) => {
let computation = value
if (isPromise(value)) {
computation = (reject, resolve) => {
value
.then(R.compose(resolve, Task.result))
.catch(e => reject(e))
}
} else if (typeof value !== 'function') {
computation = (reject, resolve) => {
resolve(value)
}
}
return new Task(computation, cleanup)
}
Task.resolve = R.compose(
result => new Task((reject, resolve) => {
resolve(result)
}),
Task.result,
)
Task.reject = actions => new Task(reject => {
reject(actions)
})
module.exports = Task
const Task = require('../Task')
const R = require('ramda')
const trace = R.curry((tag, x) => {
console.log(tag, x)
return x
})
const getDataFromServer = url => Promise.resolve({
fromUrl: url,
data: {
items: [
{
name: 'Test 1',
val: 10000,
},
],
},
})
const createSetterActions = prop => data => Task.actions(() => ({ [prop]: data }))
const handleResponse = R.compose(Task.of, createSetterActions('plugins'), R.prop('items'), R.prop('data'))
const run = R.compose(R.chain(handleResponse), Task.of, getDataFromServer, R.prop('url'))
const state = {
url: 'Test',
}
const chainActionsOverState = R.reduce((currentState, action) => Object.assign(currentState, action(currentState)))
run(state).fork(
a => {
console.error('Reject', a)
},
([payload, actions]) => {
console.log('Resolve', payload)
const newState = chainActionsOverState(state, actions)
console.log('state', newState)
}
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment