Pergunta

Eu tenho um assembly de biblioteca de classes F# que contém duas funções:

let add a b = a + b

e

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

Eu tenho um aplicativo de console C# que faz referência à biblioteca F# e está tentando fazer o seguinte:

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

No entanto, o compilador reclama que [myFsLibrary.add] não pode ser convertido de 'grupo de métodos' para FSharpFunc<int, FSharpFunc<int, int>>

Foi útil?

Solução

Você pode criar explicitamente uma função usando o FSharpFunc delegar.Em C#, é mais conveniente criar uma função que receba todos os argumentos como uma tupla, então você pode fazer isso e depois converter a função em um tipo curried usando FuncConvert.Algo como:

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

No entanto, se você precisar chamar alguma função F# do seu código C#, é recomendável expor uma função com uma interface amigável ao C#.Neste caso, você pode usar Func delegado e o primeiro argumento deve ser IEnumerable em vez do tipo de lista específico do F#:

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

Então seu aplicativo C# pode apenas usar:

List.AggregateListFriendly(Enumerable.Range(0, 10), 0, (a, b) => a + b));

Outras dicas

Outras pessoas forneceram respostas, mas vou apenas intervir para dizer que você não deveria fazer isso.

Não exponha listas F# a C#.Não exponha funções com curry ao C#.A incompatibilidade de impedância é visível neste limite, portanto é melhor expor tipos de estrutura comuns em limites de assembly entre linguagens.Ver

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

para mais conselhos.

A razão é que add é exportado como uma função normal de estilo .Net e tem a assinatura aproximada

int add(int, int)

C# e a maioria das linguagens .Net veem isso como um método que leva 2 int parâmetros e retorna um único int valor.Porém, F# não vê funções dessa maneira.Em vez disso, ele vê add como uma função assume um int e retorna uma função que por sua vez assume um int e retorna um int.Essa visão das funções facilita muito a implementação de operações como curry.

Para converter da visão de mundo C# para F#, você precisa fazer um pouco de mágica para dobrar um método sobre si mesmo.Eu consigo isso definindo um conjunto de métodos de fábrica e extensão do F# para fazer a mágica para mim.Por exemplo

[<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

Posso usar esta biblioteca para obter o tipo de delegado F# apropriado para o add método assim

var del = FSharpFuncUtil.Create<int, int, int>(myFsLibrary.add);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top