Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# type constraints on enums

Tags:

enums

f#

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?

like image 495
Gustavo Guerra Avatar asked Feb 27 '13 14:02

Gustavo Guerra


1 Answers

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'
like image 157
Daniel Avatar answered Oct 10 '22 19:10

Daniel