Как передать функцию F # в другую функцию F # из приложения C #? - PullRequest
3 голосов
/ 17 мая 2011

У меня есть сборка библиотеки классов F #, которая содержит две функции:

let add a b = a + b

и

let rec aggregateList list init (op:int -> int -> int) =
    match list with
    |[] -> init
    |head::tail ->
        let rest = aggregateList tail init op
        op rest head

У меня есть консольное приложение C #, которое ссылается на библиотеку F # и пытается сделать следующее:

FSharpList<int> l = new FSharpList<int>(1, new FSharpList<int>(2, FSharpList<int>.Empty));
int result = myFsLibrary.aggregateList(l, 0, myFsLibrary.add);

Однако компилятор жалуется, что [myFsLibrary.add] не может быть преобразован из «группы методов» в FSharpFunc<int, FSharpFunc<int, int>>

Ответы [ 3 ]

9 голосов
/ 17 мая 2011

Другие люди дали ответы, но я просто скажу, что вы не должны этого делать.

Не открывать списки F # для C #. Не открывайте функции с карри в C #. На этой границе видно несоответствие импеданса, поэтому лучше выставлять общие типы каркаса на границах сборки между языками. См

http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/fsharp-component-design-guidelines.pdf

для получения дополнительных советов.

5 голосов
/ 17 мая 2011

Вы можете явно создать функцию, используя делегат FSharpFunc.В C # удобнее создавать функцию, которая принимает все аргументы в виде кортежа, так что вы можете сделать это, а затем преобразовать функцию в карри тип, используя FuncConvert.Что-то вроде:

FuncConvert.FuncFromTupled(new FSharpFunc<Tuple<int, int>, int>(args => 
    arags.Item1 + args.Item2))

Однако, если вам нужно вызвать какую-то функцию F # из вашего кода C #, рекомендуется предоставить функцию с интерфейсом, дружественным к C #.В этом случае вы можете использовать Func делегат, и первый аргумент должен быть IEnumerable вместо F # -специфического типа списка:

module List = 
    let AggregateListFriendly inp init (op:Func<int, int, int>) =
        aggregateList (List.ofSeq inp) init (fun a b -> op.Invoke(a, b))

Тогда ваше приложение C # может просто использовать:

List.AggregateListFriendly(Enumerable.Range(0, 10), 0, (a, b) => a + b));
0 голосов
/ 17 мая 2011

Причина в том, что add экспортируется как обычная функция стиля .Net и имеет грубую подпись

int add(int, int)

C # и большинство языков .Net рассматривают это как метод, который принимает 2 int параметров и возвращает одно значение int. F # хотя не видит функции таким образом. Вместо этого он видит add как функцию, которая принимает int и возвращает функцию, которая, в свою очередь, получает int и возвращает int. Такое представление функций упрощает реализацию таких операций, как каррирование.

Чтобы перейти от представления мира C # к F #, вам нужно сделать немного магии, чтобы сложить метод на себя. Я достигаю этого, определяя набор методов фабрики и расширений F #, чтобы сделать магию для меня. Например

[<Extension>]
type public FSharpFuncUtil = 

    [<Extension>] 
    static member ToFSharpFunc<'a,'b,'c> (func:System.Func<'a,'b,'c>) = 
        fun x y -> func.Invoke(x,y)

    static member Create<'a,'b,'c> (func:System.Func<'a,'b,'c>) = 
        FSharpFuncUtil.ToFSharpFunc func

Я могу использовать эту библиотеку, чтобы получить соответствующий тип делегата F # для метода add, например:

var del = FSharpFuncUtil.Create<int, int, int>(myFsLibrary.add);
...