Supported F# language features and libraries
The compiler follows two rough guidelines when transforming the code:
string
and char
compile to JS "string" while bool
becomes "boolean".
All numeric primitives compile to JS "number" (but see Arrays below).
ResizeArray
(alias for System.Generic.Collection.List
) and non-numeric
arrays are compiled to native JS arrays. Numeric arrays are compiled to
Typed Arrays
which should provide a performance boost when interacting with HTML5 canvas or WebGL.
If you pass the
--clamp
argument to the compiler, byte arrays will be compiled asUint8ClampedArray
.
The usual string format (with a few limitations) and printing methods in F# and .NET are available:
Console/Debug.WriteLine
, String.Format
, printfn
, sprintf
... as well as the
string instance methods.
You can use the Regex
class in the same way as .NET, but the regex will always
behave as if passed RegexOptions.ECMAScript
flag (e.g., no negative look-behind
or named groups).
You can use DateTime
and TimeSpan
with the same semantics as in .NET.
TimeSpan
will just be the number of milliseconds in JS, and DateTime
will
compile down to native JS Date
with a kind
property attached.
Tuples compile to native arrays. Desestructuring, fst
, snd
... works normally.
Records are compiled as ES2015 classes
and they can be used with pattern matching (type information is available in runtime).
Record properties will be attached directly to the object instead of the prototype
making them compatible with JSON.parse
or any other function accepting plain JS objects.
Unions are also compiled as classes with the case name held in a Case
property.
Particular cases: Lists are a bit more optimized (they don't have a tag) and options are erased.
Serialized unions can be read directly with Json.NET
All enumerables compile to ES2015 iterable interface
which means they're fully compatible with compliant JS code and native methods.
The downside is you cannot implement the IEnumerable
interface in a data structure,
but you can use seq
, array
and list
comprehensions normally.
All methods in F# Seq
module have been implemented. There may be some still missing in List
and Array
modules, but in that case the compiler will default to the corresponding
Seq
function and build a new list or array from the response if necessary.
Maps and Sets fall back to the ES2015 corresponding classes
for performance. Adding and removing will create new objects. System.Collections.Generic.Dictionary
compile to ES2015 Map
too
and allows mutable operations. Same for System.Collections.Generic.HashSet
.
async
computation expressions work as expected. However, RunSynchronously
is not available and,
as JS is single-threaded, Start
and StartImmediate
will have the same effect.
Methods to convert to and from JavaScript Promises
should be simple to do and are planned for the core library.
It's possible to define custom computation expressions normally.
Pattern matching will work normally in JS and it will generate optimized
code to prevent overhead. You can match union types, records, classes or
interfaces (with :? MyClass as x
), lists, etc. Destructuring, guards and
multiple targets are also fine.
Active patterns can be used normally.
Generic information disappears in generated code. However, it's accessible
to the compiler, so calls like typeof<MyType>
are possible with concrete
types or with generics in inline functions.
Decorators are coming to JavaScript. However, there are competing proposals and it's not yet clear how the definitive specs will be. For now, attributes are only visible to the compiler which uses them, for example, when defining foreign interfaces.
Interface methods are compiled to normal object methods (there's no explicit implementation so names may collide). The interface names will be attached to the type constructor as a Symbol-keyed property, making interface type testing possible at runtime to do patterns like this one proposed by Yan Cui to extend union types.
Overloads are allowed in class implementations (not for interfaces), but they'll
have a suffix attached (_1
, _2
...) in generated code and are not recommended.
The behaviour is similar for secondary constructors.
Custom operators are possible, just note it won't be idiomatic to call
them from JS if necessary (e.g., Time.op_Addition(ts1, ts2)
).
Inheritance is possible and conforms to ES2015 inheritance. Just be careful to use the primary constructor of the base class, not a secondary one. Methods can be overridden too, but you won't be able to access the base methods by casting the object.
Anonymous lambdas will be curried by default. If you want to pass a callback with
more than one argument to JS code, please wrap it in a delegate first (e.g.,
Func<_,_,_>(fun x y -> x + y)
). If the signature of the method expects a
delegate, this will be done automatically by the compiler.
Events are not implemented yet, please use Observable instead.
Object expressions are compatible for the most general cases.
Units of measure are compatible (at least for int
and float
) but they will
be erased from the generated JS code.
You can check the tests when in doubt. If there's a test for something, it's supported :)