Hacker News new | ask | show | jobs
by 12345hn6789 1041 days ago
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.
3 comments

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.