language-ext: query expression with group by and Validation does not work

Hello Louthy and All, I have a query expression that works for data as an Option but when I try to do the same query as a Validation it does not work.

Here is the case that works as an option:

public static Func<IEnumerable<Option<CellData>>, IEnumerable<Finding>>
            GetAndMergeDbValues = cells =>
            {
                var groupings = from opt in cells
                                from c in opt
                                group c by (c.TimePeriod, c.Periodicity) into g
                                select new QueryGrouping
                                (
                                    g.Key.Item1,
                                    g.Key.Item2,
                                    g
                                );

                ...
            };

The ctor and data CellData is defined as:

public class CellData
    {
        public CellData(string sheet,
                        string range,
                        string companyId,
                        string mnemonic,
                        DateTime timePeriod,
                        string periodicity,
                        string value)
        {
            this.Sheet = sheet;
            this.Range = range;
            this.CompanyId = companyId;
            this.Mnemonic = mnemonic;
            this.TimePeriod = timePeriod;
            this.Periodicity = periodicity;
            this.Value = value;
        }

Left out private static constructors methods for brevity’s sake.

Finally QueryGrouping is defined as:

public class QueryGrouping
    {
        public QueryGrouping(DateTime TimePeriod, string Periodicity, IEnumerable<CellData> Cells)
        {
            this.TimePeriod = TimePeriod;
            this.Periodicity = Periodicity;
            this.Cells = Cells;
        }

        public string Periodicity { get; set; }

        public DateTime TimePeriod { get; set; }

        public IEnumerable<CellData> Cells { get; set; }
    }

Now if i change my function to use validation it looks like this:

public static Func<IEnumerable<Validation<string, CellData>>, IEnumerable<Finding>>
            GetAndMergeDbValues = cells =>
            {
                var groupings = from opt in cells
                                from c in opt
                                group c by (c.Success.TimePeriod, c.Success.Periodicity) into g
                                select new QueryGrouping
                                (
                                    g.Key.Item1,
                                    g.Key.Item2,
                                    g
                                );
                ...
            };

I presume I have to explicitly call Success on c because it has two states?

Also I get an error on the contructor of query grouping that it expects IEnumerable<CellData> and I’m passing IEnumerable<Validation<string, CellData>>.

I would expect the Select definition for Validation to only return the good state since this is monadic flow.

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 21 (10 by maintainers)

Most upvoted comments

IEnumerable<Seq<string>> errors = cells
  .Map(v => v.Match(_ => None, Some))
  .Somes();

I think I understand your problem. First, I will share my suggestion and then explain why you were having trouble.

Suggestion

In the Option<> case, I recommend this code:

var groupings = cells
    .Somes()
    .GroupBy(c => (c.TimePeriod, c.Periodicity))
    .Map(g => new QueryGrouping(
        g.Key.Item1,
        g.Key.Item2,
        g));

Then in the Validation<,> case, I recommend reducing to the Option<> case with this code:

var groupings = cells
    .Map(val => val.ToOption())
    .Somes()
    .GroupBy(c => (c.TimePeriod, c.Periodicity))
    .Map(g => new QueryGrouping(
        g.Key.Item1,
        g.Key.Item2,
        g));

Explanation

I would expect the Select definition for Validation to only return the good state since this is monadic flow.

That is true, but the code in your example is not calling Validation<,>.Select; it is calling IEnumerator<ValidationData<,>>.GetEnumerator, which is defined on IEnumerable<ValidationData<,>>.

Each LINQ syntax query can only “unwrap” a single monad (or, more generally, functor). In both of your examples, the single functor in question is IEnumerable<>. Both Option<> and Validation<,> implement IEnumerable<T> for some type T. For Option<A>, we have T = A. For Validation<F, S>, we have T = ValidationData<F, S>. Enumerating over an Option<A> will yield an A it if has one and nothing otherwise. Enumerating over Validation<F, S> will yield exactly one ValidationData<F, S> in the appropriate state and containing the corresponding data.

I presume I have to explicitly call Success on c because it has two states?

Sort of, but the Success property could contain garbage data if the ValidationData<,> instance is in the other state.

By looking at your code, I think you are trying to filter out the instances of Option<> and Validation<,> that are not in the “good” state and then mapping the remaining CellData instances to QueryGrouping instances. (In particular for your Option<> case, you start with cells of type IEnumerable<Option<CellData>> and end with groupings of type IEnumerable<QueryGrouping>.) That is exactly what my suggestions do.