May 23rd, 2016 - written by Kimserey with .
In WebSharper, there are two keywords to bind JS code to our F# code - Direct
and Inline
.
The documentation about the attributes can be found here http://websharper.com/docs/translation.
I have demonstrated the use of it in previous blog posts: External JS library with WebSharper in F#, Sort, drag and drop in UI Next with Sortable.
Although the documentation has some explanation about it, I still feel like it is pretty vague. So today I would like to give more explanation about the differences between Direct
and Inline
.
This post is composed by three parts:
Direct
?Inline
?Direct
?Even though WebSharper does a very good job to allow us to write JS code in F#,
some functions are still better written in JS directly.
That is where Direct
comes into action.
Direct
allows us to create a placeholder functions which can be used anywhere in our code.
During JS translatin, the function body will be replaced by the content of the string
argument passed to the Direct
attribute.
1
2
3
4
5
[<Direct "$x + $y" >]
let add (x: int) (y: int) = X<int>
... somewhere in the code ...
let result = add 1 2
This will kind of be translated to*:
1
2
3
4
5
function($x,$y) {
return $x+$y;
}
var result = add(1,2)
*It’s not really like that but this is close enough to understand.
Here there are three import points to understand:
$
is used to bind the parameters, it is also possible to use $0
, $1
, etc… to get the parameters by indexX<_>
is a placeholder value, it is a simple compiler trick for the function to have the correct type returnedDirect
has placed the content in the body of a function and have placed a return
on the valueHaving the content in the a body of a function has another advantage - it allows us to pass a piece of code to Direct
:
1
2
3
4
5
6
[<Direct """
console.log("Hey");
console.log("I am adding x and y");
return $x + $y;
""">]
let add x y = X<int>
As you would expect, this is kind of translated to*:
1
2
3
4
5
function($x,$y) {
console.log("Hey");
console.log("I am adding x and y");
return $x+$y;
}
*It’s not really like that but this is close enough to understand.
Here only one point to note:
The return
keyword has not been placed automatically anymore. This is because the JS code is multiline.
So don’t forget the return
if you want to return a value.
Inline
?Inline
is used to inline the JS code to the call of the function.
That’s what the documentation says and that’s what it does.
Although the first time I read it, it left me with questions marks.
To understand better, let’s see an example.
If we take the previous example and change Direct
with Inline
we get:
1
2
3
4
5
[<Inline "$x + $y">]
let add (x: int) (y: int) = X<int>
... somewhere in the code ...
let result = add 1 2
The translation becomes:
1
var result = 1 + 2
So that was what the documentation meant.
Everywhere the function add
is called, the call get replaced by the JS code x + y
.
The JS code is inlined to the call of the function.
Only one restriction to note, since Inline
is replacing the calls, it can’t be multiline.
At this point you may be thinking:
Direct
and Inline
both do the same thing except Inline
has restrictions, I might as well just use Direct.
Well yes, most of the time I use Direct
but in some cases, inlining the code is a necessity.
A typical example would be when you want to create binding to the members of a JS object.
1
2
3
4
5
6
7
8
type Location =
[<Inline "window.location">]
static member Create() = X<Location>
[<Inline "$0.href">]
member x.GetHref() = X<string>
...somewhere in your code...
let href = Location.Create().GetHref()
$0
represents the first parameter, for a member function it is the instance (itself x
here).
This will get translated to:
1
var href = window.location.href
What if I used Direct?
1
2
[<Direct "$0.href">]
member x.GetHref() = X<string>
This would have been translated to
1
window.location.GetHref()
which is wrong since location
doesn’t have a member called GetHref
. You would have had the following error Uncaught TypeError: window.location.GetHref is not a function
.
In this sample we can’t use Direct
. The code has to be inlined therefore we must use Inline
.
Today we saw the differences between Direct
and Inline
.
I tend to use Direct
more often. I use Inline
only when I need to define bindings for JS object members.
But that is just what I experienced so far and there might be other use cases.
I hope this helped you understand better how you could integrate JS code directly into your F# code with WebSharper and
most importantly I hope this demystified the meaning behind Direct
and Inline
attributes!
If you have any question, leave it here or hit me on Twitter @Kimserey_Lam. See you next time!