I'm trying to grok how to deal with functions that turn one "Either" into many "Either"s and how you then merge those back into a single stream.
The following turns one string into many numbers, squaring each and ignoring any errors. Given "1,2,Foo,4" it writes out 1,4,16.
It works but I don't understand why Bind(SafeSplit) returns an EitherData which has a Right property I need to dereference.
private static void ValidationPipelineVersion()
{
var result = Right<Exception, string>("1,2,Foo,4")
.Bind(SafeSplit)
.Bind(numStrs => numStrs.Right.Select(SafeParse))
.Rights() // ignore exceptions for now
.Map(num => num * num) // Squared
.Iter(num => WriteLine(num));
}
private static Either<Exception,string[]> SafeSplit(string str)
{
try
{
return str.Split(",");
}
catch (Exception e)
{
return e;
}
}
private static Either<Exception,int> SafeParse(string str)
{
try
{
return int.Parse(str);
}
catch (Exception e)
{
return e;
}
}
I don't understand why Bind(SafeSplit) returns an EitherData which has a Right property I need to dereference.
You don't have to (in fact, I'd advise against it), but it compiles because the LanguageExt library comes with a Bind
overload with this signature:
public static IEnumerable<R> Bind<T, R>(
this IEnumerable<T> self, Func<T, IEnumerable<R>> binder)
{
return self.BindFast(binder);
}
and since Either<L, R>
implements IEnumerable<EitherData<L, R>>
the code in the OP compiles.
The reason it's not advisable is because the Right
property is partial or unsafe. It works as long as the Either
object is a Right
value, but the whole point of the type is that you rarely know whether or not that's the case.
Here's an alternative expression that may not look much simpler, but at least is safer:
var result = Right<Exception, string>("1,2,Foo,4")
.Bind(SafeSplit)
.Map(numStrs => numStrs
.Map(SafeParse)
.Rights()
.Map(num => num * num) // Square
.Iter(WriteLine));
Instead of trying to get the value out of the monad, it keeps the outer Either
container and performs all actions inside of it.