Sam
Sam

Reputation: 2865

How to turn this into statically resolved type parameters

I have a need to use statically resolved type parameters in a situation similar to the below:

[<Struct>]
type Wrapper<'T> = 
    val raw:'T
    new(v:'T) = {raw = v}


type Value = 
    | Float of float
    | Int of int
    | String of string

    with

    member this.ToWrapper() :'T =
        match this with
        | Float f -> Wrapper<float>(f)      // type is inferred as float
        | Int i -> Wrapper<int>(i)          // error 
        | String s -> Wrapper<string>(s)    // error

How do I define and use a ToWrapper function (or set thereof) that can map a 'Value' type to any type within the Generic Wrapper<'T> where I know 'T will be either float | int | string?

The Wrapper<'T> type needs to be a Struct so interfaces aren't an option - as suggested in some other posts related to this.

Upvotes: 1

Views: 168

Answers (2)

Mark Seemann
Mark Seemann

Reputation: 233150

You can't do this, because Wrapper<float> isn't the same type as Wrapper<int> (which also isn't the same type as Wrapper<string>). What would the return type of ToWrapper be? It can't be all three at once.

Upvotes: 2

Gus
Gus

Reputation: 26174

It's not clear to me what are you trying to achieve. Are you trying to restrict the wrapped types to Int, String and Float?

1) If so you can check at runtime like this:

[<Struct>]
type Wrapper<'T> = 
    val raw:'T
    new(v:'T) = {raw = v}

let wrap x =
    match box x with
    | :? float  -> ()
    | :? int    -> ()
    | :? string -> ()
    | _ -> failwith "invalid type"
    Wrapper x

let a = wrap 90
let b = wrap "hi"
let c = wrap true // fails at runtime

2) If you want to restrict at compile-time an easy way is to add static members as constructors:

[<Struct>]
type Wrapper<'T> = 
    val raw:'T
    private new(v:'T) = {raw = v}
with
    static member wrap (x:float)  = Wrapper x
    static member wrap (x:int)    = Wrapper x
    static member wrap (x:string) = Wrapper x

let a = Wrapper<_>.wrap 90
let b = Wrapper<_>.wrap "hi"
let c = Wrapper<_>.wrap true // doesn't compile

3) Or may be, using the DU inside the wrapper makes more sense for you:

type Value = 
    | Float of float
    | Int of int
    | String of string

[<Struct>]
type Wrapper = 
    val raw:Value
    new(v:Value) = {raw = v}

Of all solutions 3) is the only one that really restricts your wrapper. Solutions 1) and 2) restrict the way you construct it.

From 2) you can use some tricks with statically resolved type parameters in order to come up with an inline function (not a method) that will wrap only on those types. Still that will not constraint the type itself, but since the constructor is private the code that consumes your type will be forced through your restricted constructors.

Statically resolved type parameters work with functions or methods but not on types, since they are a compile time F# feature, not a .NET type system feature.

Upvotes: 5

Related Questions