-
Notifications
You must be signed in to change notification settings - Fork 22
Description
I propose we consider the merits of a spread operator for F#
...expr
It would be used for
- building records and anonymous records from spreadable things (subsuming
with
and this suggestion) - building object expressions from spreadable things (subsuming this suggestion)
- implementing interfaces from spreadable things (subsuming this suggestion)
- perhaps other things
Records
type A = {
X: int
Y: int
}
type B = {
Z: int
}
type C = {
X: int
Y: int
Z: int
Extra: string
}
let a = { X = 1; Y = 2 }
let b = { Z = 3 }
let c = { ...a; ...b Extra = "four" }
Anonymous records
let a = {| X = 1; Y = 2 |}
let b = {| Z = 3 |}
let c = {| ...a; ...b; Extra = "four" |}
Object expressions
type IA =
abstract A: int -> int
type IAB =
abstract A: int -> int
abstract B: int -> int
let a =
{ new IA with
member _.A x = x + 1 }
let b =
{ new IB with
...a
member _.B x = x - 1 }
Interface implementations
type IA =
abstract A: int -> int
type IAB =
abstract A: int -> int
abstract B: int -> int
type A() =
interface IA with
member _.A x = x + 1
type B(a: IA) =
interface IB with
...a
member _.B x = x - 1
The existing way of approaching this problem in F# is explicit re-delegation.
Technical details?
Fundamental technical questions are
- What is considered spreadable?
- Certainly objects, records, anon records
- But what of tuples, unions and anything with methods or properties
- What spreadable items are provided by a spreadable thing?
- Assume spreadable things always have nominal known type
- Certainly record fields
- Certainly object methods, properties.
- Possibly object events (very rarely used though)
- What of extension methods and properties?
- What about F# modules?
- Is any type information propagated into the spreadable things from the thing being constructed?
- What requirements emerge from the target of spreadable items?
- Assume target of spreadable items always have nominal known type
- How exactly are the spreadable items used to satisfy the requirements?
- How about generics?
- Are values of a SRTP-constrained type considered spreadable?
- Are values of a class-constrained generic type considered spreadable?
For example
- Can you spread a record into an object expression? (Yes)
- Can you spread an object into a record? (Yes)
- If an object has a generic method
M<T> : T -> T
, can that be used to implement a requirementM: int -> int
through type specialization? (TBD)
Patterns?
Should a spread operator be allowed in patterns? e.g.
match r with
| {| A = a; ...rest |} -> ...
Initially there would I think be no need for this.
Types?
How about type syntax? e.g.
type A = {
X: int
Y: int
}
type B = {
Z: int
}
type C = {
...A;
...B;
Extra: string
}
let a = { X = 1; Y = 2 }
let b = { Z = 3 }
let c = { ...a; ...b Extra = "four" }
Spreading record types into other record types seems tempting, but note this would not give rise to subtyping. But then can you spread object types into record types? Seems like a step too far?
Collections?
Spread operators in other languages usually work with
- collections
- tuples
- spreading a spreadable-thing as a group of named parameters x.SomeMethod(...record_of_args)
- ParamArray/params (to explicitly pass a collection as a ParamArray arg, which is currently done via inference)
Bat all of this is necessarily desirable for a spread operator in F#, though each should be considered and if they aren't supported good diagnostics should be given directing people to the right way to do things.
Pros and Cons
The advantages of making this adjustment to F# are conceptual efficiency, introduction of a littler structural matching
The disadvantages of making this adjustment to F# are
- additional feature
- some subtle corner cases
- won't work 100% as expected from other languages.
- it may lead to a cascading set or requirements for using spread operators and related structural manipulations in generic code.
Extra information
Estimated cost (XS, S, M, L, XL, XXL): L
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
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.