[RFC FS-1151] Spread operator for F# #806
Replies: 6 comments 43 replies
-
Type definitionsSubthread for discussing spreads in type declarations/definitions. Record type spreadsIn record type declarations, type R1 = { A : int; B : int }
type R2 = { C : int }
type R3 = { ...R1; ...R2; D : int } Duplicate field names derived from spreads are allowed in type declarations. Duplicate field names arising from explicit field declarations remain disallowed. type R1 = { A : int }
type R2 = { ...R1; A : string } // Allowed.
type R3 = { A : int; A : string } // Still not allowed. The rightmost instance of a field with a given name, whether from explicit declaration or type spread, wins out and shadows any leftward fields with the same name. type R1 = { A : int }
type R2 = { ...R1; A : string } // `A : string` wins out over `A : int` from `...R1`. type R1 = { A : int }
type R2 = { A : string }
type R3 = { ...R1; ...R2 } // `A : string` from `R2` wins out over `A : int` from `R1`. A suppressible warning is emitted when the intent is less clear, e.g., when a field from a spread shadows a field with the same name that is explicitly declared to the left: type R1 = { A : int }
type R2 = { A : string; ...R1 }
------------------------^^^^^
warning FSXXXX: Field 'A: int' from spread type 'R1' shadows the explicitly declared field 'A: string` with the same name. Generic type parameters must be passed through to spreads: type R1<'a> = { A : 'a; B : string }
type R2<'a> = { X : 'a; Y : string }
type R3<'a> = { ...R1<'a>; ...R2<'a> } type R1<'a> = { A : 'a; B : string }
type R2<'a> = { X : 'a; Y : string }
type R3<'a, 'b> = { ...R1<'a>; ...R2<'b> } Attributes on generic type parameters must match: type R1<[<Measure>] 'a> = { A : int<'a> }
type R2<[<Measure>] 'a> = { ...R1<'a> } Constraints on generic type parameters must match: type R1<'a when 'a : comparison> = { A : 'a }
type R2<'a when 'a : comparison> = { ...R1<'a> } Interface implementation spreadsType definitionstype IA =
abstract A: int -> int
type IA =
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 This should also probably work with a self-alias (thanks for bringing the scenario up, @Happypig375): type IA =
abstract A: int -> int
type IB =
abstract A: int -> int
abstract B: int -> int
type B () as this =
member _.A x = x + 1
interface IA with ...this // uses the above implementation of A
interface IB with
...this // uses the above implementation of A
member _.B x = x - 1 Object expressionsSpreads in object expressions would work similarly to how they would in type definitions: type IA =
abstract A: int -> int
type IA =
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 } |
Beta Was this translation helpful? Give feedback.
-
ExpressionsSubthread for discussing expression-level spreads. Spreads into nominal recordsSpreads into nominal records are constrained by the set of fields defined in the target nominal record type. Missing fields in the source expression's/expressions' type(s) must be specified explicitly; extra fields in the source expression's/expressions' type(s) are ignored. 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" } From other nominal or anonymous recordsFrom other object typesTODO Spreads into anonymous recordsFrom other nominal or anonymous recordstype R1 =
{ A : int
B : string }
member _.Lol = 99
type R2 = { ...R1; C : string }
let r1 = { A = 3; B = "4"; Q = 99 }
let r2 = { A = 5; B = "6"; C = "haha" }
let r1AndR2 = {| ...r1; ...r2 |} From other object typesTODO Spreads in collection or computation expressionsIn collection expressions and computation expressions, let xs = [10..20]
let ys = [1; 2; 3; ...xs] ≡ let xs = [10..20]
let ys = [1; 2; 3; yield! xs] While More (TBD)... |
Beta Was this translation helpful? Give feedback.
-
Patterns (?)Subthread for discussing pattern-level spreads. RecordsRecord patterns already allow specifying only a subset of fields and ignoring those unuttered. Would we allow Anonymous records?fsharp/fslang-suggestions#713 (comment) Could this be implemented via SRTPs? let f x =
match x with
| {| A = 3; B = 4; ... |} -> … let f x =
match x with
| {| A = 3; B = 4; ...rest |} -> … Tuples?let tuple = 1, 2, 3, 4, 5, 6, 7
let x, y, ...rest = tuple Arrays?match xs with
| [|...; 3; 4|] -> true
| _ -> false match xs with
| [|...rest; 3; 4|] -> f rest
| _ -> false Lists?We already have match xs with
| 3 :: 4 :: _ -> true
| _ -> false Would we add match xs with
| [3; 4; ...] -> true
| _ -> false ? What about match xs with
| [...; 3; 4] -> true
| _ -> false , which would be Or match xs with
| [...rest; 3; 4] -> f rest
| _ -> false ? More (TBD)... |
Beta Was this translation helpful? Give feedback.
-
Prefix |
Beta Was this translation helpful? Give feedback.
-
AlternativesSubthread for discussing alternatives: pros and cons.
|
Beta Was this translation helpful? Give feedback.
-
ToolingRendering & signatures of record types containing spreadsRenderingF# tooling currently lists the fields (or a truncated subset thereof) of a record type definition on hover or when passed to F# interactive. What should the tooling show for a record type that includes a spread? E.g., given type R1 = { A : int; B : int }
type R2 = { ...R1; C : int } should the tooling indicate that Recall that the relationship of In such a case, should the tooling show:
? SignaturesThe signature of a record built from spreads would surely be the fully expanded version with no mention of the underlying spreads, i.e., given the definition type R1 = { A : int; B : int }
type R2 = { ...R1; C : int } the signature would be type R1 = { A : int; B : int }
type R2 = { A : int; B : int; C : int } Code fixes & refactoringsWould it be feasible and worthwhile to offer a refactoring to convert a spread to explicit redefinitions or delegations? This could be useful if the developer decides that there should no longer be a compile-time relationship between the source of the spread and the target, but wishes to keep all or most of the definitions, implementations, or values implied by the spread. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Discussion thread for RFC FS-1151: #805
Language suggestion: fsharp/fslang-suggestions#1253
Proposed implementation: TODO
Subthreads
Links to subthreads below for focused discussion on subtopics:
..
versus...
Not everything mentioned here is equally likely to be a good idea to implement.
I am personally in favor of:
Beta Was this translation helpful? Give feedback.
All reactions