-
Notifications
You must be signed in to change notification settings - Fork 22
Description
I propose we allow ByRefLike and ReadOnly struct anonymous records.
You can create struct anonymous records as such today:
let f (r: struct {| IntVal: int |}) = r.IntVal
f {| IntVal=12 |}
// Or if you don't like structness inference: f struct {| IntVal=12 |}
However, there is no ability to make them ByRefLike, which means you cannot have them contain other ByRefLike structs such as Span<'T>
.
A possible declaration syntax might look like this:
// Attribute
let f ([<IsByRefLike>] r: {| S: Span<int> |}) = S.Length
// Keyword
let a (r: byref struct {| S: Span<int> |}) = S.Length
let b (r: struct byref {| S: Span<int> |}) = S.Length
let c (r: byreflike {| S: Span<int> |}) = S.Length // 'byrefLike' implies struct
It would be sort of nonsense to have you require an attribute to instantiate one, so a keyword would likely be needed:
a byref struct {| S = Span<int>.Empty |}
b struct byref {| S = Span<int>.Empty |}
c byreflike {| S = Span<int>.Empty |} // 'byrefLike' implies struct
Or perhaps structness inference would take care of the instantiation site:
// Inferred to by a ByRefLike struct anonymous record
a {| S = Span<int>.Empty |}
Similarly, there is no way to define a IsReadOnly
struct anonymous record. Possible declaration syntax:
// Attribute
let f ([<IsReadOnly>] r: {| F: FooReadOnly<int> |}) = S.Length
// Keyword
let a (r: readonly struct {| F: FooReadOnly<int> |}) = S.Length
let b (r: struct readonly {| F: FooReadOnly<int> |}) = S.Length
let c (r: readonly {| F: FooReadOnly<int> |}) = S.Length // 'readonly' implies struct
And an instantiation syntax:
a readonly struct {| F = FooReadOnly<int>.Empty |}
b struct readonly {| F = FooReadOnly<int>.Empty |}
c readonly {| F = FooReadOnly<int>.Empty |} // 'byrefLike' implies struct
Declaration syntax options for having both:
// Attribute declaration
let f ([<IsReadOnly; IsByRefLike>] r: {| S: ReadOnlySpan<int> |}) = S.Length
// Syntax declarations
let a (r: byreflike readonly struct {| S: ReadOnlySpan<int> |}) = S.Length
let b (r: struct byreflike readonly {| S: ReadOnlySpan<int> |}) = S.Length
let c (r: byreflike readonly {| S: ReadOnlySpan<int> |}) = S.Length // 'byrefLike' and 'readonly' imply struct
Instantiation syntax for having both:
a byref readonly struct {| R = ReadOnlySpan<int>.Empty |}
b struct byref readonly {| R = ReadOnlySpan<int>.Empty |}
c byreflike readonly struct {| R = ReadOnlySpan<int>.Empty |}
Structness inference could capture both IsByRefLike
and IsReadOnly
:
f {| R = ReadOnlySpan<int>.Empty |} // Only works if the input type is byref-like and readonly
Pros and Cons
Advantages:
- Parity with records
- Allows you to use these more ephemeral constructs in the perf-sensitive environments ByRefLike structs are intended for
Disadvantages:
- I can't think of an explicit instantiation syntax that doesn't kind of suck
- This can make an input parameter heavy syntax-wise
- These kinds of structs are done with attributes in F# today, but to enable them for anonymous records would require a keyword due to the lack of support for attributes on function parameters
- Allowing a keyword instead of an attribute is inconsistent with how you define ByRefLike structs today in F#
Extra information
Estimated cost (XS, S, M, L, XL, XXL): M
Affidavit (please submit!)
Please tick this by placing a cross in the box:
- This is not a question (e.g. like one you might ask on stackoverflow) and I have searched stackoverflow for discussions of this issue
- I have searched both open and closed suggestions on this site and believe this is not a duplicate
- 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:
- 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