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
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
Language Challenges of Targeting Multiple Runtimes
Tomas Petricek, The Alan Turing Institute
tomasp.net | tomas@tomasp.net | @tomaspetricek