Using type provider types in a discriminated union


I have been experimenting with type providers but am unable to parse input to a union type of two type provider types.

type Vegetable = JsonProvider<"path">
type Fruit = XmlProvider<"path">

type Thingy = V of Vegetable.Root | F of Fruit.Fruit

type EncapsulatingRec = {name:string ; item:Thingy}

let listOfEncRec =
    |> (fun path -> {
        name = Path.GetDirectoryName path 
        item = File.ReadAllText path |> Vegetable.Parse

JetBrains Rider gives an error "This expression was expected to have type ‘Thingy’ but here has type ‘JsonProvider<…>.Root’

What is the issue? I have tried defining Thingy as

type Thingy = Vegetable | Fruit

The code works only when Thingy type is defined as

type Thingy = Vegetable.Root

How can i have a working union in this situation?

Thanks in advance!

The issue I think comes down to the difference between “tagged” (or discriminated) unions vs “untagged” unions (like you might see in typescript or python). In F#, all unions are “tagged.” That means that you have to use the “constructors” of a union (here, that’s V and F) in order to have an instance of the union. The union you have there is literally the union of V (with a Vegetable.Root attached to it) and F (with a Fruit.Fruit attached to it). It’s not the union of Vegetable.Root and Fruit.Fruit

Are you familiar with the Option type at all? Option is just a discriminated union, and has the same issue:

To make my code snippet above compile, you’d have to change it to

To make your code compile, you have to do item = File.ReadAllText path |> Vegetable.Parse |> V


Thank you again for a very through answer! This clarifies things a lot.

1 Like