Reputation: 928
Assume that there is a third-party library written in FSharp, it contains several generic classes, for example as follows:
type FirstType<'a>
has method DoWork, that accepts:
first param of type FirstType<'a>,
second param is a function of type ('a -> 'b)
DoWork method return type is FirstType<'b>
type SecondType<'a>
has method DoWork, that accepts:
first param of type SecondType<'a>,
second param is a function of type ('a -> 'b)
DoWork method return type is SecondType<'b>
type ThirdType<'a>
has method DoWork, that accepts:
first param of type ThirdType<'a>,
second param is a function of type ('a -> 'b)
DoWork method return type is ThirdType<'b>
These classes have no common interfaces or parent classes, type Object is their only common parent.
Each of the types below has one method named DoWork. It accepts two parameters:
Then this DoWork function should return the object which has type of the class it is situated in, but with generic type parameter equal to 'b.
Currently. Example usage of these classes:
let first = new FirstType<int>()
...
// here result is of type FirstType<string>
let result = FirstType.DoWork first (fun x -> "hello" + x.toString())
let second = new SecondType<int>()
...
// here result is of type SecondType<bool>
let result = SecondType.DoWork second (fun x -> 2 = 4)
let third = new ThirdType<string>()
...
// here result is of type ThirdType<int>
let result = ThirdType.DoWork third (fun x -> x.Length())
The Problem:
It is required to implement a function called Do, that accepts two parameters, first - an object that has type either equal to FirstType<'a> or to SecondType<'a> or to ThirdType<'a>, second - a function of type ('a -> 'b) where 'b can be a type another from 'a. So, this function should determine the type of it's first input parameter and based on this - return appropriate type.
The return type of Do function:
Desired. Example usage of these classes:
let first = new FirstType<int>()
let second = new SecondType<int>()
let third = new ThirdType<string>()
...
// here result1 should be of type FirstType<string>
let result1 = Do first (fun x -> "hello" + x.toString())
// here result2 should be of type SecondType<bool>
let result2 = Do second (fun x -> 2 = 4)
// here result3 should be of type ThirdType<int>
let result3 = Do third (fun x -> x.Length())
I have thought of function overloading, but it is not allowed to be used in F#. I am now thinking of how to make a function return really different types, not Discriminated Unions, because at the point of calling the function it expects a specific type.
UPDATE:
I have looked at the comment of John Palmer and tried it.
type Ops =
static member Do (f: 'a -> 'b) (x:FirstType<'a>) = ...
static member Do (f: 'a -> 'b) (x:SecondType<'a>) = ...
static member Do (f: 'a -> 'b) (x:ThirdType<'a>) = ...
When trying to create a function:
let Do f x = Ops.Do f x
There is the following error:
Error: One or more of the overloads of this method has curried arguments. Consider redesigning these members to take arguments in tupled form.
The same error is when trying to use this Do function as a member of class Ops:
let result1 = first |> Ops.Do(fun x -> x + 2)
let result2 = second |> Ops.Do(fun x -> "hello" + x.ToString())
let result3 = third |> Ops.Do(fun x -> x = 1) sq
When redesigning Do methods in Ops type with arguments in tupled form and creating Do function like this:
let Do f x = Ops.Do (f, x)
.. there is the following error in the Error List:
Error: A unique overload for method 'Do' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: static member Ops.Do: f:('a -> 'b) * x:FirstType<'a> -> FirstType<'b>, static member Ops.Do: f:('a -> 'b) * x:SecondType<'a> -> SecondType<'b>, static member Ops.Do : f:('a -> 'b) * x:ThirdType<'a> -> ThirdType<'b>
So, I am able to use Do only with indicating a class name (Ops) and in a tupled form.. as I have understood with the help of error messages.
Is there any way to be able to use currying with overloaded member-methods?
The usage I am dreaming of is like this:
let result = input |> Do someFunction
I would be happy if someone has any ideas or suggestions. Maybe I am wrong at some point.
Upvotes: 4
Views: 1797
Reputation: 26174
I think this is what you are trying to do:
type FirstType<'a> = FirstType of 'a
type SecondType<'a> = SecondType of 'a
type ThirdType<'a> = ThirdType of 'a
type Ops = Ops with
static member ($) (Ops, FirstType a) = fun f -> FirstType (f a)
static member ($) (Ops, SecondType a) = fun f -> SecondType (f a)
static member ($) (Ops, ThirdType a) = fun f -> ThirdType (f a)
let inline Do f x = (Ops $ f) x
let first = FirstType 10
let second = SecondType 12
let third = ThirdType "Hello"
let result1 = Do first (fun x -> "hello" + x.ToString())
let result2 = Do second (fun x -> 2 = 4)
let result3 = Do third (fun x -> x.Length)
Find more about inline and overloading in these posts. It seems you are trying to implement a generic map function over your wrapper classes, this correspond to the Functor Typeclass in Haskell, which has a function fmap
, with the same signature as your Do
function but with the arguments flipped.
Upvotes: 2