Fable, the F# to Javascript compiler, always had the motto “The compiler that emits JavaScript you can be proud of.” That is true, the generated javascript code is readable and idiomatic, sometimes — I can’t believe I am saying this about Javascript — It is even beautiful. However, Fable has another killer feature which will be the subject of this article: simple interop with the Javascript ecosystem.
Interop with Javascript means that you would write F# code that calls native javascript functions. In the more general sense, it lets you generate custom javascript code you control. Also it lets interact and use javascript code and libraries in your code.
In this article I will present you a plethora of examples on how and when to use Javascript interop. I will be covering a lot, so grab yourself a cup of coffee and lets get started.
Enviroment Setup
Skip this section if you already know how to setup a minimal Fable app.
We will use a local developement enviroment. To get started you will need the latest dotnet cli, Node and npm installed on your machine. Npm is packaged with Node so you don’t need a seperate download.
Lets a create a directory for our project:
mkdir FableInterop
cd FableInterop
If you are working with Fable for the first time you need to run this once:
dotnet new -i Fable.Template::*
It will download the latest fable template project from Nuget and cache it on your machine.
Now inside FableInterop
directory you can run dotnet new fable
which will generate a minimal fable app from the template. As for the editor I am using VS Code with Ionide for cross-platform F# developement.
After you have the project, you need to install it’s dependencies, this could take a couple of minutes (at least on my machine):
npm install
dotnet restore
When this finishes, you should have tooltips and auto-completion working inside VS Code.
Working with Fable is a bit tricky, you need 2 things to be working simultaneously for an “edit-save-recompile” flow, I usually use 2 shell tabs: One for running Fable compilation server using dotnet fable start
, Fable works behind a local server to cache compilation states when it recompiles a project after a change in a file, making subsequent compilations fast. Second tab is for running Webpack developement server using npm run start
that actually watches the changes in your project and sends them to the Fable server. Webpack is also responsible for serving your static content, bundling your code and refreshing the the browser when a recompilation is succesful.
Working with the code
Inside the src/App.fs
you can delete everything and leave this:
Keep Fable.Core
and Fable.Core.JsInterop
open as they provide the functions and attributes for the interop. The Fable.Import.Broweser
is opened only for using console.log
and it is generally recommended you fully qualify the the Browser
module if you want to use function from there, i.e. Browser.console.log
, here I am using it like the above for brevity.
For now we will only use src/App.fs
. To start developement mode, you can run the command:
dotnet fable npm-run start
This will start off two processes, one for Fable compilation server and one for Webpack developement server, these will work together when you make changes to your code for fast recompilations. Alternatively you can run each server seperately: run dotnet fable start
and on shell tab and in another tab, run npm run start
. Navigate to http://localhost:8080 and open your browser console, you should see this:
From now on, you can just edit and save your App.fs
file and come back to the browser or console and see the changes.
The [<Emit>] Attribute
This is an attribute that you can attach to values, functions, members and other language constructs to override their default code generation. Next are some of the many use-cases of this attribute.
Defining the undefined
Javascript has a literal value known as undefined
. F# does not have such construct. We will use the Emit
attribute to generate that value in the following example:
Notice the parts: [<Emit("undefined")>]
, obj
and jsNative
. The string “undefined” in the [<Emit>]
is called the “emit expression” , it is what gets injected in place of the undefined
value when the code is compiled. obj
is the type you give to the value. In this case an obj
is correct because undefined
could be any object in javascript. The right hand side of the assignment is ignored during compilation, due to the fact that there is an [<Emit>]
attribute. If this attribute is omitted, jsNative
will throw an error.
This way you can define custom values with their own types and use them regularly in your fsharp code. To inforce the concept, here is another example:
Whenever the compiler comes across the value one
, it will just inject the literal value within the [<Emit>]
. In this case it is 1
. Another thing to notice is the fact that it had the type of int
, this in turn allows me to use the +
operator because the type checker thinks it is an integer, which is the correct type in this case.
You have to be careful to always write the correct types because otherwise the code will just fail during runtime and cause unexpected behavior.
Generating literal code (like in the examples above) has it’s uses but is not very useful. The real fun starts when you parameterize the emit expression: using [<Emit>]
with functions, giving them macro-like behavior!
Parameterized [<Emit>] with functions
To extend our first example, I want to write a function that checks whether or not a value is undefined
, here is the example:
Now, take a closer look at the function isUndefined
. It has one paramter called x
of a generic type 'a
. The function returns bool
. Then notice that $0
in the emit expression , that is a placeholder of whatever value you pass to the function in the place of parameter x
. It has the number 0 because the parameters are 0-indexed and therefore the first parameter (in this case x
) will have the index 0. Multiple parameters are also allowed (example):
Another useful example is when you want to check whether or not a value is not a number (NaN) using the native isNaN
function:
You might want to call a function without paramters and get a result, for example if you want a random number using the native Math.random()
:
Actually, System.Random
is supported by Fable so you can use that too. Here I am just showing you what you can do.
Type-safe Javascript functions with Option<’t>
The type Option<'a>
has special usage with Fable, namely that it is erased when compiled. Some x
becomes just x
and None
becomes null
. We will use this to give native functions type-safety. For example, the function parseFloat
has a type of string -> float
. parseFloat
might fail parsing the input string and return a NaN, I know NaN is a valid value for float
but for the sake of better semantics, we want to use the type: string -> Option<float>
and return None
when the return value is NaN. We can wrap the native function inside a typed one and use pattern matching:
The [<Emit>]
expression for parseFloat
follows the logic: parseFloat
could return NaN
, Therefore I check if the result of parsing is NaN
and return null if that is the case and otherwise return the parsed value. Giving this function a return type of float option
makes it convenient to work with such functions, this way I ensure that my code has to account for failure of parsing and make sure to handle the case of None
too.
However, this approach is still not very robust because the parsing succeeds with input “5x” and returns 5 instead of failing like it should. This is more of a limitation of the native function itself and to correctly parse numbers, we will use a little javascript trick to do that. Putting the +
operator before a string will parse a string to a number! Why that works I hear you say? I don’t know, your guess is as good as mine, good reasons I hope:
Notice that I used +
twice in that emit expression and passed the parameter twice as well, which is not very efficient if my parameter was the result of an expensive operation. It should be wrapped inside a lambda (for proper scoping) and used only once:
You can still use the parse functions from the BCL such as System.Int32.(Try)Parse
, System.Double.(Try)Parse
etc. They are implemented to mimic the actual behavior from .NET as much as possible.
Writing a JQuery binding, it’s just glue.
We will use what we have learnt so far to write a jQuery binding. A binding is a collection of functions that call the functions of the original api. In this case the native library is JQuery.
First of all, add a reference to JQuery in your public/index.html
file with a script tag and include it before your bundle.js
file. index.html
should look something like:
Notice that when the page loads, jQuery’s $
will be available globally to be called in the page. Another place to reference the dollar sign is directly from the window
object like this window.$
or this window['$']
.
We will use just the [<Emit>]
to write the binding. Lets assume we want to make this binding functional-ish. It goes as follows: define a jQuery instance type, this will be an empty type just to tell when a function returns a jQuery element or something else:
Making this type an interface will ensure the type does not generate any extra code, it is only there for the type checker. Now we define our JQuery
module, first you think about how you want to use the binding. I want to be able to use it in a functional style the same way I use Seq
, Async
or List
etc:
I want it to generate something like the following with chaining:
const div = window['$']("#main")div.css("background-color", "red")
.click(function(ev) { console.log("Clicked")})
.addClass("fancy-class");
Notice I will use JQuery.select
as an alias of $
. I will need a refence to that dollar sign but I can’t just use it like [<Emit("$(...)")>]
because the dollar sign is reserved for emit expressions. However, inside a browser, every globally available variable is just a property on the window
object. So I can get a reference to $
from the window
like this:
Notice that when putting $
between quotes it becomes an allowed emit expression.
The other methods are defined in a similar way like we have seen before:
And so on and so forth for the rest of the functions if you want to support all of JQuery. Notice that, in order to enable chaining, I am passing el
as the last parameter of type IJQuery
and returning IJQuery
(most JQuery functions return a JQuery object, see docs). This makes for a nice functional API although it is just my personal preference.
Instance-based method chaining for the JQuery binding
Writing a jQuery binding as a module is one just one way to enable chaining jquery methods. I bet you expected the usual way of chaining methods is to “dot through” the api:
This requires having the methods placed on the instance type rather than on a module. Earlier we used the interface ‘IJQuery’ but it was empty, this time we will fill that interface with abstract methods:
Observations:
- using
abstract
methods to only define their type signatures. - abstract methods without an emit attribute are compiled using the name of the method.
- for custom-named functions such as
onClick
I am using[<Emit>]
to fall back to the actual name, that isclick
. The instance itself will be the first parameter that’s why I am using$0.click($1)
where$0
is the instance and$1
is the argument. - the
css
has a paramter as a tuple to correctly compile to javascript. If I used parameters such ascss : string -> string -> IJQuery
I would not be able to “dot through” the code and would have to usecss
with it’s paramters between parentheses. - I kept using the
JQuery.select
to start the “chain”.
To be used like this:
Will produce:
If you don’t like giving everything a type, you can go quick-and-dirty with dynamic programming capabilities of Fable, although I personally discourage using this model because one of the main reasons for chosing F# to compile to javascript is the powerful type-system and if I wanted to write dynamic code I wouldn’t bother to use Fable in the first place. Anyways, each to his own, you might like this model so here it is:
Working with object literals
JQuery among almost every other javascript library, works with object literals. They are used as paramters most of the time and they are ubiquitous.
Using Fable. We want to be able to create and manipulate object literals in a type-safe way. There are multipe ways of doing that. For example, assume I have the imaginary function in javascript addTime
that is natively used like this:
As you can see, the object literal consists of three properties: current
has type Date
(in javascript). amount
has type number
and unit
is a string
. To represent these types in F# we would use DateTime
for current
, int
for amount
and string
for unit
. We will use a type to represent the whole object literal like the following, lets call it AddTimeProps
:
This will output this:
A simple object literal becomes the output which is exactly what the external function addTime
is expecting. Notice that because AddTimeProps
does not have any constructors, I used the createEmpty<T>
function. This is a special Fable function that will create an empty object literal but with the given type paramter T
. In this case T
is AddTimeProps
. Also notice that we are not using [<Emit>]
with the properties. That is because they are abstract and Fable will use the name of the property provided. To use custom names, you can use the [<Emit>]
attribute but with a funny emit expression syntax like this:
Here I am replacing the property amount
with specialAmount
using [<Emit>]
then it produces the property with the custom name, it is using “optional parameter” syntax to determine whether it should use the setter or the getter for the property.
String Literal Types, only better
Now, you might be satisfied with this solution for type-safety but you can actually do better! Suppose you are reading the docs of addTime
and come across the information that the property unit
can only have string values “days”, “months” or “years”. To ensure that no one forgets these values or write them incorrectly we want the compiler to check the correctness our code. For this case we can use the [<StringEnum>]
attribute. This is similar to “string-typing” in typescript. You can define a discriminated union with cases that don’t have paramters and have them compiled to strings at compile-time. Here is an example:
We can use this to enhance the AddTimeProp
type with even more type-safety by changing the type of unit
from string
to TimeUnit
:
The case of the discriminated union is camel-cased when compiled. If you need a custom name for your union case such as “YEARS” instead of “year”, you can use the [<CompiledName>]
attribute applied to the case:
The output becomes:
Using [<Pojo>]
Attribute
Plain old javascript objects or POJOs is just another name for object literals. Fable provides a useful attribute [<Pojo>]
that is applicable to record types to make them compile to object literals, here is an example:
This does not change the fact that they are still immutable. However, you can still start with an empty object using the createEmpty<T>
function:
A note from Fable’s author, Alfonso Garcia-Caro:
Pojo records are only intended for type-safe interaction with javascript libraries that require a plain object (like React components). These records lack many features, like instance and static methods and have no reflection support.
Using a list of discriminated union as object literal
Yes, that is also possible! To use the previous example of Person
, this is how you would describe it as a discriminated union:
A Person
has these properties of Name
and Age
but because this is a sum type, you can either have Name
or Age
to be a person which does not make sense. In order for this to work, you actually need a list of Person
:
Not quite idiomatic in F# but it works well (and looks nice) when interacting with external libraries. Now that you have the list of Person, you can use the special function keyValueList
provided by Fable (in Fable.Core.JsInterop
) to turn that list into an object literal:
These type of object literals should be used you have many optional properties of an object and you want a couple of them and you ignore the rest. This works pretty well for example for React style objects or for JQuery’s ajax options.
You can all also use ad-hoc properties using unbox
or using the new dynamic operator !!
:
It it worth noting that Fable will try to convert the list to an object literal during compile-time if the value is constant and in run-time if the value is yet to be determined during run-time.
Creating object literals inline
Again, if you feel lazy and you don’t want to give everything a type, you can another Fable function called createObj
from Fable.Core
. This function creates an object literal like this:
createObj
is nice becuase it accepts a list of key-value pairs just like what would expect from an object literal. You can easily nest objects too:
Interacting with existing Javascript code
All our interaction with javascript so far was through generating some custom code that will be injected during compilation using the different attributes and functions that Fable provides. Now it is time to interact with existing javascript code and actually call it from F#. To learn this, we will write some javascript by hand. First create a file called custom.js
like this:
This file will contain two functions that we will call from F#. parseJson
and getValue
organized using modules to be compatible with webpack:
parseJson
will try to parse a string to an object literal and will return null
if that fails. getValue
will try to get a value from an object literal using it’s string index and will return null
if such property does not exist (i.e. the property is undefined
).
Because both functions will return either a result or null, that qualifies them to return Option<'a>
. Also notice that this file is in the same directory src
as the the App.fs
file. When importing custom code, you use relative paths.
To import these functions, we will use the import
function from inside App.fs
like this:
The first argument for import
is what value you want to import, in this case it is the function parseJson
and the second argument is the where you want to import that value from, in this case from the file called custom.js
in the same directory.
Now these functions are available to be used:
With the output:
Another way to import both functions or any number of functions from a javascript module is using the importAll
function, but first you have to put declarations into one type and import all the functions from the javascript module as that type:
A javascript module can export a single value:
I created a file default.js
inside src
directory with the code above. You can import it using importDefault
:
Interacting with Javascript from npm packages
Modern javascript libraries are distributed through npm, the node package manager, as modules. Less often are there “built and ready” libraries that you just add to your page with a script tag. In our Fable apps we definitely want to interact such libraries. For the following example I want to use a silly library called left-pad. I call it silly because this “library” is a single function used by millions, instead of … you know, just writing the function yourself whenever you need it.
Anyways, I will stop ranting now :), here we go. It the same as with our custom.js
file but instead of pointing to the path of library, you just point to the name of it. First you want to install the library using npm
by running npm install --save left-pad
. This package is then added as a dependency to your package.json
file, you should see the entry "left-pad": "^1.1.3"
in your dependencies
. It is insalled in your node-modules
directory and with the magic of Webpack, you can import it from anywhere in you F# code like this:
Working with overloads
If you look at the docs of left-pad, the function leftPad
should have another overload with only two parameters. Because you can’t overload normal F# functions (like the one above) you can write the same function with a different (but meaningful) name with two parameters:
If you want to overload the method with the same name, you can use static methods on a class.
What about when you have a function with one paramter but that parameter can be a string
, int
or DateTime
? Then you use “erased unions”. These special union types created just for overloading paramter types. To define a function with a such parameter:
If you had more types, you would use U4<>
, U5<>
etc. To use such functions you write:
Notice the funny !^
operator specially made to work with erased types and to check whether or not the provided type is actually compatible with parameter type.
Curried and Uncurried Functions
Functions in F# are curried by default, that is when using multiple arguments, the function becomes a single parameter function that returns other functions, here is an example:
This function is equivalent to this curried function:
In the early days of Fable, it used to compile the curried function as is with closures:
But recently as of Fable 1.0 beta, the compilation is optimized and the function is uncurried:
What if you wanted to explicitly return a function like this:
then you would have to use System.Func
in the signature as the return type:
Conclusion
There are many ways to interact with javascript in Fable. This allows you to leverage all of the javascript ecosystem and the numerous libraries published on npm. I hope you learnt a lot from this article, don’t forget to hit that heart icon below and to share!