Fable: Roadmap and Priorities

Created on 3 May 2017  路  23Comments  路  Source: fable-compiler/Fable

I know this project is very transient as far as contributors, but I thought it might be a good idea to start a roadmap, not necessarily with timelines, but a place where contributors can find places to have the most impact. Here is my current thinking as far as priorities.

ts2fable

The current version of ts2fable gives you a starting point, but there have been quite a few required changes after using it. Also, the .TypeScript version is so far behind it chokes on anything new, which unfortunately means the most popular libraries assuming they keep up with TS versions, which most do.

Long term, I would like to see it integrated into the tool chain. As in, you dotnet fable add react, we would check to see if there is a typings entry in the package.json, find the types, ts2fable them, and if there aren't any, check to see if @types has something matching the name. After generating the .fs file we would automatically add it to the project. We claim interop with other libraries, which as of now is mostly untrue. Unless you go dynamic, with a poor developer experience. IMO, developer experience is key.

I have taken ts2fable as far as I can get it. I have 2 main experiments. The first is using the TS Compiler and it is mostly completed, with issues I cannot really fix without a deeper understanding of F# which right now is still lacking.. The other uses typedoc and needs to have all the JSON parsing completed. As of now this too is a little out of my league.

Compile time

The compile time right now is brutal. This is mainly for non-browser applications where you can't really hot reload AFAIK. For example, my iteration time is between 10 and 20 seconds right now on the node template I am about to PR.

Source Maps

I am not sure how to read this from source maps.

ERROR in ./Evictor.fsproj
C:\dev\code\Evictor\src\App.fs(9,0,9,3) : error FSHARP: Incomplete structured construct at or before this point in pattern

Frequently it gives me line numbers that are greater than what I have in my file.

Samples updated.

This part of the roadmap would just track the most requested samples. Right now, I feel like there are too many samples. IMO, we should focus on the five most key scenarios and target those with simple examples. Seems like there are too many samples that take a long time to update to new versions. Plus how many D3 samples do we need?

Drop Babel in favor of TypeScript

At first it would mostly be a drop in replacement, but could be expanded to include Types. My initial testing with this showed a minor performance increase and a less than 1% increase in file size of the output.

This allows us to leverage the TypeScript eco system for things like documentation generation, declarations for developers, and allows people using TypeScript to use the output of our work, without having to convert to F#. On top of that, we can still compile down to JavaScript. The beauty of that, is that all most of the babel plugins are just compiler switches in TypeScript. I know this one might be controversial, but if it could be done, I think it would be epic.

Tooling

Right now the tooling is really bad. Differences between Ionide and the compiler can be really annoying.

Documentation

This along with ts2fable are the key to developer happiness IMO. When developers are happy, that is when you start to see growth. Simple, but true.

Gitter repeat issues

We got to get out of the workaround business and start doing a better job of tracking and addressing the repeat issues we see in Gitter.

Non-Developer facing work

I believe there is a lot of opportunity for automation of a lot of the dev ops type work. Like @Pauan mentioned in another issue, using bots to automate deployments. Anything that can be automated, frees up @alfonsogarciacaro and others to build epic shit.

That's all I got for now.

Most helpful comment

@et1975 The problem isn't with implementing a GC for F#, since as you said there already are some. There's also GC engines for C++, which can of course be compiled to WebAssembly.

The problem is that the WebAssembly code cannot access GC objects, such as JavaScript and DOM objects.

That means that if you compiled Fable to WebAssembly, you wouldn't be able to access any JavaScript objects! That means no DOM access, no window access, no access to JavaScript built-ins like Array, RegExp, etc...

Basically, there would be no JavaScript interop whatsoever, and that also means no fable-core access.

So it could be useful if you wanted to write a pure number-crunching algorithm in F#, like an encoder/decoder or encryption/decryption, but it wouldn't be useful at all for most Fable projects.

Eventually this will change, and WebAssembly will get the ability to access GC objects, but for now it's a no-go.

After WebAssembly gets GC access, I predict there will be an explosion of compile-to-JS languages, even more so than there are right now, since it means native languages like Haskell, Python, Ruby, F#, etc. will gain the ability to run in a web browser at the same speed (and with the same behavior) as on the desktop.

All 23 comments

The REPL is already operational on Fable 1.0-beta, here is some of the outstanding work:

  • publish the updated REPL, add more code samples.
  • make build target for the REPL, add all dependencies through Paket.
  • migrate REPL from Ace to Monaco editor (ionide-web) with auto-complete and tooltips support.

I don't mind outputting TypeScript, but I do like that Babel has a strong plugin ecosystem, whereas TypeScript seems a lot more limited: you have to make do with what they give you, no customization.

I guess it might be possible to have the TypeScript compile to ES2015, and then run that through Babel... so the workflow would be F# -> Fable -> TypeScript -> ES2015 -> Babel -> ES5

You would only need to do that if you wanted to use Babel plugins, otherwise it would just be F# -> Fable -> TypeScript -> ES5

@Pauan Yes, but keep in mind a lot of the things you use plugins for are built into TS. Like Decorators, outputting different module types, and more. Plus in 2.3+ they support checking .JS files for errors, which I haven't looked into much, but sounds cool. Another bonus is reducing the dependency count greatly. Not to mention all of these things are supported by one big team.

Wow, this update looks like it is a great time to jump on board. They just added language service plugins too.

https://blogs.msdn.microsoft.com/typescript/2017/04/27/announcing-typescript-2-3/

@mizzle-mo Some of the type-checking benefits of TS 2.3 (e.g. --checkJs) can still be utilized as a validation step even without switching fully the pipeline to TS.

@ncave I saw that too, pretty cool and could be useful for us to implement gradually.

@mizzle-mo Another consideration is the size/speed of standalone TypeScript (running in browser). Babel fully supports that and seems quite fast.

@ncave Since you can augment the Language Service now, could we use that as a basis for a language plugin for Fable, maybe were Intellisense triggers on dynamics? Like for Node fs? and then get Intellisense based off of the TS declaration?

@mizzle-mo I'm not sure, perhaps you have a different use-case in mind. For F# I'd rather rely on the FSharp Compiler Service directly in either browser or node setups for auto-compete, tooltips, etc.

@ncave What I am suggesting would be additive. However after thinking about it, it would be better to just light up automatic or trivial ts2fable.

@mizzle-mo Do you have a prototype of Fable compiling to TS? 馃槷 I don't mind compiling to TS at all, it was actually my first option but at the time it was not possible to emit an AST directly. I think it is now, but I haven't looked much into it.

Related, I've written some ideas for tooling evolution here: https://github.com/fable-compiler/Fable/issues/856#issuecomment-299045355

@alfonsogarciacaro I didn't have Fable compiling to TS, I just replaced, the babel-loader with ts-loader. I will upload it as soon as I can.

@alfonsogarciacaro Here are the results. I used awesome-typescript-loader this time to take advantage of some extra perf knobs, like disabling type checking. I also turned on caching so these are the results of the 2nd run of both babel and typescript. This was using the out of the box Fable-Elmish template.

Link to experiment: https://github.com/fable-compiler/Fable-TypeScript

TypeScript 2nd Start

Time: 19671ms
        Asset     Size  Chunks                    Chunk Names
    bundle.js  1.78 MB       0  [emitted]  [big]  main
bundle.js.map  2.21 MB       0  [emitted]         main
chunk    {0} bundle.js, bundle.js.map (main) 1.65 MB [entry] [rendered]
    [4] ./~/fable-core/Util.js 14.8 kB {0} [built]
  [137] ./~/fable-elmish-browser/parser.fs 7.21 kB {0} [built]
  [138] ./~/fable-react/Fable.Helpers.React.fs 21.2 kB {0} [built]
  [141] ./src/Types.fs 2.09 kB {0} [built]
  [192] ./fable-ts-prototype.fsproj 29 bytes {0} [built]
  [193] (webpack)-dev-server/client?http://localhost:8080 5.68 kB {0} [built]
  [259] ./src/App.fs 3.27 kB {0} [built]
  [420] ./~/strip-ansi/index.js 161 bytes {0} [built]
  [423] ./sass/main.sass 1.02 kB {0} [built]
  [424] ./~/url/url.js 23.3 kB {0} [built]
  [425] ./~/url/util.js 314 bytes {0} [built]
  [426] (webpack)-dev-server/client/overlay.js 3.73 kB {0} [built]
  [427] (webpack)-dev-server/client/socket.js 897 bytes {0} [built]
  [429] (webpack)/hot/emitter.js 77 bytes {0} [built]
  [430] multi (webpack)-dev-server/client?http://localhost:8080 ./fable-ts-prototype.fsproj 40 bytes {0} [built]
     + 416 hidden modules
webpack: Compiled successfully.

Babel 2nd Start

Time: 20485ms
        Asset     Size  Chunks                    Chunk Names
    bundle.js  1.87 MB       0  [emitted]  [big]  main
bundle.js.map  2.11 MB       0  [emitted]         main
chunk    {0} bundle.js, bundle.js.map (main) 1.69 MB [entry] [rendered]
   [15] ./~/fable-core/List.js 2.98 kB {0} [built]
   [47] ./~/react/react.js 56 bytes {0} [built]
  [229] ./fable-ts-prototype.fsproj 29 bytes {0} [built]
  [230] (webpack)-dev-server/client?http://localhost:8080 5.68 kB {0} [built]
  [317] ./src/App.fs 3.27 kB {0} [built]
  [324] ./src/State.fs 1.99 kB {0} [built]
  [339] ./~/html-entities/index.js 231 bytes {0} [built]
  [352] ./~/punycode/punycode.js 14.7 kB {0} [built]
  [452] ./~/sockjs-client/lib/entry.js 244 bytes {0} [built]
  [478] ./~/strip-ansi/index.js 161 bytes {0} [built]
  [482] ./~/url/url.js 23.3 kB {0} [built]
  [484] (webpack)-dev-server/client/overlay.js 3.73 kB {0} [built]
  [485] (webpack)-dev-server/client/socket.js 897 bytes {0} [built]
  [487] (webpack)/hot/emitter.js 77 bytes {0} [built]
  [488] multi (webpack)-dev-server/client?http://localhost:8080 ./fable-ts-prototype.fsproj 40 bytes {0} [built]
     + 474 hidden modules
webpack: Compiled successfully.

Babel holds an internal intermediate model of the code, if we are going to talk about plugging into a better compiler chain let's talk about LLVM, which has emscripten backend (with eventual goal of WebAssembly). TypeScript feels like a step sideways.

@et1975 What would the level of effort be to convert to LLVM? I don't know much about it except that some people use emscripten to compile C# to JS.

@et1975 Won't work. ASM.js and WebAssembly uses an array of bytes for memory. It doesn't have any garbage collection, and it can't use garbage collected objects (such as JS objects, DOM objects, etc.)

Eventually support for garbage collected objects will be added to WebAssembly, but we're not there yet.

@Pauan is right. I've been thinking of the idea of having an "ASM.js mode" that throws compile-time errors if you try to allocate memory in the heap. But for now I think WebAssembly is most useful for CPU-intensive operations and for that it's probably easier to just use the libraries that will progressively be available.

https://github.com/dotnet/llilc is implementing GC.

@et1975 The problem isn't with implementing a GC for F#, since as you said there already are some. There's also GC engines for C++, which can of course be compiled to WebAssembly.

The problem is that the WebAssembly code cannot access GC objects, such as JavaScript and DOM objects.

That means that if you compiled Fable to WebAssembly, you wouldn't be able to access any JavaScript objects! That means no DOM access, no window access, no access to JavaScript built-ins like Array, RegExp, etc...

Basically, there would be no JavaScript interop whatsoever, and that also means no fable-core access.

So it could be useful if you wanted to write a pure number-crunching algorithm in F#, like an encoder/decoder or encryption/decryption, but it wouldn't be useful at all for most Fable projects.

Eventually this will change, and WebAssembly will get the ability to access GC objects, but for now it's a no-go.

After WebAssembly gets GC access, I predict there will be an explosion of compile-to-JS languages, even more so than there are right now, since it means native languages like Haskell, Python, Ruby, F#, etc. will gain the ability to run in a web browser at the same speed (and with the same behavior) as on the desktop.

Good thing I'm not suggesting switching to LLVM is any sort of priority then :)

As this has already spawned very useful discussion and other issues, I'm closing it for now. Please feel free to comment if you still have question or open a new issue with any one of the topics mentioned in the initial post :)

@et1975 I did some digging into this, since I'm interested in Rust, and it seems that you can access DOM stuff via emscripten: https://github.com/rust-webplatform/rust-webplatform

I'm not sure exactly how it works (it seems like voodoo magic to me), and it probably won't work with WebAssembly (yet), but it seems to work well enough to implement TodoMVC in Rust: https://github.com/rust-webplatform/rust-todomvc

Was this page helpful?
0 / 5 - 0 ratings