Added in v5.0.0-rc.1

Beam target is in alpha meaning that breaking changes can happen between minor versions.

Fable provides support for some classes of .NET BCL (Base Class Library) and most of FSharp.Core library. When possible, Fable translates .NET types and methods to native Erlang types for minimum overhead.

Common Types and Objects

Many F#/.NET types have natural counterparts in Erlang. Fable takes advantage of this to compile to native types that are more performant and idiomatic:

  • Strings compile to Erlang binaries (<<"hello">>).
  • Booleans compile to Erlang atoms (true / false).
  • Chars are compiled as strings of length 1.
  • Integers use Erlang's native arbitrary-precision integers. Unlike Python and JavaScript, no wrapper types are needed for int, int64, or bigint.
  • Floats use Erlang's native float() type.
  • Tuples compile directly to Erlang tuples ({A, B, C}).
  • Lists (list<T>) compile to Erlang linked lists — both languages use cons cells, making this a perfect fit.
  • Arrays compile to process dictionary references wrapping Erlang lists (for mutability). Byte arrays use atomics for O(1) read/write.
  • ResizeArray compiles to process dictionary references with list mutation helpers.
  • Any IEnumerable (or seq) can be traversed using Erlang's iterator patterns.
  • Maps (Map<K,V>) compile to Erlang native maps (#{}).
  • Sets (Set<T>) compile to Erlang ordsets (sorted lists).
  • Mutable dictionaries compile to process dictionary references wrapping Erlang maps.
  • Unit compiles to the ok atom.

.NET Base Class Library

The following classes are translated to Erlang and most of their methods (static and instance) should be available in Fable.
.NETErlang
Numeric TypesNative integers and floats
ArraysProcess dict refs (lists / atomics for byte[])
System.Booleantrue / false atoms
System.CharBinary (string of length 1)
System.StringBinary (<<"...">>)
System.DecimalCustom fixed-scale integer
System.DateTime{Ticks, Kind} tuple
System.DateTimeOffset{Ticks, OffsetTicks, Kind} tuple
System.TimeSpanTicks-based integer
System.GuidUUID v4 binary
System.UriURI binary with parsing
System.Text.RegularExpressions.RegexErlang re module (PCRE)
System.Text.StringBuilderMutable binary builder
System.Collections.Generic.ListProcess dict ref (list)
System.Collections.Generic.DictionaryProcess dict ref (map)
System.Collections.Generic.HashSetProcess dict ref (map-based set)
System.Collections.Generic.QueueProcess dict ref (Erlang queue)
System.Collections.Generic.StackProcess dict ref (list)
System.Diagnostics.Stopwatcherlang:monotonic_time
RecordsErlang maps (#{})
Anonymous RecordsErlang maps (#{})
TuplesErlang tuples ({})

FSharp.Core

Most of FSharp.Core operators are supported, as well as formatting with sprintf, printfn, or failwithf. The following types and/or corresponding modules from FSharp.Core lib will translate to Erlang:
.NETErlang
TuplesErlang tuples
Option(erased) — Some(x) = x, None = undefined
StringBinary
ListErlang linked list (cons cells)
MapErlang native map (#{})
Setordsets (sorted list)
ResizeArrayProcess dict ref (list)
RecordErlang map (#{})
Anonymous RecordErlang map (#{})
Result{ok, Value} / {error, Error}
AsyncCPS function fun(Ctx) -> ... end
TaskAlias for Async on Beam

Caveats

  • Options are erased in Erlang (Some 5 becomes just 5 and None becomes undefined). Nested options use wrapped representation ({some, x}) to avoid ambiguity.
  • Records compile to Erlang maps with snake_case atom keys.
  • Anonymous Records also compile to Erlang maps.
  • Result maps to Erlang's idiomatic {ok, V} / {error, E} convention.
  • Unit is represented as the ok atom, which is distinct from undefined (None) — unlike JS/Python where both map to similar concepts.

Interfaces and Protocols

F# interfaces compile to Erlang maps of closures (dispatch maps):
.NETErlangComment
IEquatableNative =:=Deep structural equality on all types
IEnumeratorIterator patternnext() style iteration
IEnumerableList iterationfor loop / lists:foreach
IComparableNative <, >, =<, >=Works on all Erlang terms
IDisposableManual cleanupNo with statement equivalent
ToStringfable_string:to_string/1String formatting

Object Oriented Programming

F# OOP features like interfaces, abstract classes, inheritance, and overloading are supported. Object expressions compile to Erlang maps of closures.

Numeric Types

Erlang has native arbitrary-precision integers, making integer support much simpler than on JavaScript or Python targets. No wrapper types or Rust NIFs are needed:
F#.NETErlangNotes
boolBooleantrue/false atomsNative
intInt32integer()Native arbitrary-precision
byteByteinteger()Native
sbyteSByteinteger()Native
int16Int16integer()Native
int64Int64integer()Native (no BigInt library needed)
uint16UInt16integer()Native
uint32UInt32integer()Native
uint64UInt64integer()Native
float / doubleDoublefloat()Native IEEE 754
float32 / singleSinglefloat()Native IEEE 754
decimalDecimalCustomFixed-scale integer implementation
bigintBigIntegerinteger()Native (Erlang integers ARE arbitrary-precision)

Sized Integer Overflow

Like Python, Erlang has native arbitrary-precision integers. Sized integer wrapping (for overflow semantics of int32, int64, etc.) uses Erlang's bit syntax:

%% Wrapping int32 arithmetic
wrap32(N) ->
    <<V:32/signed-integer>> = <<N:32/signed-integer>>,
    V.

Reflection

Full FSharp.Reflection support is available via fable_reflection.erl:

  • FSharpType.IsTuple, IsRecord, IsUnion, IsFunction
  • FSharpType.GetTupleElements, GetRecordFields, GetUnionCases
  • FSharpValue.GetRecordFields, MakeRecord
  • FSharpValue.GetTupleFields, MakeTuple, GetTupleField
  • FSharpValue.GetUnionFields, MakeUnion
  • PropertyInfo.GetValue

Async and Concurrency

F# async workflows use CPS (Continuation-Passing Style) where Async<T> = fun(Ctx) -> ok end with a context map containing on_success, on_error, on_cancel, and cancel_token.
F#Erlang
async { return x }CPS function with on_success callback
let! x = compbind(Comp, fun(X) -> ... end)
Async.RunSynchronouslyCPS invocation in same process
Async.Parallelspawn per computation, receive to collect
Async.Sleeptimer:sleep(Ms)
task { return x }Same as async (alias on Beam)

task { } and async { } compile to the same CPS representation on Beam. The hot-start semantics of .NET Task are not preserved. Downcasting from obj to Task<T> or Async<T> is not supported.

CancellationToken

Full cancellation support via fable_cancellation.erl:

  • CancellationTokenSource — create, cancel, cancel after timeout
  • Register / Unregister — listener management
  • IsCancellationRequested — poll cancellation state
  • Timer-based auto-cancel via cancel_after

Observable

Full Observable module support:

  • subscribe, add, choose, filter, map
  • merge, pairwise, partition, scan, split

Sequence Expressions

Sequence expressions are supported and compile to lazy evaluation:

let numbers = seq {
    yield 1
    yield 2
    yield! [3; 4; 5]
}

String Formatting

Full F# format string support (%d, %s, %.2f, %g, %x, etc.) via fable_string.erl runtime:

  • sprintf — format to string
  • printfn — format to stdout
  • eprintfn — format to stderr
  • failwithf — format and throw
  • String.Format — .NET-style positional format strings

String interpolation ($"Hello, {name}!") compiles to iolist_to_binary with appropriate type conversions.

Tail Call Optimization

Erlang has native tail call optimization, so recursive F# functions compile to efficient tail-recursive Erlang functions without needing a trampoline.