I'm new to functional way of thinking in C# (well... not limited to language). Let's say there's method:
T LoadRecord<T>(int id)
Concept
1. Validation
When invalid input is given, I should return something like Either<IEnumerable<ValidationError>, T>
.
2. Execution
When calling DB/API/... which could throw, I should return Either<Exception, T>
.
3. Some or none record
Because entry may or may not exist I'm returning Option<T>
.
Final signature
If you combine all above, you end up with this:
Either<IEnumerable<ValidationError>, Either<Exception, Option<T>> LoadRecord<T>(int id)
I can introduce types such as:
Validation<T>
(~: Either<IEnumerable<ValidationError>, T>
)Result<T>
(~: Either<Exception, T>
)
Now, signature of my method would look like:
Validation<Result<Option<T>>> LoadRecord<T>(int id)
Usage
return LoadRecord<User>(1).Match(
vl => BadRequest(),
vr => vr.Match(
el => StatusCode(500),
er => er.Some(u => Ok(u))
.None(NotFound());
Imperative implementation
try
{
var u = LoadRecord<User>(id);
if (u == null)
{
return NotFound();
}
return Ok(u);
}
catch (ValidationException)
{
return BadRequest();
}
catch (Exception)
{
return StatusCode(500);
}
Question
As you can see signature of method is still quite odd - at first sight it gives you "validation", but I'm really asking for T
. Usage is also not very pretty looking, but indeed more concise than imperative one.
Is this correct approach? Is there way how to improve signature/readability of code?