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 * 'b
can be converted to type MyType1 * MyType2
(though this is a bit confusing to me). In any case, you can write a function that takes two arguments of type obj
and tests them separately using two :?
patterns:
type MyType1 = A | B of float
type MyType2 = C | D of int
let func (x:obj) (y:obj) =
match (x, y) with
| (:? MyType1 as x1), (:? MyType1 as x2) ->
printfn "%A %A" x1 x2
| _ ->
printfn "something else"
func A (B 3.0) // A B 3.0
func A (D 42) // something else
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 T1
and T2
, you want the function that can take 3. Is that correct (T1 * T1
, T1 * T2
and T2 * 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 type T2
, then the second argument also has to be T2
(otherwise it can be T1
too)).
You can write a safe non-curried function that takes an argument of the following type:
type MyArg = Comb1 of T1 * T1 | Comb2 of T1 * T2 | Comb3 of T2 * T2
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 T1
or T2
as both first and second argument.
type MyArg = First of T1 | Second of T2
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 * T1
shouldn't be allowed). In this case, your function will simply have to throw an exception or something like that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With