Skip to content

StackOverflow with Fable but not with fsc in release #2392

Closed

Description

@TysonMN

Description

Consider PR hedgehogqa/fsharp-hedgehog#314. Our tests pass when using the standard F# complier (fsc) when the configuration is Release, which means the compiler will perform tail call elimination/optimization. The same test fails when compiled with Fable and executed via npm.

Repro code

Here is a link to one of the specific tests that fails for Fable+npm but passes with fsc.

Expected result

All tests, including that one, pass. For example, see these logs from our GitHub Action and executed those tests compiled with the fsc.

Actual results

Some tests, including that one, fail due to a StackOverflow. See these logs from our GitHub Action that executed that test compiled with Fable using npm.

Related information

  • Fable version
    • 3.1.1
  • Operating system
    • Microsoft Windows Server 2019
    • 10.0.17763
    • Datacenter
  • Node
    • 14.15.4
    • x64

Activity

  1. alfonsogarciacaro commented on Feb 22, 2021

    @alfonsogarciacaro
    Member

    Hi @TysonMN, thanks for the report! It's great you're working to support Fable with Hedgehog, I'd love to see this library in Fable projects. It'd be great if you could isolate a case that fails only with Fable, but please note that unfortunately tail-call optimization is not as advanced as in .NET so it may be not possible to fix those cases. First, as far as I know, JS engines haven't implemented tail-call optimization natively yet, so we're restricted to the cases that can be converted to a while loop at compile time.

    Also, we don't have support for tail-call optimization with mutually recursive functions atm, sometimes you can resolve this by inlining. At some point, Tomas Petricek had a draft to support this so it may be possible, but I'm not sure how much work would entail.

  2. ncave commented on Feb 22, 2021

    @ncave
    Collaborator

    As @alfonsogarciacaro said, most JS engines unfortunately have not implemented TCO yet.
    There are some JS workarounds that vary in performance, but Fable is not using any of them:

    • Accumulate function parameters in a wrapper.
    • Explicit tail calls: fext.js (can also handle mutual recursion).
  3. TysonMN commented on Feb 22, 2021

    @TysonMN
    Author

    Thanks for the information and links.

    Yes, my understanding was that one should not typically expect TCO from Fable or the downstream JS compilers.

    What about Fable's trampoline? Would you recommend that we use a similar trampoline in our code to avoid overflowing the stack?

    That seems like a great idea and abstraction. We might be able to make use of it in F#-Hedgehog even for some cases that are overflowing when using the fsc.

    [...] we're restricted to the cases that can be converted to a while loop at compile time.

    Are you saying that Fable is typically able to compile a recursive function into a loop (so support by downstream JS compilers is not needed)?

  4. ncave commented on Feb 22, 2021

    @ncave
    Collaborator

    @TysonMN Trampolines may work but have some performance cost, which may or may not be acceptable to you.

    Yes, Fable is able to optimize (rewrite into a loop) some self-referencing tail-recursive functions.
    But not in the more general case of mutually recursive functions, or continuation passing style.

  5. MangelMaxime commented on Jul 2, 2026

    @MangelMaxime
    Member

    Looking at the latest CI (on master) result from hedgehog it seems like Fable tests are running the test linked at:

    Here is a link to one of the specific tests that fails for Fable+npm but passes with fsc.

    Please feel free to re-open if the issue is not fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions