[RFC] Watching err with "watch" Expression Block [RFC]

280 views
Skip to first unread message

Wojciech S. Czarnecki

unread,
Jan 15, 2015, 8:02:49 AM1/15/15
to golan...@googlegroups.com
Dear Gophers, what do you think about sketched below construct?

func () (rv int, err error) {

watch (err != nil) {

// switch over err, print err, whatever

return rv, err
}

// ......
result, err := callfunc() // watched err
// ......

return
}

// Watch statements
//
// "Watch" statements specify the conditional execution a Block of code
// according to the value of a boolean expression. The expression is
// checked every time any variable used in said expression has
// been assigned to in a preceding simple statement.
//
// WatchStmt = "watch" Expression Block

// One may think about 'watch' implicitly evaluating right
// before "invisible semicolon" that ends the preceding statement:
// E.g.
// watch (var != nil)
// ...
// Statement_not_assigning_to_var // ; "invisible semicolon"
// ...
// Statement_assigning_to_var // ; "invisible semicolon"
// // "invisibly perform a watch"
// // ; "invisible semicolon"
//


--
Wojciech S. Czarnecki
^oo^ OHIR-RIPE

Ian Lance Taylor

unread,
Jan 15, 2015, 11:11:17 AM1/15/15
to Wojciech S. Czarnecki, golang-nuts
Besides the obvious issue of spooky action at a distance, there is a
question of what should happen if the variable is changed in nested
function.

func F() (err error) {
watch (err != nil) {
return error.New("watch")
}
defer func() {
err = error.New("defer")
return err
}()
return
}

Now what happens? If the answer is not reasonably obvious, then there
is something wrong with the construct.

Ian

anl...@gmail.com

unread,
Jan 15, 2015, 11:17:11 AM1/15/15
to golan...@googlegroups.com

Wojciech S. Czarnecki

unread,
Jan 15, 2015, 12:13:15 PM1/15/15
to Ian Lance Taylor, golang-nuts, golang-nuts
Dnia 2015-01-15, o godz. 08:10:59
Ian Lance Taylor <ia...@golang.org> napisał(a):


> Besides the obvious issue of spooky action at a distance

This is a valid concern. And strong argument against the 'watch'
construct. Though I got "the watch" idea while pondering on
current "too many ; err != nil" threads and proposed patterns.
Some of them much convulted.

> what should happen if the variable is changed in nested function.
I meant _preceding_*simple*_statement_, i.e. an Assignment. Nothing more.
So nothing should happen. Compiler will not put the watch check, as nested
function definition (and a deffered call to) are not simple statements.

> > // been assigned to in a preceding simple statement.
_______________________________________^^^^^^__________

>
> func F() (err error) {
> watch (err != nil) {
> return error.New("watch")
> }
> defer func() {
> err = error.New("defer")
> return err
> }()
> return
> }
>
> Now what happens? If the answer is not reasonably obvious, then there
> is something wrong with the construct.

Good example.

For me the answer is obvious: neither anonymous func definition is a
simple statement nor the call to it assigns to the being watched variable is.
So there is no place for an "invisible watch check".

As to whether deffered anonymous functions should be allowed to have inner
watchers: I sincerely do not know. Though likely it would be good for
consistency.

>
> Ian

Wojciech S. Czarnecki

unread,
Jan 15, 2015, 12:17:59 PM1/15/15
to anl...@gmail.com, golan...@googlegroups.com
Dnia 2015-01-15, o godz. 08:17:11
anl...@gmail.com napisał(a):

> what's the advantage compared to
> https://github.com/goerr/goerr/tree/master/test/err0

I do not know if there is any advantage over an avaliable package.
The 'watch' construct is curently a pure idea to discuss.
Not even a proposal.
Message has been deleted

DV

unread,
Jan 15, 2015, 12:48:19 PM1/15/15
to golan...@googlegroups.com
The proposed patterns *are* indeed convoluted, yes. Not too sound too offensive, but I think 99% of the github.com "let's-make-errors-better" projects are convoluted and completely unnecessary as well. 

Here, this can be enlightening:

Errors are really *not* that difficult in Go, nor are they verbose, nor do we need 9 different special packages to make them "more usable", IMO. 

Klaus Post

unread,
Jan 16, 2015, 7:11:04 AM1/16/15
to golan...@googlegroups.com
Hi!

Other than the already mentioned issues, what would happen if:

func Foo() error {
  var int i
  watch(i != 0) {
    return error.New("watch") 
  }
  Bar(&i)
}

func Bar(i *int) {
  [...]
  *i = 1
  [..]
}

Would Bar just terminate mid-function?
Should watches follow pointers? If not, what if a function takes the pointer to a watched variable and changes it?
What if a pointer to a watched expression is stored in a struct that is returned or handed to another function?

/Klaus

Nico

unread,
Jan 16, 2015, 8:07:43 AM1/16/15
to golan...@googlegroups.com
I'd say that this proposal is equivalent to what in other languages are
called exceptions.

The proposed notation:

func () (rv int, err error) {
watch (err != nil) {
// switch over err, print err, whatever
return rv, err
}

// ......
result, err := callfunc() // watched err
// ......

return
}

could be re-written using the traditional notation as:

func () (rv int, err error) {
try {
// ......
result, err := callfunc() // watched err
// ......
return
} catch err {
// switch over err, print err, whatever
return rv, err
}
}



Exceptions were intentionally excluded from Go. See this FAQ entry:

http://golang.org/doc/faq#exceptions

Wojciech S. Czarnecki

unread,
Jan 16, 2015, 6:40:08 PM1/16/15
to Nico, golan...@googlegroups.com
Dnia 2015-01-16, o godz. 13:07:11
Nico <nicolas...@gmail.com> napisał(a):

> I'd say that this proposal is equivalent to what in other languages are
> called exceptions.

Nope. Watch does not break normal control flow as exceptions do.
Also, there is nothing in the spec to say a watch must be used to
check an error condition. It is meant to keep any otherwise repetitive
conditional check/action in a single place. Watch is purely procedural.

E.g.

func x () {
// ...
watch ( inxd.ext_flags == urgent ) {
process_urgent_data(inxd)
// no return, so the control
// flows to the next statement in the scope
}
// ...
inxd, _ := pull_something() // here the inxd will be checked
// then control returns here ->
process_normal_data(inxd) //
// ... // many other inxd assignments
// to check
}

Anyway, Ian's "action at a distance" argument still stands. This is
a single common trait the Watch statement and exceptions have.

Wojciech S. Czarnecki

unread,
Jan 16, 2015, 6:57:29 PM1/16/15
to Klaus Post, golan...@googlegroups.com
Dnia 2015-01-16, o godz. 04:11:04
Klaus Post <klau...@gmail.com> napisał(a):

> Other than the already mentioned issues, what would happen if:
> [cut]

> Should watches follow pointers? If not, what if a function takes the
> pointer to a watched variable and changes it?
> What if a pointer to a watched expression is stored in a struct that is
> returned or handed to another function?

Watch is by definition restricted to _simple_ statements_.

See http://golang.org/ref/spec#Statements

Of which watchable are: IncDecStmt|Assignment|SendStmt

>
> /Klaus

Wojciech S. Czarnecki

unread,
Jan 16, 2015, 7:29:08 PM1/16/15
to Klaus Post, golan...@googlegroups.com
Dnia 2015-01-16, o godz. 04:11:04
Klaus Post <klau...@gmail.com> napisał(a):

I think I should elaborate more:

> Should watches follow pointers? If not, what if a function takes the
> pointer to a watched variable and changes it?

Watch does not go deep in any sense. It does NOT wrap a variable memory
in some kind of magic code. It is purely procedural. It kicks in
on an assignment in current scope. It works as on example:


func x () {

watch ( v != 0 ) { // compiler: aha! note v as watched and
got_v_zero() // make "subfunc w1 (v) {
// if v != 0 {
// got_v_zero();
// return;
// } }" }

dosmth(&v) // no simple statement, no watch
v := 1 // an assignment, v on the left, assigned to
// compiler: aha! v is under watch so lets
// call watcher subfunc for v =>
// "; w1(v) ;"
// <= control returns here from w1 call
vv := v // vv is not being watched of course
// ...
}

> What if a pointer to a watched expression is stored in a struct that is
> returned or handed to another function?

All those questions are exactly of my own before I settled on _simple_
statements. To get reasoning (and implementation) straight.

Paul Hankin

unread,
Jan 17, 2015, 2:25:47 AM1/17/15
to golan...@googlegroups.com
func P(n int) bool {
  i := 1
  watch n % i == 0 {
    return false
  }
  watch i * i < n {
    goto a
  }
  a: i++
  return true
}

Is this program legal? I think the answer is that it's ambiguous in multiple ways (I count 5) given the definitions so far, and we'd end up with a relatively long amount of text in the language reference describing how this construct works in combination with the rest of the language.

I'd much rather use a simple language that I understand and pay the cost of a few extra (explicit) if-statements.

-- 
Paul

Wojciech S. Czarnecki

unread,
Jan 17, 2015, 4:54:52 AM1/17/15
to Paul Hankin, golan...@googlegroups.com
Dnia 2015-01-16, o godz. 23:25:46
Paul Hankin <paul....@gmail.com> napisał(a):

> it's ambiguous in multiple ways
> [...]

> I'd much rather use a simple language that I understand
> and pay the cost of a few extra (explicit) if-statements.

You are right.

Legibility of the go language is too precious to be abused.
Putting an if Expr {} does cost a few keystrokes and a few seconds
to reason, reasoning about whether 'watch' will be called may cost
hours.

The 'watch' construct is oficially deceased. EOT.

Thank you all for the comments.
Reply all
Reply to author
Forward
0 new messages