F # type restrictions on enumerations

I am trying to define a generic conversion operator from a string to Enum, and I would like to use it as follows:

let day = asEnum<DayOfWeek>("Monday") 

But with this implementation:

 let asEnum<'a, 'b when 'a: (new : unit -> 'a) and 'a : struct and 'a :> ValueType and 'a : enum<'b>> text = match Enum.TryParse<'a>(text) with | true, value -> Some value | false, _ -> None 

I can only use it like this:

  let day = asEnum<DayOfWeek,_>("Monday") 

or that:

  let day:DayOfWeek option = asEnum("Monday") 

If I omitted 'a : enum<'b> from the type constraint at all, I can get it the way I want, but if someone does not specify the type, the default will be int , which I really dislike, I would prefer he gave a compile-time error, how does this happen when I specify a limit

Maybe there is some trick to just specify one type parameter and draw a different conclusion? Any ideas?

+7
source share
4 answers

How about this?

 let asEnum s :'a option when 'a:enum<'b> = match System.Enum.TryParse s with | true, v -> Some v | _ -> None // works, but warns that type params shouldn't be given explicitly asEnum<System.Reflection.BindingFlags,_> "DeclaredOnly" // also okay (asEnum "DeclaredOnly" : System.Reflection.BindingFlags option) 
+3
source

Unfortunately, in order to increase the limit, it seems you need to describe all this: (as kvb pointed out, you can avoid duplicating the constraints on TryParse by adding the 'T : enum<int> constraint outside the angle brackets)

This also works:

 let asEnum<'T when 'T : enum<int> and 'T : struct and 'T :> ValueType and 'T : (new : unit -> 'T)> text = match Enum.TryParse<'T>(text) with | true, value -> Some value | _ -> None 

This gives a compile-time error if the base type is not int :

 type ByteEnum = | None = 0uy asEnum<ByteEnum> "None" //ERROR: The type 'int' does not match the type 'byte' 
+2
source

Another thing I tried was the following:

 type AsEnum = static member Get<'a, 'b when 'a: (new : unit -> 'a) and 'a : struct and 'a :> ValueType and 'a : enum<int>> (text:string) = match Enum.TryParse<'a>(text) with | true, value -> Some value | _ -> None static member Get<'a, 'b when 'a: (new : unit -> 'a) and 'a : struct and 'a :> ValueType and 'a : enum<int64>> (text:string) = match Enum.TryParse<'a>(text) with | true, value -> Some value | _ -> None let a = AsEnum.Get<BindingFlags>.Get "DeclaredOnly" 

to try to figure out if I can get the compiler to conclude which overload to cause, but if the error is with an ambiguity error

0
source

A small update about 3 years after this ^ _ ^

Using this string extension to convert a string to an enumeration

 type System.String with /// Strongly-typed shortcut for Enum.TryParse(). member this.ToEnum<'a when 'a :> System.Enum and 'a : struct and 'a : (new: unit -> 'a)> () = let ok, v = System.Enum.TryParse<'a>(this, true) if ok then Some v else None 

Be careful with the enum declaration.

 type failingEnum = | FirstValue | SecondValue | AnotherValue type compliantEnum = | FirstValue = 0 | SecondValue = 1 | AnotherValue = 2 

Then

 let x = "whatever".ToEnum<failingEnum>(); //- will give error failingEnum is not compatible with the type System.Enum let x = "whatever".ToEnum<compliantEnum>(); //- will succeed ! 
0
source

All Articles