dangeroushobo
dangeroushobo

Reputation: 1401

Convert Discriminated Union to primitive type via member function in F#

I have the following discriminated union with a member function where I want to return the unboxed type:

type ModelData =
    | Double of double
    | UInt32 of uint32
    | UInt16 of uint16
    | Byte of byte
    | Int32 of int32
    | String of string
    | Bit of Bit
    member inline this.Value : 'a =
        match this with
        | Double x -> x
        | UInt32 x -> x
        | UInt16 x -> x
        | Byte x -> x
        | Int32 x -> x
        | String x -> x
        | Bit x -> x

The error is that the member 'this.Value' doesn't return the same type for each branch. Is this possible?

Edit #1

I have a Database defined as follows which uses this ModelData type:

module Database =
    type Drecord =
        { Value: ModelData}
    type Db = Dictionary<string, Drecord>

So I have a database full of values, then I take some of these values and generate a JSON string using sprintf.

sprintf "{\"General\":{\"Example\":%.3f}}" value //where value is of type ModelData

This currently does not work since sprintf requires a float and not a ModelData. So original I thought I could call a function to safely convert to that type (hence the this.Value member). I've got this to work by having this.Value instead return a string, and having only %s in my sprintf format string. So this works:

sprintf "{\"General\":{\"Example\":%s}}" value.Value

Is there a better way to solve this?

Upvotes: 0

Views: 158

Answers (1)

Tomas Petricek
Tomas Petricek

Reputation: 243041

I'm not sure I understand what are you trying to do. If you define a discriminated union, then the value can be either of the cases and when you want to use a value of your ModelData type, you'll need to pattern match on it in order to decide what kind of value you have.

If you add a way of getting a value that works only for one specific case (e.g. when the value is a float), you are losing all the safety that discriminated unions give you. So, I think that doing what you're asking for is most likely going to be a bad idea.

That said, you can define a member GetValue<'a> that can be called to extract value of a specific type using the unbox function:

type ModelData =
    | Double of double
    | UInt32 of uint32
    | UInt16 of uint16
    member inline this.GetValue<'a>() : 'a =
        match this with
        | Double x -> unbox<'a> x
        | UInt32 x -> unbox<'a> x
        | UInt16 x -> unbox<'a> x

let v = Double 1.23
v.GetValue<float>()

This will, of course, throw an InvalidCastException if you call it with the wrong type argument, e.g. v.GetValue<int>() in the above case. Also note that this needs to be a method so that you can make it generic.

Upvotes: 2

Related Questions