When upgrading, make sure to upgrade your dependencies as well.
More than a year after the first alpha release, we are happy to announce that Fable 5 is now in the release candidate stage!
You can install Fable 5 by running the following command:
# If you haven't installed a dotnet tool in this directory yet
dotnet new tool-manifest
dotnet tool install fable --prereleaseIf you already have Fable installed, you can update it by running:
dotnet tool update fable --prereleaseCompatibility with Fable 4
Fable 5 is compatible with Fable 4 projects, except that it now targets net10.0.
Project cracking
We removed support for the old project cracker using Buildalyzer. This has been replaced by invoking MSBuild directly, which should be more robust for the future.
WarnAsError support
It has been a long-standing request to support <TreatWarningsAsErrors>true</TreatWarningsAsErrors> in Fable, and we are happy to announce that it is now supported in Fable 5.
You can enable it in your project file like this:
<PropertyGroup>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>Fable will now treat all warnings from your code as errors, but it will still allow warnings from dependencies.
This behavior should mimic how the standard F# compiler works.
.NET 10 and F# 10 support
Fable 5 adds support for the following F# features:
- Nullable reference types
- Discriminated union
.Is*properties - Partial active patterns can return
boolinstead ofunit option - Empty-bodied computation expressions
- Updates to the standard library (FSharp.Core)
JavaScript / TypeScript
The JavaScript target is still the most used and stable target, and we have continued to improve it over the past year.
Support for direct nested types when using jsOptions
let opts =
jsOptions<Level1> (fun o ->
o.level2.level3.valueA <- 10
o.level2.level3.valueB <- 20
o.topValueA <- 20
)generates
export const opts = {
level2: {
level3: {
valueA: 10,
valueB: 20,
},
},
topValueA: 20,
};Simplify Pojo bindings
Fable has a number of ways to create Plain Old JavaScript Objects (POJOs).
In Fable 4, people discovered a way of abusing a combination of attributes to write POJO bindings in a natural F# way.
open Fable.Core
[<AllowNullLiteral>]
[<Global>]
type Options
[<ParamObject; Emit("$0")>]
(
searchTerm: string,
?isCaseSensitive: bool
) =
member val searchTerm: string = jsNative with get, set
member val isCaseSensitive: bool option = jsNative with get, set
let options1 = new Options("foo")
let options2 = new Options("foo", isCaseSensitive = true)Improving on this, Fable 5 introduces a new attribute, Pojo, that can be used to write the same code with only one attribute:
open Fable.Core
[<AllowNullLiteral>]
[<JS.Pojo>]
type Options
(
searchTerm: string,
?isCaseSensitive: bool
) =
member val searchTerm: string = jsNative with get, set
member val isCaseSensitive: bool option = jsNative with get, set
let options1 = new Options("foo")
let options2 = new Options("foo", isCaseSensitive = true)Both examples generate the same JavaScript code:
export const options1 = {
searchTerm: "foo",
};
export const options2 = {
searchTerm: "foo",
isCaseSensitive: true,
};Python
If you have been following the Fable 5 alpha releases, you know the Python target has received a staggering amount of love.
This is all thanks to Dag and the early adopters who have been testing it.
- Python 3.12-3.14 support (3.10/3.11 are deprecated)
- fable-library via PyPI - No more bundled runtime files
- Modern type parameter syntax - Better type hinting in generated code
Py.Decorateattribute - Add Python decorators from F#Py.ClassAttributesattribute - Fine-grained class generation control- Improved Pydantic interop - First-class support for data validation
Rust Core with PyO3
One of the biggest changes is that the core of fable-library is now written in Rust using PyO3. The motivation here is correctness, not performance:
Why Rust?
- Correct .NET semantics - Sized/signed integers (int8, int16, int32, int64, uint8, etc.)
- Proper overflow behavior - Matches .NET exactly
- Fixed-size arrays - No more Python list quirks for byte streams
- Reliable numerics - Fable 4's pure Python numerics were a constant source of bugs
fable-library via PyPI
Before Fable v5, the runtime was bundled in the NuGet package and copied to your output directory.
Now it's a simple pip/uv dependency:
# Install with pip
pip install fable-library
# Or with uv (recommended)
uv add fable-libraryRust
The Rust target kept improving as ell, and is now using Rust 2024 language edition.
Hello Erlang/BEAM !
Dag Brattli added a new target for Erlang/BEAM, which is still in its early stages.
He has been testing it using Fable.Giraffe and the first results are promising:
Benchmarks
Simple
/pingendpoint returning "pong", 10,000 requests with 100 concurrent connections (oha):
Metric BEAM .NET Python Requests/sec 124,256 70,375 4,006 Avg latency 0.79 ms 1.40 ms 24.9 ms P99 latency 2.49 ms 3.50 ms 34.2 ms
Indeed, it seems like Erlang/BEAM is a great target for Fable, and we are excited to see how it evolves in the future.
Conclusion
Fable 5 would not have been possible without the help of the many people who contributed to it in different ways, from testing, to reporting issues, to contributing code.
Thank you all for your help and support over the last year, and we hope you will continue to help us in the future to make Fable even better!
