Discussion: The limits of actions and reducers

My bad, I presumed so due to word reducer and actual mention of Redux.

If the thing you dispatch is not a single action, but an array or other sequence structure of actions, which then all get applied to create a new state, that solves some of these problems. There’s some precedent5 for this in Redux-land.

So anywhere where you say something is too Redux-specific, I’m not sure what that means.

I guess only case it is really applicable was here:

We need some way for action routing code to add more actions or more effects without doing dangerous things like extendTransformAction—extending a previously created action, which represents a coherent state update, is wrong. But what to do instead isn’t clear to me yet.

I assumed that extendTransformAction was Redux specific, but maybe it’s a ProseMirror specific. It is just that requirement is really tide to some specific routing mechanism & not knowing it makes it hard to tell if proposed solution addresses it or not.

One aspect of your approach appears to be that only host messages are allowed to change the host state. But with plugins like the undo history or collaborative editing, plugins must be able to send plugin-specific messages that both have a custom effect on the plugin’s state, and update the host state.

Yes idea is that plugins manage their own state & if they need to also update host state they accomplish that by sending a host a message (that presumably host understands).

You mention sending a host message, but I’m A) not sure how that would work (an update function can’t send a message)

I tried my best to show that with an actual code, although I guess it may have assumed certain level of familiarity with Elm where update returns [Model, Cmd<Msg>] pair where Cmd<Msg> is an effect that will send Msg back into the update loop. PluginManager.js shows intimate details of how this could be utilized to allow plugins to send messages to the “host”.

and B) very worried about the lack of atomicity that’d cause (which you also point out).

I am not sure what do you mean. Sorry.

For what its worth, I think approach you settled on is in fact not that different from Elm’s or what I was trying to outline. From what I understand Transaction is closer to what “message” types are in Elm than actions were although it’s one generic curated type vs union of many. Main difference from what I understand is that instead of sending a followup messages from plugins to host, each plugin gets a chance to alter or cancel transaction before it is applied. Which is a neat way to batch things up without running into possibly recursive update cycle.

API aside only conceptual difference is that in my proposed approach plugin did not get to see effects submitted by plugins that run prior to it before host message was handled. Instead each plugin could submit a followup effect via message to a host that would get handled through another update cycle.

In chosen approach plugin gets to see effects submitted by plugins that run prior to it & can contribute or reject along with a host effect (or rather transform).

I think chosen approach is really neat! It does introduce certain unpredictability due to order of plugin execution though. Unlike in solution proposed plugin A may never have a chance to reject change submitted by plugin B as later will run later and subsequently get applied to the document. It is hard for me to say if this is good or bad in practice though as plugins counteracting each other seems like a problematic setup anyhow.

P.S. I’d love to see a post about the chosen approach as both Elm & Redux communities could learn from it.