2

Is anyone aware of a way to simplify error handling when doing functional programming in C# using the Language-Ext library, similar to the error propagation operator that Rust has?

For example, I want to simplify the error handling in the Create method below:

using LanguageExt;
using LanguageExt.Common;

using static LanguageExt.Prelude;

public class ProductAggregate
{
    public string Name { get; private set; } = string.Empty;

    public static Fin<ProductAggregate> Create(string name)
    {
        var product = new ProductAggregate();

        // I find this too verbose and looking to simplify:
        if (product.SetName(name) is { IsFail: true } errFin)
        {
            return (Error)errFin;
        }

        return product;
    }

    public Fin<Unit> SetName(string name)
    {
        if (string.IsNullOrWhiteSpace(name))
        {
            return Error.New("Product name cannot be null or empty.");
        }

        Name = name;

        return unit;
    }
}

Is there any way I can shorten that up? I really love how Rust does this:

fn get_current_date() -> Result<String, reqwest::Error> {
  let url = "https://postman-echo.com/time/object";
  let res = reqwest::blocking::get(url)?.json::<HashMap<String, i32>>()?;
  let date = res["years"].to_string();
  Ok(date)
}

The ? operator above is chained with further steps, but on itself, it basically check if the left member or call return is an error Result and returns that error then and there. Of course it must match the error type of the enclosing function's return type (reqwest::Error).

Is there a way to achieve something close to as elegant as this, but in C#?

EDIT: Tried to take Mark Seemann's advice about code that should provide a minimal, reproducible example.

Paul-Sebastian Manole
  • 2,538
  • 1
  • 32
  • 33
  • There's a lot of code in that question, and it refers to other code that we don't know the definition of. Please post a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example), preferably by simplifying the code, instead of adding more. – Mark Seemann Jun 13 '22 at 08:36
  • @MarkSeemann, thanks Mark. You are right. I always forget that the code that fits in my head at one moment, might not fit easily in someone else's head when reading it for the first, let alone when trying to help someone with their question in a hurry. – Paul-Sebastian Manole Jun 19 '22 at 21:28
  • Thank you for the repro. The answer given by @stb looks like the way to go. – Mark Seemann Jun 22 '22 at 01:14

1 Answers1

3

You can use LINQ syntax to bind (see e.g. LanguageExt-Wiki):

var result = from p in ProductAggregate.Create()
             from _1 in p.SetName(...)
             from _2 in p.SetCategory(...)
             select p;

This works with any type that maybe contains a "right" value: Option, Either, Try ...

(Added via Edit:) Here's a rewrite of Create

 using static LanguageExt.Prelude;

 public static Fin<ProductAggregate> Create(string name) =>
    from product in FinSucc(new ProductAggregate())
    from _1 in product.SetName(name)
    select product;
stb
  • 772
  • 5
  • 15
  • I'm still learning this library so this was informative, but it's not what I'm looking for, or I didn't understand your answer completely. Either way, I'm looking to simplify the call to `product.SetName()` inside the `Create()` method, such that I don't have to check that the `Fin` is an error and that that error is just returned (propagated) from `Create()` more elegantly. I also dislike having to cast to `Error`. Is there a way to get the `Error` instance out of `Fin` more elegantly? Like without using `.Match()` or other composition helpers. – Paul-Sebastian Manole Jun 19 '22 at 21:43
  • 1
    Hi, I added code snippet rewriting `Create`. The point is to create a `Fin` in the first `from` of LINQ expression and afterwards use `bind` (SelectMany) via `from` to pass down the first error value. This might not be better than your original code -- depends a lot on your situation. I prefer LINQ style despite sometimes resulting in an ugly first line (first `from` sometimes is a dummy from). Ideally you have pure expressions for everything. But ... it depends. – stb Jun 19 '22 at 22:09
  • 1
    Maybe check out `when`/`unless`/`guard`/`guardnot` introduced some time ago in 4.0: https://github.com/louthy/language-ext/releases/tag/v4.0.0 – stb Jun 19 '22 at 22:15