Reputation: 3392
I'm translating this F# source for railway oriented programming to C#.
I'm having trouble translating this SelectMany
overload:
static member inline SelectMany (this:Result<'TSuccess, 'TMessage>, func: Func<_,_>, mapper: Func<_,_,_>) =
let mapper = lift2 (fun a b -> mapper.Invoke(a,b))
let v = bind func.Invoke this
mapper this v
I've mapped function signature with the above:
public static Result<TResult, TMessage> SelectMany<TSuccess, TMessage, TValue, TResult>(
this Result<TSuccess, TMessage> result,
Func<TSuccess, Result<TValue, TMessage>> func,
Func<TSuccess, TValue, TResult> mapperFunc)
F# lift2
function (that I think I've correctly translated) accepts as first parameter a function with signature ('a -> 'b -> 'c)
, but when bound to mapper
let-binding with partial application I've problem understanding the used lambda function.
I normally use these helpers for partial application, but I was not able to translate this F# code to C#.
Upvotes: 1
Views: 385
Reputation: 1524
Your Lift2 is expecting a curried function, but the mapper passed into SelectMany is not curried. So let's curry it:
Func<TSuccess, Func<TValue, TResult>> curriedMapper = suc => val => mapperFunc(suc, val);
Func<
Result<TSuccess, TMessage>,
Result<TValue, TMessage>,
Result<TResult, TMessage>
> liftedMapper = (a, b) => Lift2(curriedMapper, a, b);
var v = Bind(func, result);
return liftedMapper(result, v);
Upvotes: 3
Reputation: 13577
In general, lift2
takes a function of two arguments of simple types 'a
and 'b
and produces a function that works on wrapped types M<'a>
and M<'b>
. Otherwise, you can think of it as taking three arguments: a function over simple types and two wrapped values, unwrapping those values, applying the function to them and wrapping the result.
Assuming your functions were correctly ported from F#, the body of SelectMany
would look like this:
return Lift2(mapper, this, Bind(func, this));
The odd Invokes
are there in F# version, because Funcs
are not applicable like in C#, you have to explicitly call Invoke
. Also since it's uncurried, a mapper.Invoke
can't be passed straight into lift2
- that's why it's wrapped in a curried function.
Upvotes: 1