fsharp / fsharp/fslang-suggestions
Merge fields of the same type on multi-case union structs
- Dominant language
- No language data
- Stars
- 373
- Forks
- 21
- PR merge metrics
- No merged PRs in 30d
Description
# Merge fields of the same type on multi-case union structs
I propose we change the way multi-case union structs are represented.
Let's have the following as an example:
``` fsharp
[]
type MyType =
| Case1 of v1:int
| Case2 of v2:int
| Case3 of v3:int
```
Internally, this type is represented as __three__ integers, plus another one to indicate the union case.
My proposal is to make unions like this (structs where all cases are of the same type (or tuple of types)), internally _store the value __only once___, like the following example.
The existing way of approaching this problem in F# is this one, but it ruins type-safety:
``` fsharp
type MyTypeTag =
| Case1 = 1
| Case2 = 2
| Case3 = 3
[]
type MyType = {
Tag: MyTypeTag
Value: int
}
```
## Pros and Cons
The advantage of making this adjustment to F# is generation of more compact types. Struct multi-case unions are mainly used for low-level code where space efficiency is an advantage.
The disadvantage of making this adjustment to F# is ~~lack of backwards-compatibility~~. Actually, not at all. We would still enforce the unique names requirement for the structs, just they would have to be redirected to the same field, for C# consumers, or return the default element when accessing the wrong field for the wrong union case.
## Some other thoughts
* For unions with 256 cases or less, we could compact the data structures even more by storing the tag field as a byte. This would apply to all DU types in general, ~~but I am not sure about the backwards compatibility~~, we could still _expose_ them as 4-byte signed integers in a property for C# consumers. There might also be issues mith memory alignment and other black magic like that.
* For even more compactness, we could _partially_ merge the same-type elements of the DU cases as in the example:
``` fsharp
[]
type MyType =
| Case1 of i1:int * c1: char
| Case2 of i2:int
| Case3 of s3: string * i3:int
```
The type above could be represented as a struct with only three fields instead of five: one `int` for the `i1`, `i2` and `i3` fields, one string for `s3`, and one `char` for `c1`. And their position inside the tuple wouldn't matter at all. But this would make the proposal significantly more complex. And it would also raise questions about nested tuples and other stuff like that.
## Extra information
Estimated cost (XS, S, M, L, XL, XXL): S
## Affidavit (please submit!)
Please tick this by placing a cross in the box:
* [x] This is not a question (e.g. like one you might ask on [stackoverflow](http://stackoverflow.com)) and I have searched stackoverflow for discussions of this issue
* [x] I have [searched both open and closed suggestions on this site](http://github.com/fsharp/fslang-suggestions/issues) and believe this is not a duplicate
* [x] This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it.
Please tick all that apply:
* [x] This is not a breaking change to the F# language design
* [ ] I or my company would be willing to help implement and/or test this
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.