Skip to content

Commit 21b00ec

Browse files
Blog entry
1 parent dca9742 commit 21b00ec

File tree

5 files changed

+137
-1
lines changed

5 files changed

+137
-1
lines changed

docs/source/blog.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@
22

33
# Fable blog
44

5-
## Introducing 0.7
5+
## [Tree Shaking with Fable](blog/Tree-shaking.html)
6+
7+
> By [Alfonso García-Caro](https://twitter.com/alfonsogcnunez) on Nov 30, 2016
8+
9+
Hi everybody! This is my first contribution to the [F# advent calendar](https://sergeytihon.wordpress.com/2016/10/23/f-advent-calendar-in-english-2016/) and you'll probably be very surprised to know it will be about... Fable! Yes, the lightweight F# compiler that emits JavaScript you can be proud of (or at least I'll be). This time I want to talk you about one of the exciting new features introduced in [Fable 0.7](blog/Introducing-0-7.html): tree shaking.
10+
11+
[Read more...](blog/Tree-shaking.html)
12+
13+
## [Introducing 0.7](blog/Introducing-0-7.html)
614

715
> By [Alfonso García-Caro](https://twitter.com/alfonsogcnunez) on Nov 22, 2016
816

docs/source/blog/Tree-shaking.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
- tagline: Light webs load faster!
2+
3+
# Tree Shaking with Fable
4+
5+
Hi everybody! This is my first contribution to the [F# advent calendar](https://sergeytihon.wordpress.com/2016/10/23/f-advent-calendar-in-english-2016/) and you'll probably be very surprised to know it will be about... Fable! Yes, the lightweight F# compiler that emits JavaScript you can be proud of (or at least I'll be). This time I want to talk you about one of the exciting new features introduced in [Fable 0.7](blog/Introducing-0-7.html): tree shaking.
6+
7+
----------------
8+
9+
From the very fist version, Fable compiles F# code to JavaScript using [ES2015 features](https://babeljs.io/docs/learn-es2015/) like classes or iterators, and then uses [Babel](https://babeljs.io/) (hence the name) to convert the code to something old browsers don't have that much trouble understanding. Among ES2015 features we find a new [module system](http://www.2ality.com/2014/09/es6-modules-final.html) that it's been designed to standardize the different solutions that tried to emulate modules in JS so far, like [commonjs](https://nodejs.org/docs/latest/api/modules.html) or [amd](http://requirejs.org/docs/whyamd.html). But why create a new standard instead of taking one of existing ones?
10+
11+
![How standards proliferate](img/standards.png)
12+
13+
> Image courtesy of [xkcd.com](https://xkcd.com/927/)
14+
15+
Because, both `commonjs` and `amd`, in the spirit of the JS language, allow for dynamic module patching so it's difficult to know what you are actually loading until runtime. ES2015 modules are designed so they can be **analyzed statically** (meaning _at compile time_). This has several applications but it's mostly important for bundlers. Now let's start a digression to talk about bundlers.
16+
17+
`<digression>`The JavaScript ecosystem has grown enormously in recent years and nowadays we can find a library or component for almost anything. However, JS being an interpreted language it has no compiler to link all the source files and loading them with `script` HTML tags becomes a nightmare in a rapidly changing development environment. Luckily, tools like [Webpack](https://webpack.github.io/) can pack all our sources into a single file for easy deployment.`</digression>`
18+
19+
Bundlers are nice but the obvious gotcha is we need to be careful not to convert our app in a monster of several MB that takes forever to load. Bundlers and tools like [UglifyJS](http://lisperator.net/uglifyjs/) try to mitigate this by removing **dead code**, however due to the commented dynamic nature of JS, many times it's not possible to know what it's actually used or not until runtime. Because of this, JS libraries compete to be as small in size as possible by specializing and resolving very specific problems. The result: JS apps with hundreds of dependencies and an ecosystem full of tiny packages where this year a single developer [broke the internet by retiring 11 lines of code from npm](http://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/).
20+
21+
## Individual imports and exports
22+
23+
[Rollup](http://rollupjs.org/) is a bundler that understands ES2015 modules and its author, Rich Harris, can explain much better than me [the problems of small modules](https://medium.com/@Rich_Harris/small-modules-it-s-not-quite-that-simple-3ca532d65de4#.bwwly4tk3) and [the advantages of tree shaking over dead code elimination](https://medium.com/@Rich_Harris/tree-shaking-versus-dead-code-elimination-d3765df85c80#.nnofvhkml), but I'll try to summarize them:
24+
25+
The killer feature of ES2015 modules is they allow to make **individual exports and imports**. This means you can reference a library without importing the whole package, only the functions you need. And most importantly, library authors are encouraged to structure their code having this in mind. Then, bundlers (and browsers soon) can easily eliminate anything that's not being imported. Consider this simplistic JS project and the generated bundle.
26+
27+
maths.js
28+
```js
29+
export function square ( x ) {
30+
return x * x;
31+
}
32+
33+
export function cube ( x ) {
34+
return x * x * x;
35+
}
36+
```
37+
38+
main.js (entry module)
39+
```js
40+
import { cube } from './maths.js';
41+
console.log( cube( 5 ) ); // 125
42+
```
43+
44+
bundle.js (`square` is excluded)
45+
```js
46+
function cube ( x ) {
47+
return x * x * x;
48+
}
49+
console.log( cube( 5 ) );
50+
```
51+
52+
Check Rollup's website to see how this works [live](http://rollupjs.org/).
53+
54+
Cool, isn't it? But... wait, what am I doing? I wanted to convince you to try Fable and here I am only talking about the niceties of ES2015 modules. Obviously, it's not possible for Fable to have any advantage regarding a feature that was specifically designed for JS and its ecosystem, right? Well, let's see. Challenge accepted!
55+
56+
## Autocompletion
57+
58+
ES2015 modules are very nice but there's one little problem: they make **exploratory programming** more difficult. We need to know the bits of the library we want to import in advance, which means we must check the documentation carefully to see what we actually need and we cannot use the tremendously productive autocompletion capabilities of IDEs like [Visual Studio Code](https://code.visualstudio.com/) to explore the API as we code. **Not even with TypeScript**, a statically typed language, as it uses the same syntax as ES2015 for importing modules.
59+
60+
Enter Fable (finally!). Provided you have a single root module per file, Fable will automatically export your public functions with ES2015 syntax. Check the following code and the generated JS:
61+
62+
```fsharp
63+
module MyNamespace.MyModule
64+
65+
let foo x =
66+
x + x
67+
68+
let bar y =
69+
y * y
70+
```
71+
72+
```JS
73+
export function foo(x) {
74+
return x + x;
75+
}
76+
77+
export function bar(y) {
78+
return y * y;
79+
}
80+
```
81+
82+
Noticed that? Fable doesn't create an inner module within `MyNamespace` but exposes the functions in `MyModule` directly to make the code compatible with ES2015 bundlers. Let's see how it works when we're consuming the code:
83+
84+
```fsharp
85+
#load "MyLib.fs"
86+
87+
open MyNamespace.MyModule
88+
89+
let private test() =
90+
let result = bar 5
91+
printfn "%i"
92+
93+
test()
94+
```
95+
96+
```JS
97+
import { fsFormat } from "fable-core/String";
98+
import { bar } from "./MyLib"
99+
100+
function test() {
101+
const result = bar(5);
102+
fsFormat("%i")(x => {
103+
console.log(x);
104+
})(result);
105+
}
106+
```
107+
108+
Fable is just importing the function we need, the way ES2015 modules are intended to be. The clear difference with raw JS here is we don't need to make the imports explicit: if we replace `bar` with `foo` Fable will change the import automatically. As you can see, this works not only for our code but also for functions in `fable-core` (the JS translation of FSharp.Core).
109+
110+
The example was just to show that opening a module doesn't import everything, but in order to get autocompletion we just need to qualify the module name instead. The generated JS code will be the same.
111+
112+
![Autocompletion](img/capture2.png)
113+
114+
This means we can do exploratory programming while still having all the advantages of tree-shaking: the best of two worlds! (my favourite sentence). And, as you probably know, F# has many tools to write and read documentation directly in code. With [Ionide](http://ionide.io/) you even get formatting for markdown comments!
115+
116+
![Markdown](img/capture1.jpg)
117+
118+
Have I convinced you yet to use the Fable + ES2015 bundler combo? Great! Now you just need to do is to find the right bundler, install it, learn how to configure it, make sure to run it after every Fable compilation... No, wait! [Fable 0.7 comes with Rollup embedded](http://fable.io/blog/Introducing-0-7.html#ES2015-Modules-and-Bundling) so the only thing you need to bundle your code and dependencies with tree shaking is:
119+
120+
```
121+
fable src/MyProject.fsproj --rollup
122+
```
123+
124+
And that's it!
125+
126+
----------------
127+
128+
Hope you liked the post and give Fable a try. Please check the web and the documentation (being updated to 0.7 at the moment) and come to the [Gitter channel](https://gitter.im/fable-compiler/Fable) if you have any question. Also make sure to follow the other posts of the awesome [F# advent calendar](https://sergeytihon.wordpress.com/2016/10/23/f-advent-calendar-in-english-2016/) and keep tuned with Fable's website and [Twitter](https://twitter.com/FableCompiler) for other exciting announcements!

docs/source/blog/img/capture1.jpg

27.1 KB
Loading

docs/source/blog/img/capture2.png

23.3 KB
Loading

docs/source/blog/img/standards.png

23.7 KB
Loading

0 commit comments

Comments
 (0)