I have a curried function that I’d like it to support different types of parameters, that are not on a inheritance relationship:
type MyType1 = A | B of float
type MyType2 = C | D of int
What I tried to do is:
let func x y =
match (x, y) with
| :? Tuple<MyType1, MyType1> -> "1, 1"
| _ -> "..."
However this is not possible. F# complains:
The type ”a * ‘b’ does not have any proper subtypes and cannot be used as the source of a type test or runtime coercion.
What is an elegant way to do this?
EDIT: Let me try to clarify this.
I have two similar, but distinct, types. I can very easily convert one type to another. I want to define a binary operation that will act on entities of those types, but I’d like to expose a single operation to the client.
That is, instead of providing:
let op11 (x : MyType1) (y : MyType1) = // do something useful
let op12 (x : MyType1) (y : MyType2) =
// convert y to MyType1
let y' = // ...
// dispatch to op11
op11 x y'
let op21 (x : MyType2) (y : MyType1) = // similar
let op22 (x : MyType2) (y : MyType2) = // similar
what I would like is to expose a single function to client code:
let op (x : obj) (y : obj) = // ...
This is like simulating the behavior of method overloading, but with curried functions.
Your code doesn’t work, because F# generalizes the type of arguments to a type parameter. I think you can’t dynamically test whether a type
'a * 'bcan be converted to typeMyType1 * MyType2(though this is a bit confusing to me). In any case, you can write a function that takes two arguments of typeobjand tests them separately using two:?patterns:Anyway, it would be interesting to know why do you want to do this? There may be a better solution…
EDIT (2) So, from all 4 two-element combinations of
T1andT2, you want the function that can take 3. Is that correct (T1 * T1,T1 * T2andT2 * T2)? In that case, you can’t write a fully safe curried function, because the type of second argument would “depend” on the type of first argument (if the first argument has a typeT2, then the second argument also has to beT2(otherwise it can beT1too)).You can write a safe non-curried function that takes an argument of the following type:
The type of the function would be
MyArg -> string.If you want a curried function, you can define a type which allows you to use either
T1orT2as both first and second argument.Then, your curried function will be
MyArg -> MyArg -> string. But note that if one combination of argument types is not allowed (if I understand you correctly,T2 * T1shouldn’t be allowed). In this case, your function will simply have to throw an exception or something like that.