louthy / language-ext

C# functional language extensions - a base class library for functional programming
MIT License
6.48k stars 417 forks source link

Flatten Either<U,V> to a single value #590

Closed SuheylZ closed 5 years ago

SuheylZ commented 5 years ago

Consider this code:

Either<string, (int, string)>  data; 
 ...

var result = string.Empty;

 data.Match(
      x=> result = $"{x.Item1}-{x.Item2}",
      y=> result = y
     );

return result;

Instead of the above, is there a way to achieve this: return data.Match(x=>$"{x.Item1}-{x.Item2}", y=> y);

TysonMN commented 5 years ago

How about this?

return data.IfRight(x => $"{x.Item1}-{x.Item2}")
SuheylZ commented 5 years ago

what about the left side? i need both

TysonMN commented 5 years ago

I'm sorry. I don't know what you mean. The code I wrote is equivalent to the two code examples that you gave.

Can you give another example of what you want?

SuheylZ commented 5 years ago

actually consider this that left is as valid as right but only one at a time. so you have the same line of code but sometimes you have the value in right and sometimes in left. you want two functions to Map the value to the destination type which is the same for both left and right. so for instance

Either<(int id, EmailTypes type, string notes), (int id, PhoneTypes type)>  data; 
 ...

var rec  = new DataRow;

 data.Match(
      x=> rec = new DataRow(x.id, (int)x.type),
      y=> rec = new DataRow(y.id, (int)y.type, y.notes)
     );

 _repository.Save(rec);

as you can see we are changing rec but at the same time we are not ignoring left side. When the left side is valid, it is as much important as the right one and Match() does not return value as it takes an Action<> not Func<>

so what I'm looking for is this and this is possible only if Match() can also take Func<>

 _repository.Save( data.Match(
      x=> new DataRow(x.id, (int)x.type),
      y=> new DataRow(y.id, (int)y.type, y.notes))
);

when you wrote this

return data.IfRight(x => $"{x.Item1}-{x.Item2}")

What happens if it is Left and not right? a None perhaps?

louthy commented 5 years ago

and this is possible only if Match() can also take Func<>

It does. All Match methods for all types have Func and Action variants.

You can use Match to map the types: you're using the Action variants for some reason, but it isn't necessary - the Func variants of Match does what you need.

A more elegant way if you're going to be mapping to a common type if to use BiMap, Map, or MapLeft to unify the left and right types.

You could also create an extension method for getting the common bound value:

    public static A ToValue<A>(this Either<A, A> ma) =>
        ma.Match(identity, identity);

So, on your first example:

Either<string, (int, string)>  data; 

var result = data.Map(x=> $"{x.Item1}-{x.Item2}").ToValue();

Which is the same as:

Either<string, (int, string)>  data; 

var result = data.Match(Right: x=> $"{x.Item1}-{x.Item2}", Left: identity);

Your second example:

Either<(int id, EmailTypes type, string notes), (int id, PhoneTypes type)>  data; 

var rec = data.BiMap(Right: x => new DataRow(x.id, (int)x.type),
                     Left:  y => new DataRow(y.id, (int)y.type, y.notes))
              .ToValue();

Which is the same as:

Either<(int id, EmailTypes type, string notes), (int id, PhoneTypes type)>  data; 

var rec = data.Match(Right: x => new DataRow(x.id, (int)x.type),
                     Left:  y => new DataRow(y.id, (int)y.type, y.notes));

Stop using the imperative setting of values within the Action delegates, use the Func variants and return a value, it'll just get you into trouble eventually. The Action variants are for launching side-effecting operations, not for mapping to values.