logoalt Hacker News

hackingonemptylast Tuesday at 5:39 PM3 repliesview on HN

Generators and Goroutines have keywords/syntax in Golang but now they don't want to pile on more to handle errors. They could have had one single bit of syntactic sugar, "do notation", to handle all three and more if they had considered it from the beginning but it seems too late if the language designers are even aware of it. TFA says "If you’re wondering if your particular error handling idea was previously considered, read this document!" but that document references languages with ad-hoc solutions (C++, Rust, Swift) and does not reference languages like Haskell, Scala, or OCaml which have the same generic solution known as do-notation, for-comprehensions, and monadic-let respectively.

For example instead of

  func printSum(a, b string) error {
      x, err := strconv.Atoi(a)
      if err != nil {
          return err
      }
      y, err := strconv.Atoi(b)
      if err != nil {
          return err
      }
      fmt.Println("result:", x + y)
      return nil
  }
they could have something like this:

  func printSum(a, b string) result[error, unit] {
      return for {
          x <- strconv.Atoi(a)
          y <- strconv.Atoi(b)
      } yield fmt.Println("result:", x + y)
  }

which desugars to:

  func printSum(a, b string) result[error, unit] {
      return strconv.Atoi(a).flatMap(func(x string) result[error, unit] {
          return strconv.Atoi(b).map(func(y string) unit {
              return fmt.Println("result:", x + y)
          }
      }
  }
and unlike ad-hoc solutions this one bit of syntax sugar, where for comprehensions become invocations of map, flatMap, and filter would handle errors, goroutines, channels, generators, lists, loops, and more, because monads are pervasive: https://philipnilsson.github.io/Badness10k/escaping-hell-wit...

Replies

tayo42last Tuesday at 11:05 PM

I don't think in real code you generally want to return "err" directly, but add some kind of context to made debugging easier. Its the same problem Rusts `?` has.

How do you do that with this suggestion?

VirusNewbielast Tuesday at 7:43 PM

I absolutely agree with this, and would be better than all of the other proposals.

Did anyone propose this in one of the many error handling proposals?

show 1 reply
9rxlast Wednesday at 7:33 PM

> they could have something like this:

Could in some purely theoretical way, but this is pretty much exactly the same, minor syntax differences aside, as virtually every other failed proposal that has been made around this. It would be useless in practice.

In the real world it would have to look more like this...

    func printSum(a, b string) result[error, unit] {
        return for {
            x <- strconv.Atoi(a) else (err error) {
                details := collectDetails(a, b)
                stop firstConvError{err, details}
            }
            y <- strconv.Atoi(b) else (err error) {
                stop secondConvError{err}
            }
        } yield fmt.Println("result:", x + y)
    }
or maybe something like this

   func printSum(a, b string) result[error, unit] {
        return for {
            x <- strconv.Atoi(a)
            y <- strconv.Atoi(b)
        } yield fmt.Println("result:", x + y)
    }

    handle(printSum, "x", func(vars map[string]any, err error) {
        details := collectDetails(vars["a"].(string), vars["b"].(string))
        return firstConvError{err, details}
    }

    handle(printSum, "y", func(vars map[string]any, err error) {
        return secondConvError{err}
    }
...because in that real world nobody just blindly returns errors like your contrived example shows[1]. There are a myriad of obvious (and many not so obvious) problems with that.

Once you start dealing with the realities of the real world, it is not clear how your approach is any better. It is pretty much exactly the same as what we have now. In fact, it is arguably worse as the different syntax doesn't add anything except unnecessary complexity and confusion. Which is largely the same reason why all those aforementioned proposals failed. Like the linked article states, syntax is not the problem. The problem is that nobody to date knows how to solve it conceptually without completely changing the fundamentals of the language or just doing what you did, which is pointless.

And there is another obvious problem with your code: result[error, unit], while perfectly fitting in other languages designed with that idea in mind, is logically incorrect in the context of Go. They are not dependent variables. It doesn't make sense to make them dependent. For it to make sense, you would, as before, have to completely change the fundamentals of the language. And at that point you have a band new language, making any discussion about Go moot. That said, I think the rest of your idea could be reasonably grafted onto the (T, error) idiom just as easily. However, it still fails on other problems, so...

[1] Which, I will add, is not just speculation. The Go team actually collected data about this as part of their due diligence. One of the earlier proposals was on the cusp of acceptance, but in the end it wasn't clear who – outside of contrived HN comments – would ever use it due to the limitations spoken of above, thus it ultimately was rejected on that basis.