Skip to content

Commit

Permalink
Update FS-1124-interfaces-with-static-abstract-members.md
Browse files Browse the repository at this point in the history
little tweaks
  • Loading branch information
smoothdeveloper authored Jun 25, 2022
1 parent bb55370 commit f12a88e
Showing 1 changed file with 20 additions and 23 deletions.
43 changes: 20 additions & 23 deletions RFCs/FS-1124-interfaces-with-static-abstract-members.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,20 +208,20 @@ However ambiguities can arise in the name resolution if several different unrela

This is a bit ugly and undiscoverable. However it has the huge advantage of allow very precise concretization of generic code, e.g. imagine the user writes:

```
GenericMathCode<'T when 'T : IMath<'T>> ( .... ) {
```fsharp
GenericMathCode<'T when 'T : IMath<'T>> ( .... ) =
blah //100s of lines
'T.Sin(...)
'T.Cos(...)
}
```

Then wants to accurately make this concrete to some specific type
```
ConcreteMathCode ( .... ) {

```fsharp
ConcreteMathCode ( .... ) =
blah //100s of lines
(Double :> IMath<'T>).Sin(...)
(Double :> IMath<'T>).Cos(...)
}
```

In this RFC we go with Option A+B, with the possibility of adding Option D at some later point.
Expand Down Expand Up @@ -288,16 +288,16 @@ At this point, any reader should stop to consider carefully the pros and cons he

### Drawback - Type-generic code is less succinct and less general than explicit function-passing code

Type-generic code relying on ISWAMs (and SRTP) can only be used with types that satisfy the constraints. If the types don't satisfy, you have a lot of trouble.
Type-generic code relying on IWSAMs (and SRTP) can only be used with types that satisfy the constraints. If the types don't satisfy, you have a lot of trouble.

```fsharp
type ISomeFunctionality<'T when 'T :> ISomeFunctionality<'T>>() =
static abstract DoSomething: 'T -> 'T

let SomeGenericThing<'T :> ISomeFunctionality<'T>> arg =
...
//...
'T.DoSomething(arg)
...
//...
SomeGenericThing<MyType1> arg1
SomeGenericThing<MyType2> arg2 // oh no, MyType2 doesn't have the interface! Stuck!
Expand All @@ -307,9 +307,9 @@ When the number of methods being abstracted over is small (e.g. up to, say, 10)

```fsharp
let SomeGenericThing doSomething arg =
...
//...
doSomething arg
...
//...
SomeGenericThing MyType1.DoSomething arg1
SomeGenericThing MyType2.DoSomethingElse arg2
Expand All @@ -321,9 +321,9 @@ For the vast majority of generic coding in F# the second technique is perfectly

### Drawback - Two ways to abstract

One specific drawback applies to F# - there are now two mechanisms to do type-level abstraction of patterns, ISWAMs and SRTP.
One specific drawback applies to F# - there are now two mechanisms to do type-level abstraction of patterns, IWSAMs and SRTP.

ISWAM:
IWSAM:

```fsharp
type IAddition<'T when 'T :> IAddition<'T>> =
Expand All @@ -333,15 +333,15 @@ let f1<'T when 'T :> IAddition<'T>>(x: 'T, y: 'T) =
'T.Add(x, y)
```

SRTP:
SRTP: (N.B: this simplified syntax is not part of the language yet)
```fsharp
let inline f2<^T when ^T : (static member Add: ^T * ^T -> ^T)>(x: ^T, y: ^T) =
let inline f2< ^T when ^T : (static member Add: ^T * ^T -> ^T) >(x: ^T, y: ^T) =
^T.Add(x, y)
```

These have pros and cons and can actually be used perfectly well together:
* **What is constrained.** ISWAM constrain the interfaces on the type, while SRTP constrains the members.
* **Satisfying constraints.** ISWAM require the interface be defined in the type. This massively restricts their use, and effectively makes them primarily usable by the BCL team. SRTP also only operate on the intrinsically defined members, though FS-1043 proposes to extend these to extension members.
* **What is constrained.** IWSAM constrain the interfaces on the type, while SRTP constrains the members.
* **Satisfying constraints.** IWSAM require the interface be defined in the type. This massively restricts their use, and effectively makes them primarily usable by the BCL team. SRTP also only operate on the intrinsically defined members, though [FS-1043](https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1043-extension-members-for-operators-and-srtp-constraints.md) proposes to extend these to extension members.
* **What can be generic.** SRTP can only be used in inlined F# code. This is currently enforced. For non-inlined code SRTP will only ever be implemented via witness passing. SRTP cannot realistically be used on the generic parameters of types.
* **Corner cases.** SRTP has many unusual rules for operators, and there are several known bugs with the mecahnism

Expand All @@ -362,24 +362,21 @@ No.
## Unresolved questions
[unresolved]: #unresolved-questions

* [ ] Decide the relationship between this and https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1024-simplify-constrained-call-syntax.md
* [ ] Decide the relationship between this and [RFC-1024](https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1024-simplify-constrained-call-syntax.md)

* [ ] Check carefully against https://github.com/dotnet/csharplang/blob/main/proposals/static-abstracts-in-interfaces.md
* [ ] Decide if the feature must be explicitly enabled before declaring new IWSAMs.

* [ ] We need to look carefully at a few things - e.g. IWSAM that define op_Implicit and op_Explicit. @dsyme says: I've done "the right thing" in the code but we will need to test it.

* [ ] This:

```
Just to note ^T.X   may have a conflict with ^expr in from-the-end-of-collection-slicing.
* [ ] `^T.X` may have a conflict with `^expr` in [from-the-end-of-collection-slicing](https://github.com/fsharp/fslang-design/blob/main/preview/FS-1076-from-the-end-slicing.md); That feature is only in preview so we can change this if needed.

```yacc
  | INFIX_AT_HAT_OP declExpr
    { if not (parseState.LexBuffer.SupportsFeature LanguageFeature.FromEndSlicing) then
        raiseParseErrorAt (rhs parseState 1) (FSComp.SR.fromEndSlicingRequiresVFive())
      if $1 <> "^" then reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsInvalidPrefixOperator())
      let m = (rhs2 parseState 1 2)
      SynExpr.IndexFromEnd($2, m) }
That feature is only in preview so we can change this if needed
```

0 comments on commit f12a88e

Please sign in to comment.