0

I am using lauthy language ext in here are 3 functions those will be called in form main function the aim is return aggregated results of commands.. or Error but it is complinign below when I pass y to ExecuteSingleHostCommands

Error CS1503 Argument 1: cannot convert from 'System.Func>' to 'System.Func>' 03'

  1. Returns Fun for given command

  2. executes fun with param and return results or error

  3. validates and return error in fails -- Main func -executes one by one command and bails out if any command is bad one.. if all goes well return aggregated resultsof all commands

    internal static Func> GetSingleCommands( IDictionary>> commandMap, Command hostCommand) => commandMap.Where(command => command.Key == hostCommand.Name).Select(keyValuePairs => keyValuePairs.Value).FirstOrDefault();

    internal static EitherAsync> ExecuteSingleCommands( Func> commands, string hostCommand) => new List>> { commands }.Aggregate( Right>(ImmutableList.Empty).ToAsync(), (state, func) => state.Bind(response => func(hostCommand).Map(response.Add)));

    internal static Either<Error, Unit> Validate(string hostCommand) =>
    CommandMap.Find(command => command.Key == hostCommand).IsSome ? Right(Unit.Default) : Left<Error, Unit>(new Error());
    
    public static EitherAsync<Error, ImmutableList<Response>> ExecuteAllAsync(
    IDictionary<string, Func<string, EitherAsync<Error, HostResponse>>> commandMap, IList<Command> hostCommands) =>
    from hostCommand in hostCommands
    from x in Command.Validate(hostCommand.Name)
    let y = Command.GetSingleHostCommands(commandMap, hostCommand).ToAsync()
    select Command.ExecuteSingleHostCommands(y, hostCommand.jobName);
    
userkk
  • 3
  • 2

1 Answers1

1

I think you should take a bit more care over the question. It's almost impossible to understand what you're asking here, and there are missing functions GetSingleHostCommands and types (and you even spell my name wrong ;) )

Anyway, from what I can deduce from your example you have a couple of problems:

  • In ExecuteAllAsync you're mixing several monadic types in one LINQ expression. That isn't how LINQ or monads work. You must attempt to maintain the same monadic type all the way through. So, hostCommands is an IEnumerable monad, Command.Validate(hostCommand.Name) is a Either monad

  • You seem to have a duplication problem due to the result type of Response and HostResponse. This can be made generic.

  • GetSingleCommands will not make use of the dictionary because its iterating through it every time.

  • ExecuteSingleCommands does too much work. All it has to do is invoke the delegate with the provided command.

It looks like you're overthinking this, and you might want to try and take a step back and simplify your approach. One thing to remember with functional programming is to always follow the types. The types will always guide you to the truth.

So, the first thing to do is to unravel this. This is an implementation that I have done which I think closely matches your intent. I have removed the use of Dictionary for the lanuguage-ext Map and IList and ImmutableList for the language-ext Seq. Mostly because they're easier to work with, but also easier to look at in code.

    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();
    }

What you may notice from ExecuteAllAsync is that hostCommands is now being mapped, with an inner LINQ expression running on a single command. This runs the validation, then gets the command, then executes it, and then returns the result.

Note also that the first two lines of the LINQ expression turn their result from Either to EitherAsync using .ToAsync(). So, every line of the LINQ expression is working with the same monad: EitherAsync.

The map will collect to a Seq<EitherAsync<Error, R>> which is the wrong result for the function, the EitherAsync is inside the Seq, but we want it on the outside. That's what the call to .Sequence() does. It turns a Seq<EitherAsync<Error, R>> into a EitherAsync<Error, Seq<R>>.

louthster
  • 1,560
  • 9
  • 20