Fable: Compilation error with ValueTask (fable 3)

Created on 26 Nov 2020  路  13Comments  路  Source: fable-compiler/Fable

Hello,

I'm an F# and Fable newbie, and I don't have much experience with webpack, so deal with what I say accordingly :)

I'm playing with fable 3 and asyncSeq computation expressions (with FSharp.Control.AsyncSeq 3.0.2).

My code compiles and works in the browser with dotnet fable watch src/ --run webpack-dev-server, but the compilation fails when I do dotnet fable src/ --run webpack.

My understanding is that the first command compiles in some kind of debug mode, while the second is more of a release mode.

I don't understand why it would only fail in release mode ?

Anyway, here is my code:

module App

open Browser.Dom
open FSharp.Control
open Fable.Core

let test () =
    let rec test' start =
        asyncSeq {
            for track in [start..start + 10] do yield track
            if start + 10 < 100 then yield! test' (start + 10)
        }
    test' 0

let myButton = document.querySelector(".my-button") :?> Browser.Types.HTMLButtonElement

myButton.onclick <- fun _ ->
    test()
    |> AsyncSeq.iter (fun x -> console.log x)
    |> Async.StartAsPromise

And here are the compilation error I only get in release mode:

/home/yaurthek/dev/fable3/src/.fable/FSharp.Control.AsyncSeq.3.0.2/AsyncSeq.fs(1757,18): (1757,43) error FABLE: Cannot resolve replacement System.Threading.Tasks.ValueTask.AsTask
/home/yaurthek/dev/fable3/src/.fable/FSharp.Control.AsyncSeq.3.0.2/AsyncSeq.fs(1761,19): (1761,45) error FABLE: Cannot resolve replacement System.Threading.Tasks.ValueTask`1.AsTask
/home/yaurthek/dev/fable3/src/.fable/FSharp.Control.AsyncSeq.3.0.2/AsyncSeq.fs(1783,93): (1783,108) error FABLE: Cannot resolve replacement System.Threading.Tasks.ValueTask`1..ctor

If that makes any difference, I'm compiling in WSL2 and I use Fable 3.0.0-nagareyama-rc-008.

I suspect the problem might not be with Fable itself, but with the Fable compatibility of FSharp.Control.AsyncSeq being broken, but in that case, I don't understand why it would not fail in debug mode too?

If you can spare the time, I would be eager to know about the details of this problem (why the debug/release difference? Where does the problem come from? How would one go about fixing it? What are the important things to know in this regard?)

if I can understand the problem, I might be able to do a PR myself (in Fable or in FSharp.Control.AsyncSeq).

Thanks ! (and thanks for all your work, it's truly magical :) )

All 13 comments

It is a little strange the the functions below this point get compiled (they shouldn't be because of tasks, which are not supported in Fable) despite the fact that they're behind a compiler directive. This is a long shot, but does your fsproj perhaps define NETSTANDARD2_1 or NETCOREAPP3_0?

This is a long shot, but does your fsproj perhaps define NETSTANDARD2_1 or NETCOREAPP3_0?

Yes it does! I set TargetFramework to netstandard2.1 because it's required by FSharp.Control.AsyncSeq 3.0.2. (And I didn't manage to use the asyncSeq computation expressions with the latest version compatible with netstandard2.0)

Is Fable expected to work with targets other than netstandard2.0?

Would fixing the root of the problem be as simple as adding a preprocessor check for Fable at the point you mentioned?

I don't mean target framework, but constants like

<DefineConstants>NETSTANDARD2_1</DefineConstants>

If not, then the only thing that would explain it that I can think of is Fable itself defining those, but I don't see why it would do that in the first place.

Thanks for the detailed report and investigating @mlaily @kerams! Hmm, I'm not sure. It's true that by default Fable defines the DEBUG constant in watch mode (some libraries like Elmish.HMR rely on this). If necessary you can also define it in non-watch mode with:

dotnet fable src --define DEBUG --run webpack

But I'm also puzzled about why this makes a difference. You can use the --verbose option to display the list of arguments passed to the F# compiler and see what constants are being defined in this case (note Fable options must appear before --run):

dotnet fable watch src --verbose --run webpack-dev-server
dotnet fable src --verbose --run webpack

Thanks for your replies both!

This is beginning to drive me crazy.

@kerams NETSTANDARD2_1 is indeed set somewhere (not by me),as we can see in the output when building with --verbose:

F# PROJECT: src/App.fsproj
   --define:NETSTANDARD
   --define:NETSTANDARD2_1
   --warnaserror:3239,76
   --noframework
   --nologo
   --simpleresolution
   --nocopyfsharpcore
   --define:FABLE_COMPILER
   --define:DEBUG
   --optimize-
   --warn:3
   --fullpaths
   --flaterrors
   --target:library
   --langversion:preview
   --targetprofile:netstandard
   --optimize-

@alfonsogarciacaro using the commands you suggested, here is what I found:

The only difference in the flags above between --define DEBUG on the command line or not, is just --define:DEBUG.

dotnet fable src/ --verbose --run webpack
always fails, regardless of what ran before.

dotnet fable src/ --verbose --run webpack
then
dotnet fable src/ --verbose --define DEBUG --run webpack
=> fails
then
dotnet fable src/ --verbose --define DEBUG --run webpack
=> succeeds!

Executing dotnet fable src/ --verbose --define DEBUG --run webpack a second time always succeeds.


There is no notable difference between the output of failed builds. (with or without --define DEBUG)

Failed build:

[...]
F# compilation finished in 3355ms
Compiled src/.fable/FSharp.Control.AsyncSeq.3.0.2/AssemblyInfo.fs
Compiled src/.fable/Fable.Promise.2.0.0/PromiseImpl.fs
Compiled src/App.fs
Compiled src/.fable/Fable.Fetch.2.2.0/Fetch.fs
Compiled src/LastFmApi.fs
Compiled src/.fable/Fable.Promise.2.0.0/Promise.fs
Compiled src/.fable/FSharp.Control.AsyncSeq.3.0.2/AsyncSeq.fs
Fable compilation finished in 525ms
/home/yaurthek/dev/fable3/src/.fable/FSharp.Control.AsyncSeq.3.0.2/AsyncSeq.fs(1757,18): (1757,43) error FABLE: Cannot resolve replacement System.Threading.Tasks.ValueTask.AsTask
/home/yaurthek/dev/fable3/src/.fable/FSharp.Control.AsyncSeq.3.0.2/AsyncSeq.fs(1761,19): (1761,45) error FABLE: Cannot resolve replacement System.Threading.Tasks.ValueTask`1.AsTask
/home/yaurthek/dev/fable3/src/.fable/FSharp.Control.AsyncSeq.3.0.2/AsyncSeq.fs(1783,93): (1783,108) error FABLE: Cannot resolve replacement System.Threading.Tasks.ValueTask`1..ctor
Compilation failed

Successful build:

[...]
F# compilation finished in 3375ms
Skipping Fable compilation of up-to-date JS files
Fable compilation finished in 2ms
.> node_modules/.bin/webpack
Hash: 6ba934ebccd13db6f244
Version: webpack 4.41.6
Time: 329ms
Built at: 11/26/2020 7:56:19 PM
    Asset     Size  Chunks             Chunk Names
bundle.js  591 KiB    main  [emitted]  main
Entrypoint main = bundle.js
[./src/.fable/FSharp.Control.AsyncSeq.3.0.2/AsyncSeq.fs.js] 97.1 KiB {main} [built]
[./src/.fable/fable-library.3.0.0-nagareyama-rc-008/Array.js] 32.9 KiB {main} [built]
[./src/.fable/fable-library.3.0.0-nagareyama-rc-008/Async.js] 4.64 KiB {main} [built]
[./src/.fable/fable-library.3.0.0-nagareyama-rc-008/AsyncBuilder.js] 4.78 KiB {main} [built]
[./src/.fable/fable-library.3.0.0-nagareyama-rc-008/Choice.js] 4.69 KiB {main} [built]
[./src/.fable/fable-library.3.0.0-nagareyama-rc-008/Date.js] 15 KiB {main} [built]
[./src/.fable/fable-library.3.0.0-nagareyama-rc-008/Decimal.js] 5.36 KiB {main} [built]
[./src/.fable/fable-library.3.0.0-nagareyama-rc-008/List.js] 32.2 KiB {main} [built]
[./src/.fable/fable-library.3.0.0-nagareyama-rc-008/Long.js] 5.22 KiB {main} [built]
[./src/.fable/fable-library.3.0.0-nagareyama-rc-008/MailboxProcessor.js] 2.58 KiB {main} [built]
[./src/.fable/fable-library.3.0.0-nagareyama-rc-008/MapUtil.js] 3.15 KiB {main} [built]
[./src/.fable/fable-library.3.0.0-nagareyama-rc-008/MutableMap.js] 9.17 KiB {main} [built]
[./src/.fable/fable-library.3.0.0-nagareyama-rc-008/Option.js] 2.46 KiB {main} [built]
[./src/.fable/fable-library.3.0.0-nagareyama-rc-008/Seq.js] 24 KiB {main} [built]
[./src/App.fs.js] 1.78 KiB {main} [built]
    + 10 hidden modules

WARNING in ./src/.fable/FSharp.Control.AsyncSeq.3.0.2/AsyncSeq.fs.js 2258:26-35
"export 'awaitTask' was not found in '../fable-library.3.0.0-nagareyama-rc-008/Async.js'
 @ ./src/App.fs.js

WARNING in ./src/.fable/FSharp.Control.AsyncSeq.3.0.2/AsyncSeq.fs.js 2265:182-191
"export 'awaitTask' was not found in '../fable-library.3.0.0-nagareyama-rc-008/Async.js'
 @ ./src/App.fs.js

WARNING in ./src/.fable/FSharp.Control.AsyncSeq.3.0.2/AsyncSeq.fs.js 2259:16-32
"export 'runSynchronously' was not found in '../fable-library.3.0.0-nagareyama-rc-008/Async.js'
 @ ./src/App.fs.js

WARNING in ./src/.fable/FSharp.Control.AsyncSeq.3.0.2/AsyncSeq.fs.js 2298:34-45
"export 'startAsTask' was not found in '../fable-library.3.0.0-nagareyama-rc-008/Async.js'
 @ ./src/App.fs.js

There is clearly something being cached that affects the result of the build, but I don't understand what's going on...

Help?

Besides my investigations around the debug flag, I also tried to replace my nuget reference to FSharp.Control.AsyncSeq 3.0.2 with a direct project reference to a clone of the FSharp.Control.AsyncSeq repository.

I had some trouble building the repo, but after figuring it out, I managed to compile everything.

I excluded the ValueTask related code, and I'm happy to report it compiles without a problem, without the --define DEBUG workaround.

It's only after doing this that I noticed a pull request with this fix, and it's from you, @alfonsogarciacaro! ^^

So my only remaining question is: why the difference between dotnet fable src/ --verbose --define DEBUG --run webpack and dotnet fable src/ --verbose --run webpack, and why is there some kind of fishy thing going on with some sort of cache I can't figure out?

Oh my, totally forgot about that PR 馃槄 There's indeed some "caching", which is basically just checking if a generated JS file already exists and if the timestamp is more recent than the F# source. In that case, it skips Fable compilation for that file. You should see the message "Skipping Fable compilation of up-to-date JS files" when this happens.

I thought the caching was only active in watch mode, but it's activated instead by DEBUG so that's why you see it happening when passing --define DEBUG. I'll change it so it's only active in watch mode, sorry for that! https://github.com/fable-compiler/Fable/blob/e0590b20d5541ac222e69a9a527b21812434ea6d/src/Fable.Cli/Main.fs#L425-L436

Don't be sorry, it's alright!

So in my case, if the code is not supposed to compile but it does with DEBUG, does it mean Fable still outputs a (partial) js file even when the compilation fails? It then reuses that file in the next compilation, and it works by chance?

I would consider that a bug. IMO, compilation should be more deterministic, and js files from a failed compilation should be discarded. What do you think?

(Will you be able to continue your PR, or should I make one myself with just the fix to make it work with Fable?)

I agree, for now I just "fixed" it by only enabling caching in watch mode. But we should indeed mark somehow errored files so Fable doesn't try to recycle them in the next compilation.

Actually I'm not sure who's in charge of merging PRs and publishing new packages of FSharp.Control.AsyncSeq. Maybe making a new PR with only that fix has more chances of being merged, so please go ahead :)

Alright, I'll do that, thank you!

Hopefully this should be fixed with latest FSharp.Control.AsyncSeq so I'll close the issue. @mlaily Please reopen if you still have problems.

Ok! I wasn't sure if maybe you wanted to keep the issue open for this part:

But we should indeed mark somehow errored files so Fable doesn't try to recycle them in the next compilation.

Ah, true! Maybe we could open a new one for that. In most situations errors will be F# errors and in those cases Fable just disables the cache. But it's true if you have a Fable-only error, stop the compilation without fixing it and later start the watcher again, the compiler may miss the error (I've changed it so "caching" only happens in watch mode and not in normal compilation with --define DEBUG flag).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jwosty picture jwosty  路  3Comments

alfonsogarciacaro picture alfonsogarciacaro  路  3Comments

funlambda picture funlambda  路  4Comments

forki picture forki  路  3Comments

rommsen picture rommsen  路  3Comments