I've distilled these sample types from my design:
type SomeType = T1 of int | T2 of string
type Condition = Int | String
Now, for unit testing purposes and other reasons that were distilled from here(design can't be changed), I must create SomeType values based on Condition tag.
However, my first attempt didn't compile:
let inline createSomeType' (someTypeVal : ^T, cond) =
match cond with
| Int -> T1 someTypeVal // warning FS0064: 'T restricted to "int"
| String -> T2 someTypeVal // error FS0001: expected "string" got "int"
Changing function signature to createSomeType'< ^T > didn't help either:
error FS0001: expected "int" got 'T
error FS0001: expected "string" got 'T
Then I tried overloading:
type detail =
static member inline dispatch (someTypeVal : int) = T1 someTypeVal
static member inline dispatch (someTypeVal : string) = T2 someTypeVal
let inline createSomeType' (someTypeVal : ^T, cond) =
match cond with
| Int -> detail.dispatch someTypeVal // error FS0041: ambiguous overload
| String -> detail.dispatch someTypeVal // error FS0041: ambiguous overload
Let's disambigue, right? No, adding type annotations restricts someTypeVal to int and we're back where we started.
From C++ point of view, all that means that F# compiler doesn't support SFINAE on union cases in pattern matching. We could use quotations or dynamic checking like this:
A)
let inline createSomeType ((someTypeVal : obj), cond) =
match box someTypeVal with
| :? int when cond = Int -> T1(someTypeVal :?> int)
| :? string when cond = String -> T2(someTypeVal :?> string)
| _ -> failwith "something happened:("
B)
type Condition = Int = 0 | String = 1
type detail =
static member inline dispatch ((someTypeVal : int), (c : int)) =
if Condition.Int = enum<Condition>(c) then
T1 someTypeVal
else
failwith "something happened:("
static member inline dispatch ((someTypeVal : string), (c : int)) =
if Condition.String = enum<Condition>(c) then
T2 someTypeVal
else
failwith "something happened:("
detail.dispatch(123, int Condition.Int) // usage
However, that's not concise and throws exceptions.
How should I implement createSomeType function so it does everything at compile-time?
P.S. The question is intentionally detailed because I couldn't find much info on this subject in one place, so someone googling will save time not repeating my errors.
EDIT:
Basically, I needed a single convenient function which uses both cond and someTypeVal with a signature like ^TVal -> Condition -> SomeType and compile time type resolution.
As @Gustavo says, IIUC, it's not possible without writing N * M overloads:
you can't expect the compiler to check for the cases contained in
cond, since those cases are values.