Modified Proposal (by @dsyme)
This is a suggestion to add adhoc structural type-tagged union types where
- type syntax
(A|B) or Typed(A|B) or Typed<A,B>
- each type of (A|B|C|...) is distinct and non-overlapping w.r.t. runtime type tests
- such types are erased to
object
- introducing such a union value would need to be either explicit (e.g. using some new operator like
Typed) or type-directed or both
See #538 for original proposal. e.g. this would allow some or all of these:
let generateValue1 () : Typed(int | string)) = if Monday then 2 else "4"
let generateValue2 () = if Monday then Typed 2 else Typed "4"
let eliminateValue (x : Typed(int | string)) = ...
match x with
| :? int as i -> ...
| :? string as s -> ...
let eliminateValue2 x = ...
match x with
| Typed(i : int) -> ...
| Typed(s; string) -> ...
type Allowed = Typed(int | string)
There are plenty of questions about such a design (e.g. can you eliminate "some but not all" of the cases in a match? Is column-polymorphism supported?). However putting those aside, such a construct already has utility in the context of Fable, since it corresponds pretty closely to Typescript unions and how JS treats values. It also has lots of applications in F# programming, especially if the use of Typed can be inferred in many cases.
Now, this construct automatically gives a structural union message type , e.g.
type MsgA = MsgA of int * int
let update1 () = Typed (MsgA (3,4))
type MsgB = MsgB of int * int
let update2 () = Typed (MsgB (3,4))
val update1 : unit -> Typed MsgA // actually : unit -> Typed (MsgA | ..), i.e. column-generic on use
val update2 : unit -> Typed MsgB // actually : unit -> Typed (MsgB | ..), i.e. column-generic on use
and a combination of update1 and update2 would give
let update = combine update1 update2
val update : unit -> Typed (MsgA | MsgB)
As noted in the comments, some notion of column-generics would likely be needed, at least introduced implicitly at use-sites.
I propose we add erased union types as an F# first citizen. The erased union types already exist in Fable to emulate Typescript (non-labeled) union types:
http://fable.io/docs/interacting.html#Erase-attribute
Note that Fable allows you to define your custom erased union types, but this is because it's painful to type a generic one like U2.Case1. If the compiler omits the need to prefix the argument, this wouldn't be necessary and using a generic type can be the easiest solution.
The F# compiler could convert the following code:
// The name ErasedUnion is tentative
// The compiler should check the generic args are different
let foo(arg: ErasedUnion<string, int>) =
match arg with
| ErasedUnion.Case1 s -> s.Length
| ErasedUnion.Case2 i -> i
// No need to instantiate ErasedUnion, but the compiler checks the type
foo "hola"
foo 5
// This doesn't compile
foo 5.
Into something like:
let foo(arg: obj) =
match arg with
| :? string as s -> s.Length
| :? int as i -> i
| _ -> invalidArg "arg" "Unexpected type"
-
Pros: It will make the Fable bindings generated from Typescript declaration files much more pleasant to work with.
-
Cons: It's a feature that seems to be exclusively dedicated to interact with a dynamic language like JS.
-
Estimated cost (XS, S, M, L, XL, XXL): S
Alternatives
For Fable it's been suggested to generate overloads in the type bindings instead of using erased union types:
interface IFoo {
foo(arg: string | number): void;
}
type IFoo =
abstract foo: arg: string -> unit
abstract foo: arg: number -> unit
However these has some problems:
- It can quickly explode when you have several erased union arguments
- Due to type inference the F# compiler many times doesn't know which overload to use
- Cannot be used in properties
- Doesn't let you use erased unions yourself.
Affadavit
Please tick this by placing a cross in the box:
Please tick all that apply:
Modified Proposal (by @dsyme)
This is a suggestion to add adhoc structural type-tagged union types where
(A|B)orTyped(A|B)orTyped<A,B>objectTyped) or type-directed or bothSee #538 for original proposal. e.g. this would allow some or all of these:
There are plenty of questions about such a design (e.g. can you eliminate "some but not all" of the cases in a match? Is column-polymorphism supported?). However putting those aside, such a construct already has utility in the context of Fable, since it corresponds pretty closely to Typescript unions and how JS treats values. It also has lots of applications in F# programming, especially if the use of
Typedcan be inferred in many cases.Now, this construct automatically gives a structural union message type , e.g.
and a combination of update1 and update2 would give
As noted in the comments, some notion of column-generics would likely be needed, at least introduced implicitly at use-sites.
Original Proposal (@alfonsogarciacaro)
I propose we add erased union types as an F# first citizen. The erased union types already exist in Fable to emulate Typescript (non-labeled) union types:
http://fable.io/docs/interacting.html#Erase-attribute
Note that Fable allows you to define your custom erased union types, but this is because it's painful to type a generic one like
U2.Case1. If the compiler omits the need to prefix the argument, this wouldn't be necessary and using a generic type can be the easiest solution.The F# compiler could convert the following code:
Into something like:
Pros: It will make the Fable bindings generated from Typescript declaration files much more pleasant to work with.
Cons: It's a feature that seems to be exclusively dedicated to interact with a dynamic language like JS.
Estimated cost (XS, S, M, L, XL, XXL): S
Alternatives
For Fable it's been suggested to generate overloads in the type bindings instead of using erased union types:
However these has some problems:
Affadavit
Please tick this by placing a cross in the box:
Please tick all that apply: