Question

Consider the following code...

type TypeOne () =
    member val Name = "" with get, set

type TypeTwo () =
    member val Name = "" with get, set
    member val Property = 0 with get, set

[<RequireQualifiedAccess>]
type UnionType =
    | TypeOne of TypeOne
    | TypeTwo of TypeTwo

// this only succeeds because we have defined the union type a
// requiring qualified access. otherwise TypeOne would be inferred
// as the union case, not the type (understandably)...

let t1 = TypeOne ()

// if we want t1 "as" the union type, we have to do the following...

let ut1 = UnionType.TypeOne t1

// the following function returns a result of type UnionType
// but we have to explicitly do this for each type

let makeUnion switch =
    match switch with
        | true -> UnionType.TypeOne (TypeOne ())
        | _ -> UnionType.TypeTwo (TypeTwo ())

As in the comments, it seems like there's no way of inferring that return results should be of a union type, and if we have to require qualified access on the union type this is extremely verbose (which seems worryingly wrong).

There's also no way of creating a function that takes only the union types and returns them as the union. (In this case a function which takes a TypeOne or TypeTwo and returns UnionType). Or is there? Are there better ways of working with discriminated unions?

Was it helpful?

Solution

As pad has pointed out - you don't need to qualify a union case with its parent type - so you can write

let makeUnion switch =
    match switch with
    | true -> TypeOne (new TypeOne ())
    | false -> TypeTwo (new TypeTwo ())

However you will always have to specify the TypeOne. This is to avoid the ambiguity present if you have multiple cases which take the same arguments - like

type foo=
|Bar of int
|Baz of int

Then even knowing the return type, the compiler can't work out what to return. A more common example is actually with cases which take no arguments - say you decide to redifine true and false:

type bool = |True |False

OTHER TIPS

type TypeOne() =
    member val Name = "" with get, set

type TypeTwo() =
    member val Name = "" with get, set
    member val Property = 0 with get, set

type UnionType =
    | TypeOne of TypeOne
    | TypeTwo of TypeTwo

/// You can give some hints to the type checker 
/// by saying that you want a class constructor
let t1 = new TypeOne()

/// No need to use fully-qualified access on union types
let ut1 = TypeOne t1

let makeUnion switch =
    match switch with
    | true -> TypeOne (new TypeOne())
    | false -> TypeTwo (new TypeTwo())
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top