Search code examples
f#websharper

Make WebSharper generate simple field access


I need a type that will be translated into a plain JS object so that F# field access will be translated into simple JS field access (this is necessary, since the object will be sent through postMessage, so it'll lose all its methods).

Again, I need

let x = a.b

to be translated into

var x = a.b;

without any method calls.

Here is a slightly modified example from the F# Reference:

namespace T

open IntelliFactory.WebSharper

[<JavaScript>]
module A =
    type MyClass =
        val a : int
        val b : int
        new(a0, b0) = { a = a0; b = b0; }

    let myClassObj = new MyClass(35, 22)
    let x = myClassObj.b

This won't translate with

x: error : Failed to translate property access: b.'

Ok, let's make those vals mutable:

namespace T

open IntelliFactory.WebSharper

[<JavaScript>]
module A =
    type MyClass =
        val mutable a : int
        val mutable b : int
        new(a0, b0) = { a = a0; b = b0; }

    let myClassObj = new MyClass(35, 22)
    let x = myClassObj.b

This will be successfully translated, but… MyClass.New returns an empty object. The question now starts looking much like a bugreport, right? So, back to the question.

Are there any other ways to achieve what I want?


Solution

  • There are additional issues with record-style constructors "{x = y}". I will have to look into this again on F# 3.0, the older F# did not produce sensible quotations for those and we did some partial workarounds in WebSharper. Right now your example breaks. So here is the working code with a static method instead of a constructor:

    type MyClass private () =
    
        [<DefaultValue>]
        val mutable a : int
    
        [<DefaultValue>]
        val mutable b : int
    
        static member Create(a0, b0) =
            let c = MyClass()
            c.a <- a0
            c.b <- b0
            c
    
    let test () =
        let myClassObj = MyClass.Create(35, 22)
        let x = myClassObj.a
        let y = myClassObj.b
        JavaScript.Log(x, y)
    

    Trivially, a record would also work.

    In some cases where you want to go really low-level you can annotate members with the Inline attribute. When this is too much overhead you can use untyped API:

    let x = obj ()
    x?a <- 1
    let y = x?a
    JavaScript.Log(x, y)