-
Notifications
You must be signed in to change notification settings - Fork 167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Question] More elegant solution than nested Match #195
Comments
What you're looking for is called Monadic Binding. There are several approaches you can take:
Create a pseudo-monadic Bind extension yourselfusing OneOf;
public static class OneOfExtensions
{
public static OneOf<TResult, TError> Bind<T, TResult, TError>(
this OneOf<T, TError> oneOf,
Func<T, OneOf<TResult, TError>> func)
{
return oneOf.Match<OneOf<TResult, TError>>(
error => error,
value => func(value)
);
}
} using OneOf;
using System.Xml.Linq;
public class ValidationResult
{
public ValidationResult(Exception ex) { /* handle exception */ }
public ValidationResult() { /* success */ }
}
public OneOf<XDocument, Exception> ParseIntoXDoc(string xml)
{
try
{
return XDocument.Parse(xml);
}
catch (Exception ex)
{
return ex;
}
}
public OneOf<Version, Exception> ExtractVersion(XDocument doc)
{
try
{
// Extract version logic
return new Version("1.0.0");
}
catch (Exception ex)
{
return ex;
}
}
public ValidationResult ValidateXML(Version ver, XDocument doc)
{
// Validation logic
return new ValidationResult();
}
public ValidationResult ValidateXML(string xml)
{
return ParseIntoXDoc(xml)
.Bind(doc => ExtractVersion(doc)
.Bind(ver => ValidateXML(ver, doc)));
} OneOf.MonadsHere you have an example using the Result Monad var result = GetLeague(new Query(leagueId, season))
.Bind(GetSeason)
.Bind(GetParticipants)
.Bind(GetWinner)
.Map(winner => winner.Name)
.DefaultWith(error => error.Reason); CSharpFunctionalExtensionsHere you have an example of fluent monadic binding return _customerRepository.GetById(id)
.ToResult("Customer with such Id is not found: " + id)
.Ensure(customer => customer.CanBePromoted(), "The customer has the highest status possible")
.Tap(customer => customer.Promote())
.Tap(customer => _emailGateway.SendPromotionNotification(customer.PrimaryEmail, customer.Status))
.Finally(result => result.IsSuccess ? Ok() : Error(result.Error)); LanguageExtHere you have an example using the Result Monad var actual = await GetFolder("123")
.Bind(folder => CreateFolder("New folder", folder.Id))
.Bind(newFolder => UploadFile(stream, "New file name.txt", "text/text", newFolder))
.Match(fileId => handleFileId(fileId), ex => handleException(ex)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I am dealing with parsing and validating xml and I wanted to avoid
throw
, so I decided to try OneOf, but how do I correctly handle multiple nested OneOf.Match() to not make them look like a ladder?For example this - each call can result in XmlException being returned instead of whatever I want.
Normally I would just have
if
after everything to check if I got null due to whatever reason, but OneOf simplifies it to this:After a few more checks like above it will look like
if
"ladder" and I really dislike that. Is there a more elegant solution to this?The text was updated successfully, but these errors were encountered: