fsharp / fsharp/fslang-suggestions

Spread operator for F#

Open
#1,253 35 comments 94 reactions 0 assignees View on GitHub
approved-in-principle
Dominant language
No language data
Stars
373
Forks
21
PR merge metrics
No merged PRs in 30d

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](https://github.com/fsharp/fslang-suggestions/issues/1251#issue-1612658594))
* building object expressions from spreadable things (subsuming [this suggestion](https://github.com/fsharp/fslang-suggestions/issues/1245#issuecomment-1463911009))
* implementing interfaces from spreadable things (subsuming [this suggestion](https://github.com/fsharp/fslang-suggestions/issues/132))
* perhaps other things

##### Records

```fsharp
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

```fsharp
let a = {| X = 1; Y = 2 |}
let b = {| Z = 3 |}
let c = {| ...a; ...b; Extra = "four" |}
```

##### Object expressions

```fsharp
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

```fsharp
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`, can that be used to implement a requirement `M: int -> int` through type specialization? (TBD)

### Patterns?

Should a spread operator be allowed in patterns? e.g.

```fsharp
match r with
| {| A = a; ...rest |} -> ...
```

Initially there would I think be no need for this.

### Types?

How about type syntax? e.g.

```fsharp
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:
* [x] This is not a question (e.g. like one you might ask on [stackoverflow](http://stackoverflow.com)) and I have searched stackoverflow for discussions of this issue
* [x] I have [searched both open and closed suggestions on this site](http://github.com/fsharp/fslang-suggestions/issues) and believe this is not a duplicate
* [x] 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:
* [x] This is not a breaking change to the F# language design
* [x] 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 :+1: emoji on this issue. These counts are used to generally order the suggestions by engagement.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the proposal's records, anonymous records, object expressions, and interface-implementation examples, then work through its questions about spreadable types, members, generics, patterns, types, and collections. Done would require a settled scope and language-design decision; no implementation files or tests are named in the issue.

Written by the indexing model from the issue text.

Assessment

Tech stack
fsharp
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.