Fable: Attached members minification in Fable 3

Created on 13 Jun 2020  路  7Comments  路  Source: fable-compiler/Fable

Being a dynamic language, JS bundlers cannot minify the name of object fields without breaking code. However this should be possible for a typed language. It was for example one of the main features in Elm 0.19.

We decided not to try that in Fable because we were using interfaces to interact with JS. But if we're going to use the NoMangle attribute to mark specifically interfaces that are meant for JS, we could minify the names of other interface members (as they are already mangled anyways).

The proposal is (in non-debug compilations):

  • Minify the names of interface members.
  • Minify also the names of non-anonymous record fields? (for this we would have to allow the NoMangle attribute in records too, or disallow using records to interact with JS).
  • Avoid conflicts with names of private fields in classes
optimization

Most helpful comment

The idea of mangling interface names was not originally meant for minification but to avoid name conflicts (see #2068). I just thought that by explicitly marking the interfaces (I picked NoMangle as in Rust because I found External as in Bucklescript a bit ambiguous) that are used for interop we could solve this problem and also open the door to other optimizations (like minification or tree-shaking). But you're definitely right that easy JS interop has been one of the selling points of Fable and changing that now could be frustrating for users.

It's also true that we've been living until now with the potential of name conflicts for abstract members, so having a Mangle attribute to explicitly mark interfaces not intended to be used for interop should hopefully work :+1: In order to solve @ncave's issues with System.IEquatable and the like we could extend the rule by mangling all interfaces from System namespace by default.

All 7 comments

Given how great Fable is to interop with Javascript, this "feature" would bring it a step backwards IMO. It is always good see that the compiled names of the records/interfaces are exactly the same at runtime so you could simply unbox as input to some 3rd-party library.

During development, if I am testing fiddling with a binding and interop code until it works, suddenly it will break on a full-build because of the minification.

An optimization like #2088 will have far more significance on the output of the generated bundle without introducing surprises for developers

Those are good points @Zaid-Ajaj :+1: Just remember for Fable 3 we're planning to mangle the name of the interfaces, so it won't be possible to use them for JS interop anyways unless they're decorated with NoMangle.

Instead of mangling by default and NoMangleAttribute, would it instead make sense to add MangleAttribute?

Then the compiler could warn where you have naming clashes and point you to use the attribute on one or more of the interfaces.

I also really like the good interop by default.

for Fable 3 we're planning to mangle the name of the interfaces, so it won't be possible to use them for JS interop anyways

@alfonsogarciacaro I don't know if this is really a good idea. It might make sense for Elm where interop is extremely limited but for Fable that is a powerful feature that will be affected. Just imaging right now how many people will update their fable-compiler versions to realise that many of their dependant libraries don't work with latest Fable because of the name mangling.

@0x53A Yes! The MangleAttribute sounds like a much better approach where optimization becomes a deliberate choice of the developer when they know for sure that no interop is involved. And it won't affect existing libraries since authors can choose to optimize the generated bundle by adding [<Mangle>] attribute in places they know interop is not affected.

Just a side-note: [<MinifyMemberNames>] or [<MinifyFields>] sounds clearer than [<Mangle>]

The idea of mangling interface names was not originally meant for minification but to avoid name conflicts (see #2068). I just thought that by explicitly marking the interfaces (I picked NoMangle as in Rust because I found External as in Bucklescript a bit ambiguous) that are used for interop we could solve this problem and also open the door to other optimizations (like minification or tree-shaking). But you're definitely right that easy JS interop has been one of the selling points of Fable and changing that now could be frustrating for users.

It's also true that we've been living until now with the potential of name conflicts for abstract members, so having a Mangle attribute to explicitly mark interfaces not intended to be used for interop should hopefully work :+1: In order to solve @ncave's issues with System.IEquatable and the like we could extend the rule by mangling all interfaces from System namespace by default.

I just found that terser has a --mangle-props option, so you can mangle the fields of all objects in the final bundle that already includes the JS dependencies.

I love when we don't need to implement something by ourselves :)

Well, it seems the --mangle-props option is not reliable, but it's going to be difficult to implement it on our end too so we'll consider this to be low priority for Fable 3 and only reopen it if we manage to fix the other issues in time.

Was this page helpful?
0 / 5 - 0 ratings