fable-compiler / Fable

F# to JavaScript, TypeScript, Python, Rust and Dart Compiler
http://fable.io/
MIT License
2.93k stars 301 forks source link

Should we allow `[<Erase>]` to works for members? #3929

Closed MangelMaxime closed 1 month ago

MangelMaxime commented 1 month ago

Description

I was looking into optimising the output of Oxpecker.Solid because it contains a lot of noise not used by the library (it is using Fable.CompilerPlugin to rewrite some of the output).

In theory, the generated noise should be removed by bundlers like Vite, etc. but not having it generating is even better IHMO. It makes the code easier to read.

open Fable.Core
open Fable.Core.JsInterop
open System

[<Erase>]
type input() =

    [<Erase>]
    member this.onChange
        with set (_: obj -> unit) = ()

generates

export function input__set_onChange_61A0B331(this$, _arg) {
}

This function is never used by Oxpecker output, so it would be nice to not generate it at all.

One way of doing it is by inlining the onChange member but by doing so Fable.AST doesn't contains information about onChange call anymore.

module Test

open Fable.Core
open Fable.Core.JsInterop
open System

[<Erase>]
type input() =

    [<Erase>]
    member inline this.onChange
        with set (_: obj -> unit) = ()

let x = input(onChange = fun _ -> ())

generates

export const x = (() => {
    let returnVal = input_$ctor();
    const this$ = returnVal;
    return returnVal;
})();

@ncave Do you see a danger in allowing [<Erase>] to apply to members?

One of them is that if someone apply [<Erase>] to a member and try to use that said member later it would fails at runtime but this is expected and is already the case when used on the type.

[<Erase>]
type MyClass() =
    class end

generates

export const x = MyClass_$ctor(); // MyClass_$ctor doesn't exist
ncave commented 1 month ago

@MangelMaxime For me [<Erase>] on type is mostly useful for types that have [<Emit>] members. But I can see extending this to cleanup more generated members. Perhaps a more concrete example can clarify the use, if you have one.

MangelMaxime commented 1 month ago

The use case in Oxpecker.Solid is that the user write:

[<SolidComponent>]
let MyButton =
    button(class' = "button", onChange=onChangeHandler){
        "My button"
    }

and then thanks to Oxpecker.Solid.FablePlugin the output becomes something like

const MyButton =
    <button class="button" onChange={onChangeHandler}>
        My button
    </button>

The properties on types like button are here only for the purpose of exposing the API to F#. This is kind of a new type of binding made for this specific syntax in mind.

We could perhaps add a custom attribute to Oxpecker.Solid.FablePlugin to flag such properties and remove them from the output. But I don't know if Fable plugin can remove code and not just modify it. This would also feel redundant with [<Erase>].