New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Async performAction #55
Conversation
Hey - your last comment in the other ticket was that you weren't sure if this would break react interoperability. Have you already verified that this is not the case? Because I would love to see nested async supported. |
Yes, as far as I can see it doesn't break anything - I already converted a sizeable chunk of a |
I'll take it for a spin this weekend and let you know if I run into any unforced errors. |
I started implementing The |
Oh, I wasn't aware of that equivalence! But I have to admit it's still not clear to me how to turn
The implementation of |
Also I often find myself wishing for the following pattern:
This is a very real use case - for example clearing an array before calling an async function to append some items to said array. Right now, the second function would read the current (and not the transformed) state, thus restoring the cleared items (or else resort to explicitly keeping track of the transformed state and passing that to the next function etc etc) What would you say about a free monad that supports |
Doesn't |
I'm sorry if I'm thick and missing the obvious, but could you tell me how? I fail to see how to
|
Ah, ok, I see now. Perhaps React could be described by a coroutine with suspension functor given by data IndexedStore i o a = IndexedStore o (i -> a)
type ReactState state = Co (IndexedStore state state)
emitGiven :: (state -> m state) -> ReactState state m Unit which zips with a |
Cool, I'm gonna try to come up with something too. |
@pkamenarsky I put this together, let me know what you think. I think it's a better fit, and I'll put together a PR to use |
Just checking if I got this right: React, acting as a In case I understood correctly, the problem with intermediary states still remains:
Now there would be no way to pass Maybe if |
Yeah, I tried I think we'll need a custom coroutine type after all. |
How about this? It's just a draft though, needs to be cleaned up etc. |
This looks really great, but I wonder if we should put it in Two minor comments:
|
Regarding The fundamental problem with putting this in
to
(The reverse conversion is trivial). Unfortunately there would be no point to introducing Can you come up with something? Then we can just patch What was the biggest difficulty for beginners with the old |
Have you seen the I could be convinced to add this to Thermite itself, if we can make things simple for beginners. From what I remember, it wasn't any one particular thing which made it difficult, it just wasn't clear how to implement certain things. |
I'd like to get this merged, ideally before the next release, which will be to update the core libraries dependencies to 1.0. Would you be interested in finishing this off? There isn't a massive rush, since other dependencies still need to be updated. If so, I'll do a more thorough review of this during this week. After some more thought, I'm less concerned if the coroutine functionality is implemented here as opposed to inside Thanks! |
Sure, tell me what is needed still. I'm using it in production, so it works. Documentation, names, API? |
import Control.Monad.Free.Trans | ||
import Control.Monad.Rec.Class | ||
|
||
import Debug.Trace |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be removed?
|
||
import Debug.Trace | ||
|
||
data Query s a = Get (s -> a) | Modify (s -> s) a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add documentation for any exported members.
What is your experience so far with this choice of coroutine type? I'd like to think about it some more, but I wonder if there are any alternatives. Without having tried it, it seems to make sense. I'd still like to merge this before I cut 1.0. Thanks for working on this! |
It works well in my experience. It's like the What would alternative solutions look like in the presence of async side-effects? I'm not able to think of something simpler short of some sort of FRP, but then I'll work on the issues you raised over the weekend and next week. |
@pkamenarsky I've pushed some changes to master to make things compatible with 0.9.1, so this PR will need to be updated, but I'm going to spend some time this afternoon trying out some ideas with |
@paf31 Allright, tell me if you need anything! |
So I spent a bit of time on this, and I'm a little uncomfortable with the need for I'm tempted to say we can add in a Perhaps it's better to figure this out, and make a 2.0 release if we can. Let me think on it a bit more. |
What about something like that to make the types work out:
|
I think it is possible, since another component can modify the state asynchronously. If another component gets there first, that index might not exist in the list any more. We need to pick a strategy for dealing with this problem generally. Some strategies might be:
|
Wait, actually, isn't this a problem with What about providing a keying function to EDIT: alternatively provide a |
The use case for |
I suppose it is. We can document this behavior, and users can add some sort of version field to their state to handle it if they need to. I suggest we could go with the producer approach for now until we figure out the best story for My concern is more over the |
But that's what I mean, isn't A |
So, we can handle the race conditions in documentation, I think. The bigger issue, as I see it, is that the This is why I'm tempted to put the |
If we mention this in the documentation, then what about my earlier suggestion |
That should work. I'll have another look at the It sort of saddens me to lose the simplicity of One thing I'm still not sure about though - if we make the assumption that there will only be one asynchronous request at a time per |
I've merged |
I'm going to try implementing this using |
So a fundamental use case I have goes like this:
There's
purescript-thermite-aff
, but the current design doesn't play well with nesting asyncperformActions
- check out this ticket.So basically, I converted
PerformAction
to be aProducer
. The difference in usage is negligible (i.e. useemit \st -> st {...}
instead of just\st -> st {...}
), but the outcome is much better composability.Would you be interested in going forward with this? I have to decide on a purescript framework for an ongoing project, and so far
thermite
is my top choice, but async composability is a must-have.