Reputation: 133
I have the following F# function
let Fetch logger id =
logger "string1" "string2"
// search a database with the id and return a result
In my C# class I want to call the Fetch F# function mocking out the logger function. So I have the following C# function as the mocker.
void Print(string x, string y) { // do nothing }
I'm trying to call the F# function from a C# class with the following.
var _logger = FuncConvert.ToFSharpFunc<string, string>(Print);
var _result = Fetch(logger, 3);
The problem with FuncConvert.ToFSharpFunc is that takes only one type argument. When I change the Fetch F# logger function to the following it works fine when I use ToFSharpFunc(Print) where the C# Print function also takes in one string.
let Fetch logger id =
logger "string1"
// search a database with the id and return a result
Anyone got ideas?
Upvotes: 13
Views: 1885
Reputation: 2898
F# functions can have only one argument. When you have for example: logger "string1" "string2"
the expression logger "string1"
creates another function with "string1"
inside it which then can take "string2"
as an argument. So to convert this you can create such helper method:
public static class FuncConvertExt
{
public static FSharpFunc<T1, FSharpFunc<T2, Unit>> Create<T1, T2>(Action<T1, T2> action)
{
Converter<T1, FSharpFunc<T2, Unit>> conv = value1 =>
{
return FuncConvert.ToFSharpFunc<T2>(value2 => action(value1, value2));
};
return FSharpFunc<T1, FSharpFunc<T2, Unit>>.FromConverter(conv);
}
}
Then you can do this:
var func = FuncConvertExt.Create<string, string>(Print);
func.Invoke("aaa").Invoke("bbb");
Upvotes: 4
Reputation: 131180
Short Version
var tupleLogger = FuncConvert.ToFSharpFunc<Tuple<string,string>>(t=>Print(t.Item1,t.Item2));
var logger = FuncConvert.FuncFromTupled(tupleLogger);
MyOtheProject.MyModule.Fetch(logger, 3);
Long Version
F# functions only accept one argument. Multiple arguments essentially create nested functions. You need to do the same on C#'s side.
Check the type of the logger
parameter in C# with Intellisense. It's
Microsoft.FSharp.Core.FSharpFunc<string, Microsoft.FSharp.Core.FSharpFunc<string, a>>
FuncConvert.ToFSharpFunc can't create this. FuncFromTupled though can create this from an FSharpFunc that takes a Tuple with multiple fields as an argument.
That's something that can be created by ToFsharpFunc :
FSharpFunc<Tuple<string,string>,Unit> tupleLogger = FuncConvert.ToFSharpFunc<Tuple<string,string>>(
t=> Print(t.Item1,t.Item2));
or
var tupleLogger = FuncConvert.ToFSharpFunc<Tuple<string,string>>(t=> Print(t.Item1,t.Item2));
As FuncFromTupled
's description says, it's A utility function to convert function values from tupled to curried form.
. tupleLogger
is a tupled form that we need to convert to a curried form:
var logger = FuncConvert.FuncFromTupled(tupleLogger);
The resulting code looks like:
var tupleLogger = FuncConvert.ToFSharpFunc<Tuple<string,string>>(t=>Print(t.Item1,t.Item2));
var logger = FuncConvert.FuncFromTupled(tupleLogger);
MyOtheProject.MyModule.Fetch(logger, 3);
You could create a utility function to combine the two conversions :
public static class MyFuncConvert
{
public static FSharpFunc<T1, FSharpFunc<T2, Unit>> ToFSharpFunc<T1, T2>(Action<T1, T2> action)
{
var tupled = FuncConvert.ToFSharpFunc<Tuple<T1, T2>>(
t => action(t.Item1, t.Item2));
var curried = FuncConvert.FuncFromTupled(tupled);
return curried;
}
}
Which would allow you to write :
var logger = MyFuncConvert.ToFSharpFunc<string,string>(Print);
Upvotes: 8