fable-the-what-why-and-how



fable-the-what-why-and-how

1 1


fable-the-what-why-and-how

Slides for Fable presentation

On Github alfonsogarciacaro / fable-the-what-why-and-how

module Stringfrom Microsoft.FSharp.Core
Multiple itemstype Format<'Printer,'State,'Residue,'Result> = PrintfFormat<'Printer,'State,'Residue,'Result>Full name: Microsoft.FSharp.Core.Format<_,_,_,_>--------------------type Format<'Printer,'State,'Residue,'Result,'Tuple> = PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple>Full name: Microsoft.FSharp.Core.Format<_,_,_,_,_>
val sprintf : format:Printf.StringFormat<'T> -> 'TFull name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
val ( Util.reverse works ) : unit -> 'aFull name: index.( Util.reverse works )
val res : obj
namespace Microsoft.FSharp.Core
val printfn : format:Printf.TextWriterFormat<'T> -> 'TFull name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val x : objFull name: index.x
val y : objFull name: index.y
val data : objFull name: index.data
union case Option.None: Option<'T>
val add : x:int -> y:string -> floatFull name: index.add
val x : int
Multiple itemsval int : value:'T -> int (requires member op_Explicit)Full name: Microsoft.FSharp.Core.Operators.int--------------------type int = int32Full name: Microsoft.FSharp.Core.int--------------------type int<'Measure> = intFull name: Microsoft.FSharp.Core.int<_>
val y : string
Multiple itemsval string : value:'T -> stringFull name: Microsoft.FSharp.Core.Operators.string--------------------type string = System.StringFull name: Microsoft.FSharp.Core.string
Multiple itemsval float : value:'T -> float (requires member op_Explicit)Full name: Microsoft.FSharp.Core.Operators.float--------------------type float = System.DoubleFull name: Microsoft.FSharp.Core.float--------------------type float<'Measure> = floatFull name: Microsoft.FSharp.Core.float<_>
val failwith : message:string -> 'TFull name: Microsoft.FSharp.Core.Operators.failwith
Multiple itemstype Test =  new : unit -> Test  member Invoke : args:int [] -> obj  member Item : float  member Item : float with setFull name: index.Test--------------------new : unit -> Test
member Test.Invoke : args:int [] -> objFull name: index.Test.Invoke
val args : int []
type obj = System.ObjectFull name: Microsoft.FSharp.Core.obj
val __ : Test
member Test.Item : float with setFull name: index.Test.Item
val set : elements:seq<'T> -> Set<'T> (requires comparison)Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.set
val v : float
type unit = UnitFull name: Microsoft.FSharp.Core.unit
module string_decoderfrom index
type NodeStringDecoder =  interface    abstract member detectIncompleteChar : buffer:'a0 -> float    abstract member write : buffer:'a0 -> 'a1  endFull name: index.string_decoder.NodeStringDecoder
abstract member NodeStringDecoder.write : buffer:'a0 -> 'a1Full name: index.string_decoder.NodeStringDecoder.write
abstract member NodeStringDecoder.detectIncompleteChar : buffer:'a0 -> floatFull name: index.string_decoder.NodeStringDecoder.detectIncompleteChar
val StringDecoder : NodeStringDecoderFull name: index.string_decoder.StringDecoder

Fable: the what, the why and the how

Bringing together the F# and JS worlds

meets

WHO IS MAKING THIS PRESENTATION?

Alfonso Garcia-Caro

  • Degree in Linguistics
  • Lived in Japan, Germany (back in Spain now)
  • 5 years .NET experience
  • 2 years F# and JS experience
  • Developed desktop, mobile and web apps for videogame, green energy and education industries

Ping me! @alfonsogcnunez

Working at toggl.com, the insanely simple time tracking app

Give it a try!

WHAT IS FABLE?

  • F# to JS compiler
  • Source maps
  • Plugin system
  • Framework agnostic
  • Batteries charged: F# core library and some .NET BCL

NOT AND AD-HOC LANGUAGE

  • F# is a general purpose language mainly targeting .NET
  • Not crafted specifically to compile to JS, like Dart, TypeScript or Elm
  • Allows you to reuse your knowledge: semantics, tooling and APIs

BUT BEHAVES LIKE ONE

  • No runtime
  • Compiles to clean JS code
  • Great interoperability with JS libraries
  • Adheres to standard practices and workflows
  • Compatible with JS developments tools: Babel, npm, WebPack

WHY FABLE?

JavaScript is evolving at rapid pace, do we need a new language?

ES2015 adds many great features and JS development tools keep getting better and better

But there are still things some programmers miss:

  • Static typing
  • Great editor support out of the box
  • Comprehensive core library

F# brings this and a couple more features...

  • REPL
  • Intellisense
  • Type inference
  • Custom operators
  • Whitespace indentation (optional)
  • Expression based programming
  • Pattern Matching
  • Active Patterns
  • Immutable by default
  • Partial application / Pipelines
  • Functional core library (LINQ on steroids!)
  • Observables
  • String formatting
  • Structural comparison
  • List, array and iterable comprehensions
  • Generics, unions, records and tuples
  • Type aliases
  • DSL embedded in the language
  • Typed-stateless Async programming
  • OOP: Inheritance, interfaces, abstract classes
  • Custom computation expressions
  • Overloading, type extensions
  • Type providers (coming to Fable)
  • Units of measure
  • Circular dependency restriction
  • Compiler directives
  • An excuse for being condescendant with non-functional programming
  • Don Syme retweeting you!

And the killer feature...

LEFT PADDING OUT OF THE BOX!

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
// Explicit
"3.14".PadLeft(10)                    // "      3.14"
"3.14".PadRight(10)                   // "3.14      "
"22".PadLeft(10, '0')                 // "0000000022"

// With .NET string formatting
String.Format("{0,10:F1}", 3.14)      // "       3.1"
String.Format("{0,-10:F1}", 3.14)     // "3.1       "

// With F# typed string formatting
sprintf "%10.1f" 3.14                 // "       3.1"
sprintf "%-10.1f" 3.14                // "3.1       "
sprintf "%+010i" 22                   // "+000000022"

HOW DOES FABLE WORK?

This is what a day in the life of a compiler looks like:

Parse and validate text into an Abstract Syntax Tree Make necessary transformations on that AST Generate new code from the AST: assembly, bytecode, JS...

As Fable works with known languages it can take advantage of existing tools.

F# compiler, like Roslyn, can be used as a service: we completed the first step for free!

Unfortunately JS is not a compiled language

...or is it?

Enter Babel

Babel generates an AST from ES2015 code, applies transformations with a pluggable system and generates ES5 JavaScript code.

Fable builds a bridge between F# and Babel AST delegating the reponsibility of code parsing and generation.

Fable adds its own AST for internal operations:

HOW CAN I USE FABLE?

Fable can be downloaded from npm

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
mkdir temp
cd temp
npm init --yes
npm install -g fable-compiler
npm install --save fable-core
echo "printfn \"Hello World\"" > hello.fsx
fable hello.fsx
node hello.js

Compiler options

Tailor compilation to your needs:

fableconfig.json

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
{
    "module": "commonjs",
    "outDir": "out",
    "sourceMaps": true,
    "projFile": "src/Fable.Samples.React.fsproj",
    "babelPlugins": ["transform-runtime"],
    "scripts": {
        "prebuild": "npm install",
        "postbuild": "node node_modules/webpack/bin/webpack"
    },
    "targets": {
        "debug": {
            "watch": true,
            "symbols": ["DEBUG"],
            "scripts": {
                "postbuild": "node out/server"
            }
        }
    }
}

DEMO

Console application

https://github.com/fsprojects/Fable/blob/master/samples/node/console/index.fs

TESTING

NUnit or Visual Studio tests can be compiled to JS too

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
#r "../../../packages/NUnit/lib/nunit.framework.dll"
#load "util/util.fs"

open NUnit.Framework

[<Test>]
let ``Util.reverse works``() =
    let res = Util.reverse "yllihP"
    Assert.AreEqual("Philly", res)

Compile the tests using NUnit plugin and run them with Mocha

1: 
2: 
3: 
4: 
5: 
fable samples/node/console/tests.fsx -m commonjs
    --outDir out --plugins build/plugins/Fable.Plugins.NUnit.dll

node build/tests/node_modules/mocha/bin/mocha
    samples/node/console/out/tests.js

INTERACTING WITH JS

We don't want to just intrude the JS ecosytem, we want to take advantage of its full potential.

Because .NET community is great

F# community is fantastic

JS community is...

HUGE

...and amazing too :)

DYNAMIC PROGRAMMING WITH FABLE

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
open Fable.Core

printfn "Value: %O" jsObj?myProp    // Property access

jsObj?myProp <- 5                   // Assignment

let x = jsObj?myMethod $ (1, 2)     // Application

let y = createNew jsCons (1, 2)     // Apply `new` keyword

let data =                          // JS literal object
    createObj [
        "todos" ==> Storage.fetch()
        "newTodo" ==> ""
        "editedTodo" ==> None
        "visibility" ==> "all"
    ]

JS MACROS

Use Emit attribute to emit JS code directly

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
open Fable.Core

[<Emit("$0 + $1")>]
let add (x: int) (y: string): float = failwith "JS only"

type Test() =
    // Rest arguments
    [<Emit("$0($1...)")>]
    member __.Invoke([<ParamArray>] args: int[]): obj =
        failwith "JS only"

    // Syntax conditioned to optional parameter
    [<Emit("$0[$1]{{=$2}}")>]
    member __.Item
        with get(): float = failwith "JS only"
        and set(v: float): unit = failwith "JS only"

FOREIGN INTERFACES

Define foreign interfaces easily to get the benefits of static checking and Intellisense

1: 
2: 
3: 
4: 
5: 
6: 
7: 
[<Import("*","string_decoder")>]
module string_decoder =
    type NodeStringDecoder =
        abstract write: buffer: Buffer -> strings
        abstract detectIncompleteChar: buffer: Buffer -> float

    let StringDecoder: NodeStringDecoder = failwith "JS only"

Use Import attribute to import external JS modules in ES2015 fashion

  • Native JS, Browser and Node interfaces are included in fable-core
  • More definitions can be found in npm: fable-import-xxx
  • A TypeScript parser (still in development) is also available: ts2fable

DEMO

Node static server

https://github.com/fsprojects/Fable/blob/master/samples/node/server/index.fsx

DEMO

Debugging a node express server with VS Code

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
{
    "name": "Launch Node",
    "type": "node",
    "request": "launch",
    "program": "${workspaceRoot}/samples/node/express/index.fsx",
    "outDir": "${workspaceRoot}/samples/node/express/out",
    "sourceMaps": true,
}

DEMO

Front end applications with JS frameworks: Vue, React

https://github.com/fsprojects/Fable/tree/master/samples/browser/todomvc

https://github.com/fsprojects/Fable/tree/master/samples/browser/react

FABLE IN NUMBERS

  • Currently in beta: v0.2.2 (soon v1.0.0)
  • >600 unit tests
  • >800 lines of documentation
  • >4500 lines of code of compiler core
  • 4 months of development (but building on FunScript experience)
  • 5th place after 2 months in fsprojects (Github stars)
  • >300 commits, >170 stars, 12 contributors
  • 6 packages in npm: compiler, core lib, bindings, TypeScript parser
  • 2500 downloads last month
  • 10,000 users 2016 Q4 (1)
  • 1,000,000 downloads 2016 Q4 (1)

(1) CMUF: Completely made up figures

SO WITH F# NOW YOU CAN DO...

  • Front-end apps
  • Node apps
  • Native iOS & Android apps with Xamarin
  • Mobile apps with Apache Cordova or React Native
  • Universal Windows Platform (soon)
  • Cross-platform desktop apps (Electron, .NET/Mono)
  • Server programming: Suave, ASP.NET
  • GPU programming
  • Functional programming on .NET

Thanks for listening!

Questions?

https://github.com/fsprojects/Fable

@alfonsogcnunez

Fable: the what, the why and the how Bringing together the F# and JS worlds meets