I'm just starting out in F#, and some of the issues around casting are confusing me mightily. Unfortunately, my background reading to try to figure out why is confusing me even more, so I'm looking for some specific answers I can fit into the general explanations...
I've got a ReadOnlyCollection<'T> of enums, produced by this function:
let GetValues<'T when 'T :> Enum> () =
(new ReadOnlyCollection<'T>(Enum.GetValues (typeof<'T>) :?> 'T[])) :> IList<'T>
What I want to do with it is find all the bits of the enum that are used by its values (i.e., bitwise-or all the values in the list together), and return that as the generic enum type, 'T. The obvious way to do that seemed to me to be this:
let UsedBits<'T when 'T :> Enum> () =
GetValues<'T>()
|> Seq.fold (fun acc a -> acc ||| a) 0
...except that that fails to compile, with the error "The declared type parameter 'T' cannot be used here since the type parameter cannot be resolved at compile time."
I can get the actual job done by converting to Int32 first (which I don't really want to do, because I want this function to work on all enums regardless of underlying type), viz.:
let UsedBits<'T when 'T :> Enum> () =
GetValues<'T>()
|> Seq.map (fun a -> Convert.ToInt32(a))
|> Seq.fold (fun acc a -> acc ||| a) 0
...but then the result is produced as Int32. If I try to cast it back to 'T, I again get compilation errors.
I don't want to get too specific in my question because I'm not sure which specifics I should be asking about, so -- where's the flaw(s) in this approach? How should I be going about it?
(Edited to add:, post @Daniel's answer
Alas, this appears to be one of those situations where I don't understand the context well enough to understand the answer, so...
I think I understand what inline and the different constraint are doing in your answer, but being an F# newbie, would you mind awfully expanding on those things a little so I can check that my understanding isn't way off base? Thanks.
)
You could do this:
let GetValues<'T, 'U when 'T : enum<'U>>() =
Enum.GetValues(typeof<'T>) :?> 'T[]
let inline GetUsedBits() =
GetValues() |> Seq.reduce (|||)
inline
allows a more flexible constraint, namely 'T (requires member ( ||| ))
. Without it, the compiler must choose a constraint that can be expressed in IL, or, if unable to do so, choose a concrete type. In this case it chooses int
since it supports (|||)
.
Here's a simpler repro:
let Or a b = a ||| b //add 'inline' to compare
See Statically Resolved Type Parameters on MSDN for more info.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With