_Platform_gatherEffects can throw runtime exception - Maximum call stack size exceeded
rupertlssmith opened this issue · 3 comments
Elm 0.19.1
elm.js:2166 Uncaught RangeError: Maximum call stack size exceeded
at _Platform_gatherEffects (elm.js:2166)
at _Platform_gatherEffects (elm.js:2179)
...
This function is not tail recursive when working with a nested Cmd.batch. Large flat batches are ok, since a for loop is used to iterate those.
https://github.com/elm/core/blob/master/src/Elm/Kernel/Platform.js#L273
Workarounds:
- Don't create large nested Cmd.batch.
- OR Users can write their own custom version of
Cmd.batchthat keeps the batch flat, and gets transformed into aCmd.batchlater one, once the whole batch has been built.
===
It is conventient to use an update monad like this:
andThen :
(model -> ( model, Cmd msg ))
-> ( model, Cmd msg )
-> ( model, Cmd msg )
andThen fn ( model, cmd ) =
let
( nextModel, nextCmd ) =
fn model
in
( nextModel, Cmd.batch [ cmd, nextCmd ] )
When operating over a large number of update functions, which are written in this style:
someUpdate : ... -> Model -> ( Model, Cmd Msg )
It can be convenient to create a larger update by folding over a list of smaller ones, like this:
List.foldl
(\... accum ->
andThen
(someUpdate ...)
accum
)
( model, Cmd.none )
Even if the smaller someUpdate functions return Cmd.none, a large nested Cmd.batch will be created.
This can be fixed by avoiding using this style of programming, but it seems a shame to not allow it.
Solutions:
- Make _Platform_gatherEffects tail recursive.
- OR Make
Cmd.batcha bit smarter, by having it flatten any nested batching.
Thanks for reporting this! To set expectations:
- Issues are reviewed in batches, so it can take some time to get a response.
- Ask questions a community forum. You will get an answer quicker that way!
- If you experience something similar, open a new issue. We like duplicates.
Finally, please be patient with the core team. They are trying their best with limited resources.
How deep of a nesting are you talking about? I thought current browsers typically allow 10,000-30,000 levels of stack calls. Do you have thousands of updates you're composing, or is this happening with just tens or hundreds of nesting levels?
Also note that:
andThen :
(model -> ( model, Cmd msg ))
-> ( model, Cmd msg )
-> ( model, Cmd msg )is not a describing monad. As you can see it's (a -> X a) -> X a -> X a while point of nomadic bind (andThen) is to go from X a to X b. I would suggest not to go crazy about similar abstractions especially those which involve recursion in update.