diff --git a/_posts/2024-10-24-collecting-and-handling-result-values.html b/_posts/2024-11-18-collecting-and-handling-result-values.html similarity index 95% rename from _posts/2024-10-24-collecting-and-handling-result-values.html rename to _posts/2024-11-18-collecting-and-handling-result-values.html index 5d82ee86..93b75ad9 100644 --- a/_posts/2024-10-24-collecting-and-handling-result-values.html +++ b/_posts/2024-11-18-collecting-and-handling-result-values.html @@ -2,7 +2,7 @@ layout: post title: "Collecting and handling result values" description: "The answer is traverse. It's always traverse." -date: 2024-10-24 9:25 UTC +date: 2024-11-18 7:39 UTC tags: [Functional Programming] --- {% include JB/setup %} @@ -12,13 +12,13 @@ {{ page.description }}
- I recently came across a Stack Overflow question about collecting and handling sum types (AKA discriminated unions or, in this case, result types). While the question was tagged functional-programming, the overall structure of the code was so imperative with so much interleaved I/O that it hardly qualified as functional architecture. + I recently came across a Stack Overflow question about collecting and handling sum types (AKA discriminated unions or, in this case, result types). While the question was tagged functional-programming, the overall structure of the code was so imperative, with so much interleaved I/O, that it hardly qualified as functional architecture.
Instead, I gave an answer which involved a minimal change to the code. Subsequently, the original poster asked to see a more functional version of the code. That's a bit too large a task for a Stack Overflow answer, I think, so I'll do it here on the blog instead.
- Further comments and discussion on the original post reveal that the poster is interested in two alternatives. I'll start with the alternative that's only discussed, but not shown in the question. The motivation for that is that this variation is easier to implement than the other one, and I consider it pedagogical to start with the simplest case. + Further comments and discussion on the original post reveal that the poster is interested in two alternatives. I'll start with the alternative that's only discussed, but not shown, in the question. The motivation for this ordering is that this variation is easier to implement than the other one, and I consider it pedagogical to start with the simplest case.
I'll do that in this article, and then follow up with another article that covers the short-circuiting case. @@ -83,7 +83,7 @@
results
object is a sequence of tasks. If we consider Task as a surrogate for IO, each task should be considered impure, as it's either non-deterministic, has side effects, or both. This means that we can't pass results
to a pure function, and that frustrates the ambition to structure the code as an Impureim Sandwich.
- This is one of the most common problems in functional programming, and the answer is usually: Use a traversal. + This is one of the most common problems in functional programming, and the answer is usually: Use a traversal.
IEnumerable<OneOf<ShoppingListItem, NotFound<ShoppingListItem>, Error>> results = @@ -127,7 +127,7 @@It's also possible to inline the
seed
value, but here I defined it in a separate expression in an attempt at making the code a little more readable. I don't know if I succeeded, because regardless of where it goes, it's hardly idiomatic to break tuple initialization over multiple lines. I had to, though, because otherwise the code would run too far to the right.- The lambda expression handles each result in
results
and usesMatch
to append the value to its proper 'bucket'. Theresult
is a tuple with the three collections. + The lambda expression handles eachresult
inresults
and usesMatch
to append the value to its proper 'bucket'. The outerresult
is a tuple of the three collections.Saving the changes and returning the results # @@ -148,7 +148,7 @@
Accumulating the bulk-update result #
- So far, I've assumed that the final
BulkUpdateResult
class is just a simple immutable container without much functionality. If, however, we add some copy-and-update functions to it, we can use that to aggregate the result, instead of an anonymous tuple. + So far, I've assumed that the finalBulkUpdateResult
class is just a simple immutable container without much functionality. If, however, we add some copy-and-update functions to it, we can use them to aggregate the result, instead of an anonymous tuple.
internal BulkUpdateResult Store(ShoppingListItem item) => @@ -191,7 +191,7 @@Parallel Sequence #
- If the tasks you want to traverse are thread-safe, you might consider making it concurrent. You can use Task.WhenAll for that. It has the same type as
Sequence
, so if you can live with the extra non-determinism that comes with parallel execution, you can use that instead: + If the tasks you want to traverse are thread-safe, you might consider making the traversal concurrent. You can use Task.WhenAll for that. It has the same type asSequence
, so if you can live with the extra non-determinism that comes with parallel execution, you can use that instead:
internal static async Task<IEnumerable<T>> Sequence<T>(this IEnumerable<Task<T>> tasks)