On the topic of try() and errors

267 views
Skip to first unread message

Michael Jones

unread,
Jul 9, 2019, 7:46:03 PM7/9/19
to golang-nuts
In the hope of elevating the discussion of late, I've made a little speech as one might do at a lunchtime discussion with students or colleagues. Maybe this will make sense to some kindred spirits here...

Thoughts about facilitating error handling in Go(), and the try() proposal in particular, have inspired a great deal of thought and sharing, with heated disapproval by some and, it seems, general approval by many. In a sense this is all premature--the proposal is a test and the good/bad judgement will come over time.

One aspect of the topic that seems to cause alarms is the fact it looks like and in some ways acts like error-related try() features in C, C++, C#, Java, JavaScript, and SQL. This invites presumption of harm through similarity. The goal here is to offer an entirely different way to think about the issue. In this alternate view, some aspects are more readily understood and extensions and refinements more inviting. So, from here out, it is not about errors.

In 1969, C. A. R. Hoare, Mr. Quicksort and the father of the ideas in Go's channels, was inspired to seek a logical, mathematical basis for computer programming. He, Nicholas Wirth, Ole-Johan Dahl, and Edsger Dijkstra were marching down this path and they all realized that guarded computation was important; that statements of provable truth are the basis of rigor.

assert.002.jpeg
In my Fig. 1 here, you can see the problem, the result of math.Sqrt() is valid or invalid depending on the status of x; x in the domain of non-negative real numbers means Sqrt is good and x out of that domain means the opposite. 

As a starting point, Hoare and the others embraced AJT's call for assertions, such as is shown in Fig 2. as is typical now and then. Let's call it an assertion statement. (Hoare actually proposed a keyword version, "assert <boolean expression>;" that would return with prejudice if the bool was false.)

We all know this. But, are other kinds of assertions interesting, beyond statements? Here are two others, the assertion expression and the assertion assignment, along with Alan Turing's original 1949 appeal for assertions: 

assert.003.jpeg
What I've shown here is not something I'm aware of being implemented before, but both Fig. 3 and 4 show consistent specializations of assertion statement to substatements; in the one case, to a function's arguments and in the other, to the values being assigned. If we had these inline assertions, we could call functions with range checking clear, local, and concise, and assign return values with stated conditions known to have been proven. To quote Turing, "...from which the correctness of the whole program easily follows."

There is a subtle shift here, from asserting that x>=0 which is specific, to insisting that Sqrt() has not complained about a domain error, which is specific in meaning but omits specifying what that range might be. (AssertDomain(math.Log(x)) would have the same meaning but a different domain.)

Perhaps Figure 4 looks familiar to you. Compare to Fig. 5...

assert.004.jpeg
...as you can see, we've come to the great debate, but along a different path. If you can pretend that "try" did not have a decade of bitter meanings from other languages and common misuse, then you can see try as nothing more than an assertion assignment. In this view, disjoint from the error type, we can also see that other "same meaning different detail" uses could make sense, just like the Domain assertion of Fig 4. If I have test for error value != nil, then why not test for non-error pointer value == nil as in Fig. 6. Not only does this seem useful and thematically consistent, it happens to already be in Go!

Where you ask? Where has this nil-pointer-try been hiding? Right here:
assert.005.jpeg
So now we reach my destination: Go's "must" is an assertion at its heart. That your program is executing means "it must have or else we'd not be here." It is an extreme, fatal must. Assertions considered previously range from the fatal-must of the assertion statement to the return-with-error of the assertion assignment.

Should "try" be "must"? Should musts take an optional error argument that defines what is returned? Is there a beautiful way to unify compile-time must, init-time must, and runtime must? I'll not comment as my goal here is not to propose, rather to encourage each of us to offer insights that convey the wisdom of those who came before us as well as learnings from our own experience.

Michael

--
Michael T. Jones
michae...@gmail.com

pierre...@gmail.com

unread,
Jul 10, 2019, 3:52:22 AM7/10/19
to golang-nuts
Interesting reasoning Michael...

Tyler Compton

unread,
Jul 10, 2019, 5:33:23 PM7/10/19
to Michael Jones, golang-nuts
First, I want to say that I really appreciate the effort you put into this write-up!

Is there a beautiful way to unify compile-time must, init-time must, and runtime must?

This is an interesting topic. I'm not sure exactly what you're referring to by a "compile-time must", but it does seem achievable to unify the init-time and runtime musts. Theoretically, "try" could be specified to panic instead of return an error when used outside a function, so it could be used instead of template.Must and regexp.MustCompile in many circumstances. This would allow it to better fill the role of a conceptually general assertion tool. As it turns out, the design doc mentioned something similar as being considered in an earlier proposal, albeit for circumstances where "try" is used in a function that doesn't return an error:

In one of our earlier attempts at specifying try (see the section on Design iterations, below), try was designed to panic upon encountering an error if used inside a function without an error result. This enabled the use of try in unit tests as supported by the standard library’s testing package.

Not everyone agrees, but find "try" as it is today to be conceptually sound. In Go, when we write functions that return one or more results followed by an error, we're essentially creating a Result<T>, where T is a tuple of one or more non-error result values. The function either outputs the result values, or it outputs an error. "try" is a tool that branches execution based on what kind of output a function returns. Conceptualizing it instead as an assert expression is an interesting idea, but I'm not sure if it's a clearer one. It would follow that an assert expression could mean different things in different contexts (bubble up the error in a function, panic at the file level, etc), but at first blush I feel it would make the concept harder to understand.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CALoEmQzYASyns3wWXc3820c-KETZfVLpaA_%3Dnq06%3DJKc7aQuqg%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Andrey Tcherepanov

unread,
Jul 12, 2019, 1:42:37 AM7/12/19
to golang-nuts
Michael,

Wonderful reasoning! Your post did reach the goal of "encourage to offer insights", because now I have "now what?" stuck in my head.

My first reaction was "holy ..., it is an assert after all!" Well, "assert" + gentle return instead of panic as you pointed out.

thing is ... how would we use that in making error handling shorter(optional?) and more readable(must have)?...

func f()(string, error){
   f
, err := os.Open("file")
   
assert f!=nil, err==nil // or assert f!=nil && err == nil
   defer f
.Close()
   
//do things with f that assumes it is not nil
}

or 

func f()(string, error){
   f
:= assert os.Open("file") && f != nil
   defer f.Close()
   
//do things with f that assumes it is not nil
}



does not strike me as a more readable.

(And I don't find try() to be appealing for being so much associated with "try-catch", and for having function syntax to really hide the "return from inside".)

Andrey
Reply all
Reply to author
Forward
0 new messages