F# type constraints on enums

故事扮演 提交于 2019-12-23 08:12:26

问题


I'm trying to define a generic conversion operator from a string to an Enum, and I'd like to use it like this:

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 this:

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

If I omit the 'a : enum<'b> altogether from the type constraint, I can have it as I want, but then if someone doesn't specify the type it will default to int, which I really don't like, I'd prefer it to give a compile time error like it does when I specify a constraint

Maybe there's any trick to just specify one type parameter and have the other one infered? Any ideas?


回答1:


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)



回答2:


Unfortunately, in order to augment the constraint it seems you have to spell it all out: (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 underlying type isn't int:

type ByteEnum =
  | None = 0uy

asEnum<ByteEnum> "None" //ERROR: The type 'int' does not match the type 'byte'



回答3:


Another thing I tried was this:

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 see if I could get the compiler to infer which overload to call, but if fails with an ambiguity error




回答4:


A little update something like 3 years afterwards ^_^

Using this string extension to convert string to an enum

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 your 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 !


来源:https://stackoverflow.com/questions/15115050/f-type-constraints-on-enums

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!