On Thu, Sep 7, 2017 at 8:00 AM, Tim Uckun <timuc...@gmail.com> wrote:

> I don't see anything wrong with the try catch paradigm, you can choose
> your granularity. If you want to check every call then you can, if you want
> to let a few cluster together you can. You guys make is sound like go style
> error handling is the only way to catch errors at every step. That's not
> true at all.
>

But the argument is specifically that since you should consider and handle
every error specifically, that making *not* doing that easier is bad. Good
error handling (even with a try-catch like mechanism) will end up looking
pretty much the same as it does today - and bad error handling is something
I want to see discouraged.

This is akin to someone coming to Haskell and complaining about all the
boilerplate they have in their code. "Why do I have to have this 'IO'
everywhere? Can't we just make that implicit? And get some loop-constructs
up in here, I don't want to have to think about expressing myself via
recursion all the time. All other modern languages have iteration concepts,
side-effects and mutable data. Maybe be open to some new ideas, instead of
assuming everyone else is wrong?"

How would you react to that? By saying "oh, yeah, good point, let's build
in implicit side-effects and loops and variables"? I don't think so. You'd
tell them that functional programming is about carefully containing
side-effects. To not have mutable state. And that if they are passing an IO
argument everywhere, they should think about restructuring their code and
contain IO to the outermost core that actually needs it, while keeping the
rest purely functional.

I *like* the way go does error passing, *because* it's constrained to
handle errors explicitly. And I think there is a good argument to be made,
that restructuring your program and rethinking your error handling strategy
might help you see it similarly.

Surely something can be done about the boilerplate if err != nil  maybe
> shorten that to iferr or something like that. In fact why can't "err" be a
> language construct where you raise something and it goes into an err[]
> array. At any time you can check to see what's in there. That would clean
> up the code so much.
>
> result = doSomething()
> iferr {
>    error = err.pop()
> }
>
> Isn't that so much more readable?
>

TBH, no. Not only do I not know what this is supposed to be doing (err.pop?
of what? Why are we assigning to error? What is happening here?), at best
the code seems equivalent to the current code, to me. There is a function
call, a conditional to check for error and then some error handling. When
skimming that code, it would look pretty much identical to the current
idiom.

And even if we would, for the sake of argument, assume this is more
readable; this doesn't mean it justifies a new language construct. The
current error-passing mechanism is implemented with basic language
primitives that are already there anyway. That's one of its beauties.

You could even create checkpoints
>
> raiseErrors()
> // from now on all errors are raised and the program halts
> do()
> some()
> tasks()
> collectErrors()
> // from now on program doesn't halt but err[] array is populated
> do()
> other()
> things()
> dealWithErrArray() // it's global so need to pass it in
> raiseErrors()
> // back to raising them.
>
> As I said none of these are particularly nice.  Elixir gives you both ways
>
>
> def register({email, password}) do
>   {:ok, pid} = Membership.connect()
>   sql = "select * from membership.register($1, $2);"
>
>   case Postgrex.Connection.query(pid, sql, [email, password]) do
>     {:ok, res} ->
>       cols = res.columns
>       [first_row | _] = res.rows
>       [new_id, validation_token, auth_token, success, message] = first_row
>       {:ok, %RegistrationResult{
>         success: success,
>         message: message,
>         new_id: new_id,
>         authentication_token: auth_token,
>         validation_token: validation_token
>     }}
>
>     {:error, err} -> {:error, err}
>   end
> end
>
>
> try do
>   opts
>   |> Keyword.fetch!(:source_file)
>   |> File.read!
> rescue
>   e in KeyError -> IO.puts "missing :source_file option"
>   e in File.Error -> IO.puts "unable to read source file"
> end
>
> As somebody who has just started learning go I can't find any
> justification for the insane amount of boilerplate go error handling
> inflicts on the programmer.  Surely there is a a better way.
>
>
>
> On Wednesday, September 6, 2017 at 8:54:38 PM UTC+12, Henry wrote:
>>
>> I use what I would call as *error context*.
>>
>> Here is the simple definition of the *error context*:
>>
>> type ErrorContext interface {
>>     ContainsError() bool
>>     SetError(err error)
>>     Error() error
>> }
>>
>> Here is how you would use it inside the error-prone function:
>>
>> func FragileFunction(ctx ErrorContext) {
>>     if ctx.ContainsError() {
>>         return
>>     }
>>
>>     //some processing ...
>>     //if there is an error
>>     ctx.SetError(errors.New("some error"))
>>     return
>> }
>>
>>
>> It allows you to do this:
>>
>> ctx := NewErrorContext()
>>
>> fridge := GetFridge(ctx)
>> egg := GetEgg(fridge, ctx)
>> mixedEgg := MixAndSeason(egg, ctx)
>> friedEgg := Fry(mixedEgg, ctx)
>>
>> if ctx.ContainsError() {
>>     fmt.Printf("failed to fry eggs: %s", ctx.Error().Error())
>> }
>>
>> Or you can even do this:
>>
>> ctxA := NewErrorContext()
>> ctxB := NewErrorContext()
>> ignored := NewErrorContext()
>>
>> a := DoSomethingOne(ctxA)
>> b := DoSomethingTwo(a, ctxA)
>> c,d := DoSomethingThree(b, ctxB) //different context
>> if ctxB.ContainsError() {
>>    c = 1
>>    d = 2
>> }
>> e := DoSomethingFour(c, d, ctxA)
>> if ctxA.ContainsError() {
>>     fmt.Println("Failed To do A")
>> }
>>
>> DoSomething(e, ignored) //error is ignored
>>
>> It is up to you how you would implement the error context.
>>
>>
>> On Tuesday, September 5, 2017 at 1:27:20 AM UTC+7,
>> marti...@programmfabrik.de wrote:
>>
>>> Hi guys,
>>>
>>> at first I though I really like the idea of how Go deals with error
>>> management and handling, but the more Go code I look at or try to program,
>>> the more I get scared about checking errors every second line in every
>>> given block of code.
>>>
>>> Take a look at this example here from "Build Web Application with
>>> Golang":
>>>
>>> // insert
>>> stmt, err := db.Prepare("INSERT INTO userinfo(username, departname, 
>>> created) values(?,?,?)")
>>> if err != nil {
>>>   // handle error
>>> }
>>> res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09")
>>> if err != nil {
>>>   // handle error
>>> }
>>> id, err := res.LastInsertId()
>>> if err != nil {
>>>   // handle error
>>> }
>>> fmt.Println(id)
>>> // update
>>> stmt, err = db.Prepare("update userinfo set username=? where uid=?")
>>> if err != nil {
>>>   // handle error
>>> }
>>> res, err = stmt.Exec("astaxieupdate", id)
>>> if err != nil {
>>>   // handle error
>>> }
>>> affect, err := res.RowsAffected()
>>> if err != nil {
>>>   // handle error
>>> }
>>>
>>>
>>> Seriously? And yes, I have read https://blog.golang.org/e
>>> rrors-are-values...
>>>
>>> The best case reduction I found is:
>>>
>>> ...
>>> res, err = stmt.Exec("astaxieupdate", id)
>>> checkError(err)
>>> ...
>>>
>>> Still, I need this after each line of calling a function which may
>>> return an error.
>>>
>>> I bet this is not pleasant to do in larger code bases and it also takes
>>> away focus from what is actually happening.
>>>
>>> 50-80% of all lines of code in my example deal with error handling?
>>>
>>> This is not good. Seriously.
>>>
>>> And don't get me wrong, there is a lot of things I really like, love and
>>> adore about Go, but catching errors needs an improved syntax!
>>>
>>> And I am not proposing try...catch here.
>>>
>>> How about introducing a new piece of syntax
>>>
>>> "watch if  .... "
>>>
>>> which tells the compiler to watch out for changes in a given SimpleStmt
>>>
>>> The same code as above would look like this:
>>>
>>> var err Error
>>>
>>> watch if err != nil {
>>>   // handle error(s)
>>> }
>>>
>>> // insert
>>> stmt, err := db.Prepare("INSERT INTO userinfo(username, departname,
>>> created) values(?,?,?)")
>>> res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09")
>>> id, err := res.LastInsertId()
>>> fmt.Println(id)
>>>
>>> // update
>>> stmt, err = db.Prepare("update userinfo set username=? where uid=?")
>>> res, err = stmt.Exec("astaxieupdate", id)
>>> affect, err := res.RowsAffected()
>>>
>>>
>>>    - The "watch if" would be executed after each assignment of any of
>>>    the variables used in SimpleStmt of the statement.
>>>    - Multiple "watch if" would be executed in order or appearance
>>>    - The "watch if" could be used like "defer..." inside functions
>>>    - The "watch if" would work in its full scope of the watched
>>>    variables
>>>
>>> I am not a language expert, so may be there is a saner way of expression
>>> what I want to achieve.
>>>
>>> But bottom line is, there should by an easier to read and write way to
>>> deal with errors in Go.
>>>
>>>
>>> Martin
>>>
>>>
>>>
>>>
>>>
>>> --
> 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+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
>

-- 
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+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to