Skip to content

Commit

Permalink
Allow fn passed to Async.fromPromise to be partially applied (#475)
Browse files Browse the repository at this point in the history
* Allow fn passed to Async.fromPromise to be partially applied

* Update docs to reflect changes to Async.fromPromise currying its input

* Update contributors
  • Loading branch information
JamieDixon authored May 22, 2020
1 parent ac80421 commit 4e0296e
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 12 deletions.
9 changes: 9 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,15 @@
"contributions": [
"doc"
]
},
{
"login": "JamieDixon",
"name": "Jamie Dixon",
"avatar_url": "https://avatars2.githubusercontent.com/u/1519443?v=4",
"profile": "https://twitter.com/jamiedixon",
"contributions": [
"code"
]
}
],
"repoType": "github",
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,10 @@ Thanks goes to these wonderful people ([emoji key][emojis]):
<td align="center"><a href="https://github.com/RichardForrester"><img src="https://avatars0.githubusercontent.com/u/12902182?v=4" width="100px;" alt=""/><br /><sub><b>RichardForrester</b></sub></a><br /><a href="https://github.com/evilsoft/crocks/commits?author=RichardForrester" title="Code">💻</a> <a href="https://github.com/evilsoft/crocks/commits?author=RichardForrester" title="Documentation">📖</a> <a href="https://github.com/evilsoft/crocks/commits?author=RichardForrester" title="Tests">⚠️</a></td>
<td align="center"><a href="http://furkantunali.com"><img src="https://avatars2.githubusercontent.com/u/68406?v=4" width="100px;" alt=""/><br /><sub><b>Furkan Tunalı</b></sub></a><br /><a href="https://github.com/evilsoft/crocks/commits?author=JacopKane" title="Documentation">📖</a> <a href="#example-JacopKane" title="Examples">💡</a></td>
<td align="center"><a href="https://github.com/sunwukonga"><img src="https://avatars3.githubusercontent.com/u/12194690?v=4" width="100px;" alt=""/><br /><sub><b>Paul Desmond Parker</b></sub></a><br /><a href="https://github.com/evilsoft/crocks/commits?author=sunwukonga" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/area73"><img src="https://avatars3.githubusercontent.com/u/2030605?v=4" width="100px;" alt=""/><br /><sub><b>Rodrigo Erades</b></sub></a><br /><a href="https://github.com/evilsoft/crocks/commits?author=area73" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="http://jamie-dixon.co.uk"><img src="https://avatars2.githubusercontent.com/u/1519443?v=4" width="100px;" alt=""/><br /><sub><b>Jamie Dixon</b></sub></a><br /><a href="https://github.com/evilsoft/crocks/commits?author=JamieDixon" title="Code">💻</a></td>
</tr>
</table>

Expand Down
19 changes: 9 additions & 10 deletions docs/src/pages/docs/crocks/Async.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,12 +296,12 @@ n.fork(log('rej'), log('res'))
Async.fromPromise :: (* -> Promise a e) -> (* -> Async e a)
```

Used to turn an "eager" `Promise` returning function, into a function that takes
the same arguments but returns a "lazy" `Async` instance instead. Due to the
lazy nature of `Async`, any curried interface will not be respected on the
provided function. This can be solved by wrapping the resulting function
with [`nAry`][nary], which will provide a curried function that will return the
desired `Async`.
Used to turn an "eager" `Promise` returning function into a function that takes
the same arguments, but returns a "lazy" `Async` instance instead.

The `Promise` returning function given to `fromPromise` is automatically curried,
allowing you to partially apply the resulting function to its parameters until
an `Async` type is returned.

<!-- eslint-disable no-console -->
<!-- eslint-disable no-sequences -->
Expand All @@ -311,7 +311,6 @@ import Async from 'crocks/Async'

import ifElse from 'crocks/logic/ifElse'
import isNumber from 'crocks/predicates/isNumber'
import nAry from 'crocks/helpers/nAry'

const { fromPromise } = Async

Expand All @@ -334,7 +333,7 @@ safeProm(isNumber, '34')

// safeAsync :: (a -> Boolean) -> a -> Async a a
const safeAsync =
nAry(2, fromPromise(safeProm))
fromPromise(safeProm)

// numAsync :: a -> Async a Number
const numAsync =
Expand Down Expand Up @@ -380,8 +379,8 @@ As such, the need for binding may arise. `fromNode` provides a second, optional
argument that takes the context that will be used to bind the function being
wrapped.

Like [`fromPromise`](#frompromise), any curried interface will not be respected.
If a curried interface is needed then [`nAry`][nary] can be used.
Any curried interface will not be respected and if a curried interface is needed
then [`nAry`][nary] can be used.

<!-- eslint-disable no-console -->
<!-- eslint-disable no-sequences -->
Expand Down
19 changes: 18 additions & 1 deletion src/Async/Async.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ test('Async fromPromise', t => {
t.throws(fork(true), err, 'throws when true is returned from promise function')
t.throws(fork([]), err, 'throws when an array is returned from promise function')
t.throws(fork({}), err, 'throws when an object is returned from promise function')
t.throws(fork(unit), err, 'throws when an object is returned from promise function')

t.end()
})
Expand All @@ -170,6 +169,24 @@ test('Async fromPromise resolution', t => {
Async.fromPromise(resProm)(val).fork(rej(val), res(val))
})

test('Async fromPromise resolution with partially applied function', t => {
t.plan(2)

const val1 = 1
const val2 = 2

const val = val1 + val2

const rejProm = x => y => new Promise((_, rej) => rej(x + y))
const resProm = x => y => new Promise((res) => res(x + y))

const rej = y => x => t.equal(x, y, 'rejects a rejected Promise')
const res = y => x => t.equal(x, y, 'resolves a resolved Promise')

Async.fromPromise(rejProm)(val1)(val2).fork(rej(val), res(val))
Async.fromPromise(resProm)(val1)(val2).fork(rej(val), res(val))
})

test('Async fromNode', t => {
const resCPS = (x, cf) => cf(null, x)

Expand Down
10 changes: 9 additions & 1 deletion src/Async/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const fl = require('../core/flNames')

const array = require('../core/array')
const compose = require('../core/compose')
const curry = require('../core/curry')
const once = require('../core/once')
const unit = require('../core/_unit')

Expand Down Expand Up @@ -62,11 +63,18 @@ function fromPromise(fn) {
throw new TypeError('Async.fromPromise: Promise returning function required')
}

const _fn = curry(fn)

return function() {
const promiseArgs = arguments

const promise = _fn.apply(null, promiseArgs)

if (isFunction(promise)) {
return fromPromise(promise)
}

return Async(function(reject, resolve) {
const promise = fn.apply(null, promiseArgs)

if(!isPromise(promise)) {
throw new TypeError('Async.fromPromise: Promise returning function required')
Expand Down

0 comments on commit 4e0296e

Please sign in to comment.