ssp
ssp

Reputation: 1722

How to write function where argument is type but not typed value?

I want to convert a string representations of few dozen enum types to enum values. It's easy to convert string to concrete type:

Enum.Parse(typeof<FontStyle>,"Bold") |> unbox<FontStyle>

but for now i want to write function where type and string are parameters. The best one i can write is:

> let s2e (_: 'a) s = Enum.Parse(typeof<'a>,s) |> unbox<'a>;;
val s2e : 'a -> string -> 'a
> s2e FontStyle.Regular "Bold";;
val it : FontStyle = Bold

Is there any option to write something like this but with type itself as first argument?

Upvotes: 3

Views: 281

Answers (2)

kvb
kvb

Reputation: 55184

Tomas's answer is good. Additionally, note that you can apply F#'s enum constraints to prevent nonsensical type parameters from being used:

let parseEnum<'t,'u when 't:enum<'u>> s = System.Enum.Parse(typeof<'t>, s) :?> 't
(parseEnum "Class" : System.AttributeTargets)

Now calling (parseEnum "Test" : int) will fail at compile time since int is not an enum.

EDIT

Since we don't actually do anything with the underlying type 'u, we don't need the full power of F#'s enum constraints, as ssp points out in a comment. This is easier to use because it only has a single type parameter:

let parseEnum<'t when 't :> System.Enum and 't : struct> s =
  System.Enum.Parse(typeof<'t>, s) :?> 't

Note that the struct constraint prevents using System.Enum itself as the type argument.

Upvotes: 1

Tomas Petricek
Tomas Petricek

Reputation: 243041

The function needs to take a single type parameter, which will be the type of the returned enum. However, you don't need to specify the type using a "fake" parameter that isn't used anywhere in the code - you can specify the actual type parameter when calling the function using the same notation as for example when calling defaultof<SomeType>.

The following modified function takes a single type parameter which occurs only in the return type (to avoid confusing SO code formatter, I replaced ' with ´ in the code):

> let parseEnum<´a> s = Enum.Parse(typeof<´a>,s) |> unbox<´a>;; 
val parseEnum : string -> ´a

When calling the function, you need to specify the type explicitly:

> parseEnum<FontStyle> "Bold";; 
val it : FontStyle = Bold

Your solution was pretty close to this - the only change is that you can specify the type parameter explicitly instead of providing a witness value to guide the type inference.

Upvotes: 4

Related Questions