val render : canv:'a -> Async<unit>

Full name: index.render
val canv : 'a
val async : AsyncBuilder

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
val ctx : obj
val img : obj
val x : int
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
val y : int
val x' : float
Multiple items
val float : value:'T -> float (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.float

--------------------
type float = System.Double

Full name: Microsoft.FSharp.Core.float

--------------------
type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>
val snd : tuple:('T1 * 'T2) -> 'T2

Full name: Microsoft.FSharp.Core.Operators.snd
val fst : tuple:('T1 * 'T2) -> 'T1

Full name: Microsoft.FSharp.Core.Operators.fst
val y' : float
val it : obj
Multiple items
type Async
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitTask : task:Task -> Async<unit>
static member AwaitTask : task:Task<'T> -> Async<'T>
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
static member Ignore : computation:Async<'T> -> Async<unit>
static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T
static member Sleep : millisecondsDueTime:int -> Async<unit>
static member Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions * ?cancellationToken:CancellationToken -> Task<'T>
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartChildAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions -> Async<Task<'T>>
static member StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken -> unit
static member SwitchToContext : syncContext:SynchronizationContext -> Async<unit>
static member SwitchToNewThread : unit -> Async<unit>
static member SwitchToThreadPool : unit -> Async<unit>
static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
static member CancellationToken : Async<CancellationToken>
static member DefaultCancellationToken : CancellationToken

Full name: Microsoft.FSharp.Control.Async

--------------------
type Async<'T>

Full name: Microsoft.FSharp.Control.Async<_>
static member Async.Sleep : millisecondsDueTime:int -> Async<unit>
val unbox : value:obj -> 'T

Full name: Microsoft.FSharp.Core.Operators.unbox
union case Option.Some: Value: 'T -> Option<'T>
val canv : obj
union case Option.None: Option<'T>
val enigma : string

Full name: index.enigma
val whitehouse : string

Full name: index.whitehouse
val schema : string

Full name: index.schema
val work : unit -> Async<unit>

Full name: index.work
val res : obj
val people : obj
val spec : obj
static member Async.StartImmediate : computation:Async<unit> * ?cancellationToken:System.Threading.CancellationToken -> unit
type VegaData =
  {values: obj [];}

Full name: index.VegaData
VegaData.values: obj []
type obj = System.Object

Full name: Microsoft.FSharp.Core.obj
type VegaSpec =
  {$schema: string;
   width: int option;
   height: int option;
   mark: string;
   data: VegaData;
   encoding: obj;}

Full name: index.VegaSpec
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
VegaSpec.width: int option
type 'T option = Option<'T>

Full name: Microsoft.FSharp.Core.option<_>
VegaSpec.height: int option
VegaSpec.mark: string
VegaSpec.data: VegaData
VegaSpec.encoding: obj
type Vega =
  interface
    abstract member embed : string * VegaSpec -> unit
  end

Full name: index.Vega
abstract member Vega.embed : string * VegaSpec -> unit

Full name: index.Vega.embed
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
val vega : Vega

Full name: index.vega
val failwith : message:string -> 'T

Full name: Microsoft.FSharp.Core.Operators.failwith
val jsonParse : str:string -> 'R

Full name: index.jsonParse
val str : string
val chart : 'a
val x : VegaSpec

Full name: index.x

Language Challenges of
Targeting Multiple Runtimes

Tomas Petricek, The Alan Turing Institute
tomasp.net | tomas@tomasp.net | @tomaspetricek

F# as a multi-runtime language

The multi-runtime reality

  • F# was built for .NET
  • F# compilers to JavaScript
  • F# subsets runs on GPU, SQL, etc.

The unrealistic specification
The specification assumes .NET runtime.

Deep targetting

  • Keep semantics
  • Complicates interop
  • Can keep ecosystem

Shallow targetting

  • Relax semantics
  • Makes interop easy
  • Harder portability

Semantic challenges

When F# does not match the F# specification


DEMO
Rendering a fractal using Fable

Rendering a fractal using Fable

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
let render (canv:HTMLCanvasElement) = async {
  let ctx = canv.getContext_2d()
  let img = ctx.createImageData(U2.Case1 width, height)

  for x in 0 .. int width - 1 do
    for y in 0 .. int height - 1 do
      let x' = (float x / width * (snd w - fst w)) + fst w
      let y' = (float y / height * (snd h - fst h)) + fst h
      let it = countIterations palette.Length x' y'
      setPixel img x y width palette.[it]

    do! Async.Sleep(1)
    ctx.putImageData(img, 0.0, 0.0) }

match unbox (document.getElementById("go")) with 
| Some canv -> render canv
| None -> ()

Interesting aspects

Using nice F# features

  • Async for background processing
  • Option to handle missing element
  • Integer and float numeric types

Interesting aspects

They behave in JavaScript way

  • Async doesn't use background threads
  • Option types are erased using nulls
  • Computation uses JavaScript numbers

Semantic challenges

When F# does not match the F# specification


Option types

F# on .NET
An instance of Option<T> class

F# on JavaScript
Valid object or null value

Option types

Benefits
Can use pattern matching on JavaScript values!

Drawbacks
At runtime None == Some(null)

Numerical types

F# on .NET
A range of float, int, uint, etc.

F# on JavaScript
JavaScript numerical type

Numerical types

Benefits
Simulating full .NET behavior is hard!
Fable simulates integer / and %

Drawbacks
Numerical computations will differ

Asynchronous workflows

F# on .NET
Runs work items using thread pool

F# on JavaScript
Cooperative multi-tasking on single thread

Asynchronous workflows

Benefits
Can reuse code using familiar library
Strictly speaking satisfies the spec

Drawbacks
Lack of threads might cause deadlocks
Async.RunSynchronously unsupported

Unsafe downcasts

F# on .NET
Object type checked at cast point

F# on JavaScript
All checks delayed to member access

Unsafe downcasts

Benefits
Keep runtime manageable
Interop with native JS objects

Drawbacks
JavaScript-style error behaviour

Further semantic challenges

Exceptions
JavaScript can throw anything, .NET cannot

Tail-calls
.NET supports them, JavaScript does not

Arrays
.NET has fixed length, JS does not

Interop challenges

Making F# talk to JavaScript


DEMO
Visualizing Whitehouse salaries

Visualizing Whitehouse salaries

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
let enigma = "https://api.enigma.io/v2/data/<key>"
let whitehouse = enigma + "/us.gov.whitehouse.salaries.2016"
let schema = "https://vega.github.io/schema/vega-lite/v2.0.json"

let work () = async {
  let! res = Http.Request("GET", whitehouse + "/sort/name+/limit/20")
  let people = jsonParse(res)?result
  let spec = 
    { ``$schema`` = schema
      width = Some 800; height = Some 800
      data = { values = unbox people }
      mark = "bar"
      encoding = 
        { y = { field = "name"; ``type`` = "ordinal"}
          x = { field = "salary"; ``type`` = "quantitative"} } }
  vega.embed("#chart", spec, {actions = false}) }

work () |> Async.StartImmediate

Interesting aspects

Using nice F# features

  • Asynchronous workflows for REST calls
  • F# records and types calling Vega
  • Dynamic operator for member lookup

Interesting aspects

They behave in JavaScript way

  • Cannot wait and block an async
  • Types serve merely as a façade
  • Unchecked member lookup can fail

Interop challenges

Making F# talk to JavaScript


Façade F# records and interfaces

Importing the Vega library API

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
type VegaData = 
  { values : obj[] }

type VegaSpec = 
  { ``$schema`` : string
    width : int option
    height : int option
    mark : string
    data : VegaData
    encoding : VegaEncoding }

type Vega =
  abstract embed : string * VegaSpec -> unit

Façade F# records and interfaces

Shallow targetting
.NET member calls compile as JS member calls

Deep targetting
Members could be compiled as lookup table

Inline JavaScript annotations

Defining global values and functions

1: 
2: 
3: 
4: 
5: 
[<Emit("vega")>]
let vega : Vega = failwith "JS only"

[<Emit("JSON.parse($0)")>]
let jsonParse<'R> (str:string) : 'R = failwith "JS Only"

Inline JavaScript annotations

Shallow targetting
Direct mapping to JS functionality

Deep targetting
Leads to unportable libraries

Dynamic object access and creation

Access any member dynamicaly

1: 
2: 
chart?size(768,480)?on("renderlet", fun chart ->
    (* do something *) )

Create empty object dynamically

1: 
2: 
let x = createEmpty<Size>
x.width <- 800

Dynamic object access and creation

Shallow targetting
Special handling of dynamic constructs

Deep targetting
Cannot create empty object

Undefined behaviour

What should the F# specification look like?


Undefined behaviour in C

Behaviour is left undefined not to allow optimisation, but as an inevitable consequence of two things. Firstly, the program containing it is an error—the language implementation is not required to support it. Secondly, no behaviour for detecting or handling the error can be prescribed in all cases.

Stephen Kell (2017)
Some Were Meant for C

Deep targetting

Minimize cases with undefined behaviour

Shallow targetting

Leave undefined or under-defined for flexibility

Undefined behaviour

Leaves room for platform-specific implementation

  • Fine properties of primitive types
  • Anything involving null value
  • "Unsafe" operations like casts
  • What erroneous member access does

What is a language?

F# compiled to JavaScript breaks the spec!

  • ... but not the spirit!
  • It is safe thanks to JavaScript
  • Well-behaved core code works
  • Useful spec should be runtime-neutral

Summary

Multi-runtime programming languages


How can we compile one language to JVM, SQL, .NET, LLVM,
JavaScript, Squeak, GPUs, Arduino and other IoT platforms?

Allow some under-defined behavior

  • Minimum for portability ("deep")
  • A lot for integration ("shallow")

Escape hatches for good integration

  • Explicit encoding of escape hatches ("deep")
  • Dynamic language features ("shallow")
  • Flexible or misusable object model ("shallow")
  • Allow arbitrary unchecked effects ("shallow")

Discussion! & Questions?

References

Noble & Biddle (2002). Notes on postmodern programming

Petricek, Syme & Bray (2014). In the Age of Web:
Typed Functional-First Programming Revisited

Stephen Kell (2017). Some Were Meant for C

Tomas Petricek, The Alan Turing Institute
tomasp.net | tomas@tomasp.net | @tomaspetricek