Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Prototyping Erased Union types #10566

Closed
wants to merge 17 commits into from
Closed

WIP: Prototyping Erased Union types #10566

wants to merge 17 commits into from

Conversation

Swoorup
Copy link
Contributor

@Swoorup Swoorup commented Nov 29, 2020

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:
type I = interface end
type IA = interface end
type IB = interface end

type AA = AA of int interface IA
type BB = BB of int interface IB
let aa = AA(10)
let aabb: (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)
     let formUnion<'a, 'b when 'a :> ('a |'b) and 'b :> ('a | 'b)> (a: 'a) (b: 'b) = a :> ('a| 'b)
     let formUnion2<'a, 'b> (a: 'a) (b: 'b) = a :> ('a| 'b) // yields constraint issue

Links:

@Swoorup
Copy link
Contributor Author

Swoorup commented Dec 3, 2020

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.

src/fsharp/TypedTreeOps.fs Outdated Show resolved Hide resolved
src/fsharp/TypedTreeOps.fs Outdated Show resolved Hide resolved
src/fsharp/TypedTreeOps.fs Outdated Show resolved Hide resolved
src/fsharp/TypedTreeOps.fs Outdated Show resolved Hide resolved
@dsyme
Copy link
Contributor

dsyme commented Jan 12, 2021

@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
Contributor

dsyme commented Jan 18, 2021

@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-widen January 18, 2021 16:19
@dsyme
Copy link
Contributor

dsyme commented Jan 18, 2021

@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:

let f1 (x : (int|string|int64)) = printfn "x = %A" x

f1 1
f1 1L
f1 "z"

let f2 (x : (int|string)) = f1 x

f2 1
f2 "z"

let data : (int|string)[] = [| 1; "a"; 4; |]

printfn "data = %A" data

let v = (1 : (int | string))
let v2 : (int | string) = 1
let f() : (int | string) = Unchecked.defaultof<int>

let data2 : (string * (int|string))[] = [| ("a", 1); ("b", ("a" : (int | string)) ) |]

printfn "data2 = %A" data2

@dsyme
Copy link
Contributor

dsyme commented Jan 19, 2021

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

@dsyme
Copy link
Contributor

dsyme commented Jan 19, 2021

@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
Contributor Author

Swoorup commented Jan 19, 2021

@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
Contributor

dsyme commented Jan 19, 2021

Closing in favour of #10896

Work will now be on feature/erased-unions

@dsyme dsyme closed this Jan 19, 2021
Sign up for free to 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