Skip to content

WIP: Prototyping Erased Union types - #10566

Closed
Swoorup wants to merge 17 commits into
dotnet:feature/auto-widenfrom
Swoorup:sj_erased_union_prototype
Closed

WIP: Prototyping Erased Union types#10566
Swoorup wants to merge 17 commits into
dotnet:feature/auto-widenfrom
Swoorup:sj_erased_union_prototype

Conversation

@Swoorup

@SwoorupSwoorup commented Nov 29, 2020

Copy link
Copy Markdown
Contributor

RFC https://github.com/fsharp/fslang-design/blob/master/RFCs/FS-1092-anonymous-type-tagged-unions.md

I am currently experimenting hacking the compiler to support union types. I'll admit I don't have much knowledge of the F# compiler and this is a merely an exploration to see if the features would fit well within F#.

What currently works:

  • Type Declaration (Commutativity and Associativity)
    type A = (int | (int64 | int16) is same as ((int | int64) | int16)
    type C = (int | string) same as (string | int)
  • Subtyping rules:
typeI=interfaceendtypeIA=interfaceendtypeIB=interfaceendtypeAA= AA ofintinterfaceIAtypeBB= BB ofintinterfaceIBletaa= AA(10)letaabb:(AA|BB) = aa
aabb :>(IA|IB)// should pass
aabb :>(IA|IB|string)// should pass
aabb :> I // should fail
aabb :> IA // should fail
aabb :>(IA|int)// should fail

What doesn't:

  • Some issues appear to exist when interacting with anonymous records.
  • No exhaustive match checks, no union types within match cases.
  • Would be nice not to have :> _ every time to convert to union type.
  • Naked Generics, following doesn't work (Should likely be disallowed)
    letformUnion<'a,'bwhen'a:>('a |'b)and'b:>('a | 'b)>(a:'a)(b:'b)= a :>('a| 'b)letformUnion2<'a,'b>(a:'a)(b:'b)= a :>('a| 'b)// yields constraint issue

Links:

@Swoorup

Copy link
Copy Markdown
ContributorAuthor

One thing that is bit bugging me, atm is how to support pattern matching on erased union since the :? <instance> could be a supertype which might not necessary belong in cases.

I'll do a few more experimentation how this could be supported.

Comment threadsrc/fsharp/ConstraintSolver.fs Outdated
Comment threadsrc/fsharp/ConstraintSolver.fs Outdated
Comment threadsrc/fsharp/ConstraintSolver.fs Outdated
Comment threadsrc/fsharp/TypedTree.fs
Comment threadsrc/fsharp/TypedTreeOps.fs Outdated
Comment threadsrc/fsharp/TypedTreeOps.fs Outdated
Comment threadsrc/fsharp/TypedTreeOps.fs
Comment threadsrc/fsharp/TypedTreeOps.fs
Comment threadsrc/fsharp/TypedTreeOps.fs Outdated
Comment threadsrc/fsharp/TypedTreeOps.fs Outdated
@dsyme

Copy link
Copy Markdown
Contributor

@Swoorup There's a lot of good foundational work here.

As I mentioned on twitter this will interact with widening, for example to get

let f () : (int|string) = if true then 1 else "z"

to widen both 1 and "z" to (int|string). Likewise it interacts with widening associated with calling functions, e.g. to get

let f (x : (int|string)) = ...
f 1
f "z"

to work. Plus there is the question of subtyping - which you've made a start on - used in member calls and explicit coercions for example, e.g.

type C() =
static member M (x : (int|string)) = ...
C.M 1
C.M "z"

These three things actually use slightly different mechanisms and all three need to be adjusted. The second is actually the hardest because it introduces constrained type variables to represent the flexibility in the use of f.

For (1), in the branch https://github.com/dotnet/fsharp/tree/experiment/widen-literals I started looking at our treatment of widening w.r.t. branching constructs. I'll take a look at resurrecting that.

@dsyme

Copy link
Copy Markdown
Contributor

@Swoorup I'm content with the operation of #10884 and will change this PR to target that branch, so it can assume that branch as a baseline.

I'll also do some work addressing the items I mentioned in code review.

@dsyme
dsyme changed the base branch from main to feature/auto-widenJanuary 18, 2021 16:19
@dsyme

Copy link
Copy Markdown
Contributor

@Swoorup I have pushed a set of changes that incorporates feature/auto-widen into this PR and checked that the two features play nicely together.

I'm really happy with how this is looking - much more testing is needed of course. If you'd like to hammer on this that would be great. I'll also need to start work on an RFC for feature/auto-widen.

Here is a set of working examples:

letf1(x :(int|string|int64))= printfn "x = %A" x
f1 1
f1 1L
f1 "z"letf2(x :(int|string))= f1 x
f2 1
f2 "z"letdata:(int|string)[]=[|1;"a";4;|]
printfn "data = %A" data
letv=(1:(int | string))letv2:(int | string) =1letf():(int | string) = Unchecked.defaultof<int>letdata2:(string *(int|string))[]=[|("a",1);("b",("a":(int | string)))|]
printfn "data2 = %A" data2

@dsyme

Copy link
Copy Markdown
Contributor

@Swoorup I added a language feature flag so you will need /langversion:preview to use this now

@dsyme

Copy link
Copy Markdown
Contributor

@Swoorup I'd like to suggest we open a new PR from a feature branch feature/erased-unions. I will push the branch now and do that

@Swoorup

Copy link
Copy Markdown
ContributorAuthor

@Swoorup I'd like to suggest we open a new PR from a feature branch feature/erased-unions. I will push the branch now and do that

I agree. Liking how the progress on this has ramped up so quickly. It does seem straightforward. 😄
🚀 🚀 🚀

@dsyme

Copy link
Copy Markdown
Contributor

Closing in favour of #10896

Work will now be on feature/erased-unions

@dsymedsyme closed this Jan 19, 2021
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Swoorup@dsyme