dotnet-websharper / core

WebSharper - Full-stack, functional, reactive web apps and microservices in F# and C#
https://websharper.com
Apache License 2.0
594 stars 52 forks source link

Create proper JavaScript "classes" with properties #276

Open intellifactory-bb opened 9 years ago

intellifactory-bb commented 9 years ago

WebSharper still compiles to a weird format for JavaScript classes. ES6 has defined a class syntax, and an ES5 equivalent is now a known quantity. Can WebSharper now compile to proper JavaScript classes?

In particular, I find the current property compilation strange. JavaScript provides Object.defineProperty and Object.defineProperties to generate properties. Use these methods to create proper JavaScript properties from F# properties, e.g.:

#!fsharp
type TestType(value) =
    let mutable value = value
    member x.Value
        with get() = value
        and set(v) = value <- v
    member x.AddValue y = y + x.Value

should become:

#!javascript
// ES6
class TestType {
    constructor(value) {
        this._value = value;
    }

    get value() {
        return this._value;
    }

    set value(v) {
        this._value = v;
    }

    addValue(y) {
        return y + this._value;
    }
}

// ES5
var TestType = (function () {
    function TestType(value) {
        this._value = value;
    }

    Object.defineProperty(TestType.prototype, 'value', {
        get: function () { return this._value; },
        set: function (v) { this._value = v; },
        enumerable: true,
        configurable: true
    });

    TestType.prototype.addValue = function (y) {
        return y + this.value;
    };

    return TestType;
}());

NOTE the wrapper in the ES5 scenario is unnecessary but can be leveraged for inheritance scenarios.


intellifactory-bb commented 9 years ago

The main inconvenient I can see right now is that this would mean dropping support for Internet Explorer 8. I'm not sure its share has dropped enough yet for this to be a reasonable decision.

We would probably also need to be careful about translating explicit calls to get_* and set_* methods, although this is not show-stopping.


Original comment by: Loic Denuziere

intellifactory-bb commented 9 years ago

For what it's worth, I think that any move in this direction is a mistake. W# should be able to pick any representation that executes right. It should probably even not populate global namespace with W# symbols.

If you want to generate JS classes of a certain shape, you can write a W# library (combinators or DSL) to do this, since classes are first-class things.


Original comment by: Anton Tayanovskyy

intellifactory-bb commented 9 years ago

@tarmil, see the link above, which includes a polyfill.

@t0yv0, what would that look like? Also, why would you need a DSL to translate a class 1:1?


Original comment by: Ryan Riley

intellifactory-bb commented 9 years ago

You can change representation of properties, that's ok. In general, there're more issues like, are tuples func args or boxed (arrays)?

After some discussion with Ryan, it seems that the best possible world would be like this:

(1) for most types/classes/modules/methods, W# uses an internal undocumented representation strategy, the objective is for it to "just work" as close to F# semantics as possible. This frees the hands of the compiler to do what it needs to do for efficiency, small code size, etc. This is exactly my concern with pushing too many "JS standards" into internals of W# generated code - it constraints the compiler too much.

(2) some classes are specifically marked as "JS interop" classes. These are actually exported into JS namespace in a predictable way, and are callable from JS in predictable way. Only a subset of F# features is supported on these classes, and if there's no direct predictable mapping to JS, compiler simply fails.

This can go well with (optional?) contract checking on W#-JS boundaries.

It's a lot of work though from here to there, probably not feasible in a short run. I am just recording this for future reference.


Original comment by: Anton Tayanovskyy