Preferences

`Either` works pretty well in Go. I implemented it and it felt reasonably close to Rust/Haskell (without `try!` of course).

I don't think you should bolt every concept into every language. One USP of Go is how dead simple it is to learn and how fast it compiles (because it is dead simple - compare it to Rust compile times). I did a lot of Lisp and Haskell, scaled a startup on FP Scala code and sold it. I did put Option everywhere. In the 2000s I came up with the Iterable hack for Option<> in Java

   public abstract class Option[T] implements Iterable[T] { }

   // Some then is a one element Iterator/ None is an empty iterator

   // With for you then can do something on Some
   // orElse left as an exercise to the reader
   
   for (String name: option) {
      // do something with name
   }
but today I think one should embrace the language and if it does not work for you, use something else.

For Go I think something like Zigs !i32 would fit in perhaps, if one wants a higher level of error handling.

I agree about cramming features across languages. Plus, Software dev is social. I only did this in code for myself.

That being said though, it actually fit really well in golang. Allowed functions that used to return ‘null, err’ to return an Either, which improved on all the downsides of returning null (if you return null your callers have to check for it).

It actually improved the ergonomics quite a bit. ‘Either’ fits nicely into golang, but I doubt it will become mainstream anytime soon.

Either is nearly in the language anyways. The vast majority of pragmatic go functions will return [Result, Error] and or just [Error]. We are only missing support to treat this as a monad.
You don't need support. Monad composition isn't a special feature it can be implemented directly.

Not an expert in Go but I think you can do this:

   func compose[A any, B any, C any](a func(A) (B, error), b func(B) (C, error)) func(A) (C, error) {
      return func(aInp A) (C, error) {
        res, err := a(aInp)
        if err == nil {
           return b(res)
        } else {
           return *new(C), err
        }
      }
   }
The above is equivalent to haskells fish operator >=>

The bind operator (>>=) can be implimented in terms of composition:

   func bind[A any, B any, C any](a func(A) (B, error), b func(B) (C, error), aInput A) (C, error) {
      return compose[A, B, C](a, b)(aInput)
   }
To me, the problem is that Go returns two values which makes it hard to compose functions.
That's deliberate to stop you trying to compose functions that return errors. You should be explicitly handling the errors before you compose.
great, except 99% of the time I want to handle the error by bubbling it up to a top level routine.

From my experience, "handling" the error means wrapping it in another error and returning, which is what you get from other languages for free.

Either as a pattern is _used_ everywhere, but the standard library in go doesn't have one (people just use a tuple), so it's _always_ encoded wrong. It's very annoying.
There is no tuple type in go. When you see a func() (A, error) that is not a function returning a tuple. It is a function returning two values that cannot be composed. You can't A(B()) if B returns two values. It's pure comedy.
> You can't A(B()) if B returns two values.

You can if A is a function which takes two arguments, e.g.

  package main

  import "log"

  func A(x, y int) { log.Printf("%d, %d", x, y) }
  func B() (int, int) { return 1, 2 }
  func main() { A(B()) }
https://go.dev/play/p/Jp4B0L6NJj2
What you can't do is a(b(), c()) unless b and c return single values.

And there are no slices or channels of tuples, return values must be destructured right away.

This item has no comments currently.

Keyboard Shortcuts

Story Lists

j
Next story
k
Previous story
Shift+j
Last story
Shift+k
First story
o Enter
Go to story URL
c
Go to comments
u
Go to author

Navigation

Shift+t
Go to top stories
Shift+n
Go to new stories
Shift+b
Go to best stories
Shift+a
Go to Ask HN
Shift+s
Go to Show HN

Miscellaneous

?
Show this modal