Question

If I have a discriminate union of different types of arrays how can i convert them to their 'actual' types?

type ItemStuff = 
   | Colors of string[]
   | Sizes of int[]

let foo = Sizes [|1;2;3|]

After running the above when I get the value of foo I see:

val foo : ItemStuff = Sizes [|1;2;3|]

How can I get an actual array of int's from foo? Am I just missing some syntax that allows me access to something like foo.[2]? I cannot enumerate through foo so I wasn't able to use map. I could write a member for ItemStuff that returns a properly typed array for each different type of array i am returning, but that just didn't seem right?

What are my best approaches here?

Here is what I ended up doing. any ideas on better ways to do it?

type ItemProp =
| Colors of string[]
| Sizes of int[]
| Quants of int[]
member this.GetColors() =
   match this with
   | Colors (stringArray) ->
       stringArray
   | _ -> null
member this.GetIntArr() =
   match this with
   | Sizes (intArray) | Quants (intArray) ->
       intArray
   |_ -> null

foo.GetIntArr()
Was it helpful?

Solution

How can I get an actual array of int's from foo?

That's the actual problem since foo is just said to have type ItemStuff. So it doesn't have to contain a Sizes-value at all - it could be a Colors as well.

Hence your programm has to decide here

let getIntArray = function
    | Sizes arr -> arr
    | Colors _  -> failwith "Given value doesn't contain an int array`

getIntArray foo will work properly but getIntArray (Colors [||]) will fail, but from type-level both are valid.

Note that if you're completely sure that the operation will succeed, you can use pattern-matching directly:

let getIntArray (Sizes arr) = arr

OTHER TIPS

Typically you'd use pattern matching since you won't know if an ItemStuff holds colors or sizes. If you definitely know that you've got a Sizes instance, you can do something like:

let (Sizes arr) = foo

to pull the array back out.

If you are not actually representing an either/or scenario (Colors OR Sizes), consider using a Record type here:

type ItemStuff = 
    { Colors : string[];
      Sizes : int[] } with
    static member Default = { Colors = [||]; Sizes = [||] }

let foo = { ItemStuff.Default with Sizes = [|1;2;3|] }
printfn "%A" foo.Colors    // [||]
printfn "%A" foo.Sizes     // [|1; 2; 3|]
printfn "%d" foo.Sizes.[2] // 3

You can also return an option type with None if the discriminated union wasn't a Sizes.

let getIntArray item =
    match item with
    | Sizes(i) -> Some(i)
    | _        -> None

But keep in mind that at the end in F# a function always transforms an input data type into an output data type, or a function just makes a side effect (printing something on screen or inserting data into an array).

So, for example, if you just want to print the Sizes (or nothing if it's a Colors) it will be shorter to write a new function for that purpose:

let printSizes item =
    match item with
    | Sizes(i) -> printfn "%A" i
    | _        -> ()

instead of:

let getIntArray item =
    match item with
    | Sizes(i) -> Some(i)
    | _        -> None

let res = getIntArray foo

(function | Some(i) -> printfn "%A" i | _ -> ()) res
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top