fluture-js / fluture Goto Github PK
View Code? Open in Web Editor NEW๐ฆ Fantasy Land compliant (monadic) alternative to Promises
License: MIT License
๐ฆ Fantasy Land compliant (monadic) alternative to Promises
License: MIT License
NodeJS and a lot of libraries/frameworks use promises.
Hence, I often find myself chaining promises like this:
export const doSomething = () => {
return Future.of(1)
.chain(Future.fromPromise(() => /* a promise */ ))
.chain(Future.fromPromise(() => /* a promise */ ))
.chain(Future.fromPromise(() => /* a promise */ ))
}
I think if we have a .chainPromise
function that allows user to write the above as:
export const doSomething = () => {
return Future.of(1)
.chainPromise(() => /* a promise */ )
.chainPromise(() => /* a promise */ )
.chainPromise(() => /* a promise */ )
}
It is pure sugar syntax but I think it will creates a lot of developer happiness.
Please let me know if this is something Fluture library should adopt and I can contribute the code for it.
See #17
Future
with Future.Guarded
Future.Guarded
Doing this would make Futures stateful by default. It seems like a very useful thing, but the statefulness doesn't sit well with me. Maybe I'm overlooking some consequences.
This question illustrates confusion about the fromPromise
function which I think was, in part, caused by the naming of the function.
From Promise suggests that the function takes in a Promise and returns a Future. In fact it encases a function which returns a Promise to return a Future instead, very similar to the encase
family of functions.
To make the similarities between the two families of functions more apparent, I'm thinking to rename fromPromise
to encaseP
. This would also naturally provide a name for encaseP(f, undefined)
: tryP(f)
.
Future.of(1).both(Future.of(2));
//> Future.of([1, 2])
Future.of(1).both(Future.reject('kaputt'))
//> Future.reject('kaputt')
Future.reject('kaputt').both(Future.of(1))
//> Future.reject('kaputt')
I propose flipping the argument order of .ap()
back to how it was pre-Fantasy Land 1.0, but we keep the argument order of ['fantasy-land/ap']()
compliant with Fantasy Land 1.0 and later. That way we tackle two problems:
.ap
is awkward to use in fluent method chains.Fluture#ap
. Fluture will become compliant to all versions of Fantasy Land with this change.The counter-argument is that this may be confusing for two reasons:
Fluture#ap
will behave differently from other libraries' ap
method. This may be a source of confusion, but it should also alleviate confusion depending on the users' expectation.Fluture#ap
will behave differently from Fluture#fatansy-land/ap
. I'm not sure if this is confusing, since nobody uses the latter directly.sry bad joke
All cards in Project 1 should find their way to Done.
Hi, I faced a very interesting issue when Fluture is ran in Jest tests:
// Jest test
// works
it('error is caught by Jest without another chain', () => {
return new Promise((resolve, reject) => {
return Future.of(1)
.chain(() => { throw new Error('error') })
.fork(reject, resolve)
})
})
// does not work
it('error is not caught by Jest but instead timeouts', () => {
return new Promise((resolve, reject) => {
return Future.of(1)
.chain(Future.fromPromise(Promise.resolve))
.chain(() => { throw new Error('error') })
.fork(reject, resolve)
})
})
// works
it('error is caught by Jest when encapsulated in Future.try', () => {
return new Promise((resolve, reject) => {
return Future.of(1)
.chain(Future.fromPromise(Promise.resolve))
.chain(() => Future.try(() => { throw new Error('error') }))
.fork(reject, resolve)
})
})
In the second test case, the exception thrown in the Future is not bubbled up into the promise.
Hence the promise is not rejected and the test case timed out since nothing rejects or resolves it.
It seems like the Future has swallowed the exception!
Jest needs to catch the exceptions otherwise expect
cannot work.
This issue is causing me to wrap all my expect
statements in Future.try or else my test cases will not work.
I would like to know how Fluture re-throws the exception so that I can resolve this.
Any help to point out where in the source code for me to look into for possible solution is greatly appreciated too.
Since Future.finally
is a simplified version of Future.hook
(one where the cleanup
does not depend on the resource), it would be better if they both behaved the same way under cancellation.
I propose that the finally
-computation is started and cancelled immediately when the computation is cancelled, similar to how hook
behaves.
I sense that the docs for encase are wrong, but I wasn't able to write clearer docs. The problem is, that encase returns a function, not a Future. But I have hard times to put into writing what the new function does.
Eg fantasy-land/map
, as described in the spec.
This looks similar to this issue in Futurize: Return void from computation
I'm getting this when I try to run the fourInstableFutures
example from the Readme.md.
Here's the original code that throws the error (copied and pasted from Readme.md):
const fourInstableFutures = Array.from(Array(4).keys()).map(
i => Future(
(rej, res) => setTimeout(
() => Math.random() > 0.8 ? rej('failed') : res(i),
20
)
)
);
const stabalizedFutures = fourInstableFutures.map(Future.fold(S.Left, S.Right))
Future.parallel(2, stabalizedFutures).fork(console.error, console.log);
Putting void
in front of setTimeout
makes the error go away:
const fourInstableFutures = Array.from(Array(4).keys()).map(
i => Future(
(rej, res) => void setTimeout(
() => Math.random() > 0.8 ? rej('failed') : res(i),
20
)
)
);
const stabalizedFutures = fourInstableFutures.map(Future.fold(S.Left, S.Right))
Future.parallel(2, stabalizedFutures).fork(console.error, console.log);
According to the how to add Fantasy Land compliance section of their README, FantasyLand is meant to be a Peer Dependency. Currently FantasyLand is incorrectly included as a hard dependency.
The new ES6 code is not completely covered by the old unit tests. In attaining 100% coverage, we should be sure to test for the following:
never
, especially with regards to race
Rejected
, Resolved
and Never
.I'm trying to manually thread some state through chained futures:
F.of({ state })
.chain(({ state }) => F.of({ state, ...someFuture(state).value(x => x) }))
.chain(({ state }) => F.of({ state, ...someOtherFuture(state).value(x => x) }))
.fork(console.log, console.log);
My thought is that with this approach and someFuture() and someOtherFuture returning { state: "new"}
this would essentially replace the state as if a put
was ran in a State monad.
The problem here is that clearly nested Futures are not supposed to work like this as I figure F.of() evaluates its argument strictly which returns the actual function object rather than the result of .value()
.
Do you have thoughts on how to accomplish this kind of explicit state threading?
Future.of(1).and(Future.of(2));
//> Future.of(2)
Future.of(1).and(Future.reject('kaputt'))
//> Future.reject('kaputt')
Future.reject('kaputt').and(Future.of(1))
//> Future.reject('kaputt')
FL.bimap
as a function name.Now that race
has a special case for Never
instances, we can implement lawful Alternative using race
and never
. This would mean that conversion to the ConcurrentFuture
would only be necessary to get parallel ap
behavior.
I might be totally missing something, but I'm having trouble wrapping fs.read with Future.node. I think the reason is that fs.read expects a callback with three arguments: err, bytesRead, and the data buffer. When I console.log ...args in the chained function called, it only shows bytesRead in the array of arguments.
Am I way off? If not, is there anyway around this?
Been into RxJS the last few days and Observables seem so similar to flutures, any idea if there is a possibility to explain the difference between them?
Improve error messages generated when expecting a fluture/Future@x
and getting one of the wrong version.
But keep the signature of the dispatcher the same.
See #59
sanctuary-type-identifiers
and use its function in the definition of isFuture
@@type
property to the Future constructorHi there,
Just came accross Fluture and giving it a try to try and bring some functional tricks inside an existing node framework (seneca).
In the way that there is .node()
abiding by the NodeJS convention of (err, results)
maybe there should be a version of .fork()
which allows to consume the future by executing a node callback(err, results)
?
Maybe a .done()
?
Cheers!
Jun
I think the changes to FantasyLand do not affect Fluture. Therefore I believe Fluture already support 3.0.
To fix this issue, the README should be updated to indicate support for FL 3.0. I think it may be better to indicate that Fluture supports FantasyLand 1.0, 2.0 and 3.0.
The problem occurs in Future$fork
and Future$chain
/Future$chainRej
. Both functions export or import the clear
(resource disposal) function into another scope, causing references to be kept until clear
is garbage-collected. This is killing for long running processes.
I think it would be nice if you could opt-out of runtime type checking and create a dev and prod version of this type. Similar to that in sanctuary-js
Once the spec is final.
Also might want to rename chainRej
to lchain
for naming consistency.
I thought I'd open an issue to track progress on this and maybe bring in some fantasy-land veterans to advise. As pointed out by @Avaq in #82
Most do implementations fail when a Future is forked twice, because of stateful generators. I thought of a do which fixes that, but it has other problems associated with Functors of multiple values (like Array). :(
Hi there,
I would find it useful to be able to call node style callback functions with one or several arguments.
For instance in the current example:
Future.node(done => fs.readFile('package.json', 'utf8', done))
Is a future with no arguments, but it would be useful to be able to wrap fs.readFile into a future of one or two arguments.
Cheers,
Jun
sanctuary-type-classes
The second argument to BinaryType should be a url since 0.8.0 (?) of sanctuary-def, presumably 'https://github.com/Avaq/Fluture#future'
predicate :: (a -> Boolean) -> a -> Future a a
As far as I can see, we are already compliant. Just need to write some tests to make sure.
IMO, TypeScript may be useful for a library like that.
These functions are missing fast failure due to laziness. This question made me realize fast failure is a much desired feature.
Object.create
, Object.assign
, Array.isArray
; orIt will be nice to expose non monadic version of Future for which ap
would be parallel.
This version could also implement Alternative where alt
would be race
The computation can be run sync but reject or resolve execute async to make it more predictable. Thoughts?
I think that the thread
higher order function described in #82
const thread = (left, right, future) => function*(){ return S.either(left, right, yield F.fold(S.Left, S.Right, future))}
F.of( {state} )
.chain(({ state }) => F.do(thread(left => `Oh no! ${left}`, some => ({ state, some }), someFuture(state))))
.chain(({ state, some }) => F.do(thread(left => `Oh no! ${left}`, other => ({ state, some, other }), someOtherFuture(some))))
.fork(console.log, console.log)
defines some sort of mini monad transformer which allows to thread an Either Monad with the Fluture monad. And I guess this makes the code above some kind of Keisli composition for 2 monads? My theory doesn't hold up yet there...
I'm thinking that there's probably a well known interface/type class for this, maybe requiring monad transformers and lifting (as discussed in mattbierner/akh#31) ?
It could be a nice ad-hoc short term addition to have a .thread()
operation in Fluture, but there's probably a more composable way?
Fail as soon as the invalid argument is provided to the curried function.
very many bugs
EDIT: Bad practical joke
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.