Я думаю, вам следует уделить больше внимания этому вопросу.Почти невозможно понять, о чем вы спрашиваете здесь, и здесь отсутствуют функции GetSingleHostCommands
и типы (и вы даже пишете мое имя неправильно;))
В любом случае, из того, что я могу вывести из вашего примера, выесть пара проблем:
В ExecuteAllAsync
вы смешиваете несколько монадических типов в одном выражении LINQ.Это не то, как работают LINQ или монады.Вы должны пытаться поддерживать один и тот же монадический тип на протяжении всего пути.Итак, hostCommands
- это IEnumerable
монада, Command.Validate(hostCommand.Name)
- это Either
монада
Похоже, у вас возникла проблема с дублированием из-за типа результата Response
иHostResponse
.Это можно сделать универсальным.
GetSingleCommands
не будет использовать словарь, потому что он повторяется каждый раз.
ExecuteSingleCommands
делает слишком много работы.Все, что он должен сделать, это вызвать делегата с помощью предоставленной команды.
Похоже, вы слишком обдумали это, и вы можете попытаться сделать шаг назад и упростить свой подход.При функциональном программировании следует помнить, что всегда следует за типами .Типы всегда приведут вас к истине.
Итак, первое, что нужно сделать, это разгадать.Это реализация, которую я сделал, и я думаю, что она соответствует вашим намерениям.Я удалил использование Dictionary
для lanuguage-ext Map
и IList
и ImmutableList
для языка-ext Seq
.Главным образом потому, что с ними легче работать, но также легче смотреть в коде.
public class Command {
public readonly string Name;
static Either<Error, Func<string, EitherAsync<Error, R>>> GetCommand<R>(
Map<string, Func<string, EitherAsync<Error, R>>> commandMap,
Command hostCommand) =>
commandMap.Find(hostCommand.Name)
.ToEither(new Error());
internal static EitherAsync<Error, R> ExecuteCommand<R>(
Func<string, EitherAsync<Error, R>> command,
Command cmd) =>
command(cmd.Name);
static Either<Error, Unit> Validate<R>(
Map<string, Func<string, EitherAsync<Error, R>>> commandMap,
Command hostCommand) =>
commandMap.Find(hostCommand.Name)
.Map(_ => unit)
.ToEither(new Error());
public static EitherAsync<Error, Seq<R>> ExecuteAllAsync<R>(
Map<string, Func<string, EitherAsync<Error, R>>> commandMap,
Seq<Command> hostCommands) =>
hostCommands.Map(cmd =>
from _ in Command.Validate(commandMap, cmd).ToAsync()
from f in Command.GetCommand<R>(commandMap, cmd).ToAsync()
from r in Command.ExecuteCommand(f, cmd)
select r)
.Sequence();
}
Что вы можете заметить из ExecuteAllAsync
, так это то, что hostCommands
теперь отображается с внутренним LINQвыражение работает по одной команде.Это запускает проверку, затем получает команду, затем выполняет ее и затем возвращает результат.
Также обратите внимание, что первые две строки выражения LINQ изменяют свой результат с Either
на EitherAsync
, используя .ToAsync()
.Итак, каждая строка выражения LINQ работает с одной и той же монадой: EitherAsync
.
Карта соберет в Seq<EitherAsync<Error, R>>
, что является неправильным результатом для функции, EitherAsync
равно внутри Seq
, но мы хотим, чтобы он был снаружи .Это то, что делает вызов .Sequence()
.Превращает Seq<EitherAsync<Error, R>>
в EitherAsync<Error, Seq<R>>
.