r/golang•
nordbrain

Monad `Result` in Golang and `try` pattern to error handling for simplicity and shortness without `if err == nil`

Hi there,

I wrote a simple lib for Rust-like error handling based on Result (which in turn is based on ideas of Haskell's Either monad).

https://github.com/nordborn/mo

No `if err == nil` needed. Dead simple error handling whith complete error tracking.
Example:

func processFile(fname string) (res mo.Result[string]) {
    // catcher-wrapper, place it in each func with Try
    defer mo.Catch(&res)

    // convert `val, err` into Result, then Try with extra context (usually omitted)
    file := mo.ResultFrom(openFile(fname)).Try("open file")

    // `readFile` returns Result[string], Try to get containing data or early return
    data := readFile(file).Try()

    ....

    // return successful result by wrapping data into Result
    return res.WithOk(data)
}

If err occurs (Result with error), then Try will trigger early return: it'll be paniced, catched in place and wrapped with useful tracking info like `module.func(args): file:line: err reason`.

It's very close to Rust's `?` approach (extract val or early return with Err), which was initially a `try` macros.

If you finally need to log the err or handle it your way, then just `Unpack()` the `Result`. `Unpack()` converts `Result` back to `val, err`

data, err := processFile(fname).Unpack()
if err != nil {
    logger.Err.Println(err)
    ...
    return ...
}

The log output be like:
```
[ERR] 2025/01/01 02:59:10 somefile.go:28: module.(*Acceptor).Method(Arg=12345): <path/to_file.go:29>: try err: pg.(*PgStore).GetData(12345): <pg/data.go:18>: try err: sql: no rows in result set
```

There is usefult `res.WithOK(data)` to return the updated result without boilerplate generic type definitions.
And those `mo.ResultFrom` and `Unpack` make it easy to switch from/to usual error handling way.

The lib is in production use.

As `Try` on err will be intercepted in place, then there is no overhead due to unwinding, as well as it's usually not a common case to get a huge amount of errors, thus, the overhead IMO is acceptable and the code base becomes much more clean and clear (remember Haskell, Rust).

'mo' means 'monadic operations' but it focuses on doing simple work with Result and Option, I actually don't suppose to extend it significantly.

That's all. Try it in your current or next project)

Posted Jul 5, 2025, 11:44 AM UTC

01
Loading comments…