-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow the union pattern to be implemented explicitly #1152
Comments
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. 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) |
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: #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 👍 emoji on this issue. These counts are used to generally order the suggestions by engagement.
The text was updated successfully, but these errors were encountered: