Getting a typed array from a discriminate union of different types of arrays in F#
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 wh开发者_如何学Pythonen 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()
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
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
精彩评论