-
Notifications
You must be signed in to change notification settings - Fork 310
Description
When I saw the bundle size increase when compiling fulma-demo with Nagareyam I tried to find a solution for it. Then I remembered when we tried to turn unions into arrays (as Bucklescript does) for Fable 2 (but we couldn't if we wanted to keep full compatibility with F# unions). I came up with the idea of a new attribute to create EraseNamed
(tentative name) unions. This is explained below but the idea is to put the case name as the first element of the array.
In a quick prototype with a hack to turn all unions in Fulma
and Fable.React.Props
namespaces into EraseNamed unions the bundle size of fulma-demo went from 221K to 184K so closer to what we have now with Fable 2.
Erase unions
- Single case/single field (Fable 2): Erases to single value
Foo 5
->5
- Single case/multiple fields (new in Nagareyama): Erases to tuple (JS array)
Foo(5, "a")
->[5, "a"]
- Multiple cases/single fields (Fable 2): Erases to single value to emulate Typescript union types, union test converts to type testing.
- Multiple cases/none or multiple fields: Not allowed
EraseNamed (tentative name) unions
Same limitations as Erase unions: cannot implement interfaces or override ToString/Equals, no reflection info. They erase to a tuple where the first element is the case name: Foo(5, "a")
-> ["Foo", 5, "a"]
The main advantage is this allows for pattern matching, so we can perform most of the things we do with normal unions. Also, because we keep the case name, these unions are compatible with keyValueList
.
Pros
- The bundle size seems to decrease. This can have more effect in bigger apps. Maybe we can even get better results by erasing all the
Msg
unions in Elmish apps.
Cons
- We may be complicating things too much
- Easier to confuse Erase and EraseNamed unions. Both conceptually and also by making mistakes when adding the attribute.
- No matter how many times we warn about it, some users will still request/expect the erased unions to have the full power of "proper" unions.
Notes
- Still haven't figured out why erasing the unions makes for a smaller bundler, the code generated should be more or less the same (union constructors don't include the case name now). Removing the union class declarations definitely helps but not sure if this accounts for the whole difference. Maybe the minifier works better with tuples (JS array literals) because it can move them more agressively?
- At one point I broke the
ParamList
attribute and an empty array was emitted for the children of void elements (e.g.react.createElement("img", { src: "" }, [])
which made React fail... but it made the bundle smaller! 184K -> 167KB 😮 Still puzzled by that. - When gzipped, the difference is obviously smaller (maybe not worth the trouble?): 43K vs 39K
Thoughts? @ncave @Zaid-Ajaj @MangelMaxime