Я начал изучать функциональное программирование на C#, в основном через эту книгуи это руководство
Итак, я реализовал опцию с методами расширения Map и Bind. Пока все хорошо.
Допустим, я хочу сначала построить конвейер с некоторой фильтрацией, затем вызвать асинхронный метод, а затем продолжить обработку с результатом. Например:
Хорошо, этот псевдокод не очень полезен, но, надеюсь, он показывает, что я имею в виду. Я хочу
сначала сделать цепную замену "if name == Kalle". Тогда, если да, получите продукты из асинхронного метода и еще немного обработайте. Однако первая карта вернет Option
, и мне придется ждать его внутри второй карты... некрасиво.
Option cust = Some(new Customer(){Name ="Kalle"});
cust.Bind(NamedKalle) //yes I could use where here, not the point though
.Map(async c => await myHttpClient.GetAdressAsync(c))
.Map(addressTask=> (await addressTask).DoSomethingMore(address))
//...
public Customer NamedKalle(Customer c) =>
c.Name == "Kalle" ? Some(c) : None;
Мне удалось создать асинхронное сопоставление и асинхронную карту, которые, кажется, работают, но я этого не видел, что заставляет меня думать, что я что-то упускаю. Могу ли я использовать оригинальную карту и каким-либо образом привязать ее?
// Match that awaits (member on Option)
public async Task MatchAsync(Func noneFunc, Func someFunc) =>
IsSome
? await someFunc(_value)
: await Task.FromResult(noneFunc());
// Map that awaits, extension method
public static async Task MapAsync(this Option optT, Func func) =>
await optT.MatchAsync(
()=> F.None,
async v => F.Some(func(await v)));
НИЖЕ ТОЛЬКО ДЛЯ СПРАВКИ, это реализация Option, помощников и метода расширения. Это подмножество проекта с открытым исходным кодом по адресу https://github.com/la-yumba/functional-csharp-code/
.namespace ProductCatalog.Api.Functional
{
public static class FunkyExtensions
{
public static Option Map(this Option opt, Func func) =>
opt.Match(() => F.None, v => F.Some(func(v)));
public static async Task Map2(this Option optT, Func func) =>
await optT.Match2(
()=> F.None,
async v => F.Some(func(await v)));
public static Option Where(this Option opt, Func pred) =>
opt.Match(() => F.None,
t => pred(t) ? opt: F.None);
public static Option Bind(this Option opt, Func func) =>
opt.Match(() => F.None, (v) => func(v));
}
public static partial class F
{
public static Option Some(T value) => new Option.Some(value); // wrap the given value into a Some
public static Option.None None => Option.None.Default; // the None value
}
public struct Option
{
T _value;
public bool IsSome { get; set; }
public bool IsNone => !IsSome;
public override string ToString() => IsNone ? $"
private Option(T value)
{
if (value == null)
throw new Exception("Cant be null");
_value = value;
IsSome = true;
}
public static implicit operator Option(Option.None _) => new Option();
public static implicit operator Option(Option.Some some) => new Option(some.Value);
public R Match(Func noneFunc, Func someFunc) =>
IsSome ? someFunc(_value)
: noneFunc();
public async Task Match2(Func noneFunc, Func someFunc) =>
IsSome
? await someFunc(_value)
: await Task.FromResult(noneFunc());
}
namespace Option
{
public struct None
{
internal static readonly None Default = new None();
}
public struct Some
{
public T Value { get; set; }
public Some(T value)
{
if (value == null)
throw new Exception("Value should not be null when creating Some, use None instead");
Value = value;
}
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/707 ... mming-in-c
Мобильная версия