Open dsyme opened 2 years ago
This possibly can enable better interop with "variant-style" unions e.g.
type JsonUnion = {
Tag: CaseEnum
Prop1: Case1 option (or nullable)
Prop2: Case2 option (or nullable)
Prop3: Case3 option (or nullable)
}
where it's guaranteed to have 1 of N cases at one time. It's closer to JS/C# folks usually do I guess. For example, telegram typically sends it's messages like that and I've used multiple ActivePatterns for better interop with C# Telegram library
module Update =
[<return: Struct>]
let inline (|Unknown|_|) (update: Update) =
match update.Type with
| UpdateType.Unknown -> ValueSome()
| _ -> ValueNone
[<return: Struct>]
let inline (|Message|_|) (update: Update) =
match update.Type with
| UpdateType.Message -> ValueSome update.Message
| _ -> ValueNone
[<return: Struct>]
let inline (|InlineQuery|_|) (update: Update) =
match update.Type with
| UpdateType.InlineQuery -> ValueSome update.InlineQuery
| _ -> ValueNone
[<return: Struct>]
let inline (|ChosenInlineResult|_|) (update: Update) =
match update.Type with
| UpdateType.ChosenInlineResult -> ValueSome update.ChosenInlineResult
| _ -> ValueNone
// etc
We could instead generate our own DU-like types for this kind of interop. For example, C# could just see it as Tag + Property but F# could see it as proper Union type.
Also, this could in theory provide perf-oriented people to write custom struct DU's with overlapped fields of any type (at own risk)
provide perf-oriented people to write custom struct DU's
This would be good not just for perf reasons but low-level interop in general. I'm working with a C API using overlapped unions implemented something like this:
[<StructLayout(LayoutKind.Explicit, Size = 24)>]
type Values =
struct
[<FieldOffset(0)>] val mutable a:float
[<FieldOffset(0)>] val mutable b:nativeint
[<FieldOffset(0)>] val mutable c:int
[<FieldOffset(0)>] val mutable d:SomeEnum
end
[<StructLayout(LayoutKind.Sequential)>]
type Union =
struct
val mutable value:Values
val mutable tag:UnionEnum
end
I then use partial active patterns and static members to safely read and write these structs. It would be nice to simplify this but I can understand it not being a high priority.
In F#, discriminated unions are a pattern, e.g.
generates code containing roughly this public API:
plus various attributes.
I propose it be possible to implement this pattern explicitly (unchecked) without committing to a representation of the backing data beyond the presence of subtypes A and B of the reference type U, and the presence of
Tag
/Tags
.This is quite a large feature:
Tag
andTags
even if pattern matching over a large number of union cases is slower for these cases (preferring type tests on the input type)Tags
need to form a contiguous0..n-1
listThe existing way of approaching this problem in F# is to accept the default representations for backing data.
Pros and Cons
The advantages of making this adjustment to F# are that programmers can control the ultimate representation of union types without subsequent consuming code changing and without breaking binary compatibility.
The disadvantages of making this adjustment to F# are that
On the whole I feel this is "too costly" to do for the benefits it brings but I am recording the idea here, partly because it clarifies what was meant by #726 and partly because it relates to #154 and #277
Extra information
Estimated cost (XS, S, M, L, XL, XXL): L
Related suggestions: https://github.com/fsharp/fslang-suggestions/issues/164 deals with some similar issues for records.
Affidavit (please submit!)
Please tick this by placing a cross in the box:
Please tick all that apply:
For Readers
If you would like to see this issue implemented, please click the :+1: emoji on this issue. These counts are used to generally order the suggestions by engagement.