[e-lang] scoping rules of when-catch
Mark S. Miller
markm at cs.jhu.edu
Mon May 22 01:11:01 EDT 2006
Toby Murray wrote:
> From what I've read in MarcS' "Walnut" and the tutorial on erights.org,
> it says that when using nested when-catch statements, one must avoid
> doing things like the following, where we re-use the name "done"
>
> def func1 := { .... }
> def p1 := func1 <- run()
> def p2 := func1 <- run()
> when (p1) -> done(v1) :any {
> when (p2) -> done(v2) :any {
> } catch prob {
> }
> } catch prob {
> }
If Walnut says this, it is wrong.
> The reason it gives is that both "done" functions that we are defining
> here sit in the same scope, meaning that the second "done" definition
> will clash with the first. I understand this to mean that the above code
> is defining "done"s like this:
> def done(v1) :any {
> }
> def done(v2) :any {
> }
I just tried it, and the expansion is nested, as you'd expect.
> I personally found this totally counter-intuitive -- to my way of
> thnking, the programmer can do nothing else but assume that his code is
> behaving like this:
> def done(v1) :any {
> def done(v2) :any {
> }
> }
>
> which, to me, should be totally fine. Experimenting seems to indicate
> that it is infact (in the latest stable version) implemented the second
> way.
Correct.
> I can write nested "done"s without any problem. Also, the inner
> "done" has access to any local variables (sorry, can't think of a better
> term) defined by the outer done.
>
> This seems to contradict the statements in "Walnut" and the tutorial,
> which if true, obviously isn't good for newcomers to E trying to learn
> the language.
Yes. The real issue that Walnut was trying to get at is a different
counter-intuitive aspect of the scoping of these done functions; the one you
demonstrate below.
> Upon further experimentation, it does appear though that the body of the
> "catch" clause sits within the body of the "done" clause, in terms of
> scoping.
>
> For example, when interpreting the following code, rune complains about
> comflicting re-definition of $done__C$done__C
> (or something like that, I don't have the output in front of me).
>
> def func() :any {
> return 1.0
> }
>
> def p1 := func <- run()
> when (p1) -> done(v1 :int) :any {
> println("done called")
> def p2 := func <- run()
> when (p2) -> done(v2: int) :any {
> println("done-done called")
> } catch prob {
> println("done-catch called")
> }
> } catch prob {
> println("catch called")
> def p2 := func <- run()
> when (p2) -> done(v2: int) :any {
> println("catch-done called")
> } catch prob {
> println("catch-catch called")
> }
> }
Yes. The problem isn't a scoping problem in the traditional sense. Rather, it
is a problem that arises in assigning fully qualified names to object
definitions. Here is a simpler example of the same problem that involves no
when clauses:
? def outer() :any {
> if (1 == 2) {
> return def foo():any{}
> } else {
> return def foo():any{}
> }
> }
*** conflicting definitions of __main$outer__C$foo__C
There is no scoping ambiguity: Each "foo" variable exists in its own scope,
just like your above "done" functions. However, what fully qualified name
should we assign to these functions? They both have the same local name and
the same enclosing object-definition expression, so they would both be
assigned the same fully qualified name.
> Changing the "done" inside the first "catch" to "done2" fixes the
> problem. Likewise, chainging both the "done" inside the outermost "done"
> to "done2" and also the "done" inside the outermost "catch" to "done2"
> keeps the problem, confirming my suspicion that both of these inner
> "done"s (done-done and catch-done) are being defined at the same
> scoping level.
>
> It appears (to my mind) that the above is behaving like
> def done(v1 :int) {
> println("done called")
> def done(v2: int) {
> println("done-done called")
> }
>
> def done(v2: int) {
> println("done-catch called")
> }
> }
Regarding scoping, no. Regarding assignment of fully qualified names, yes. The
two inner "done"s conflict with each other. Neither conflicts with the outer done.
> Strangely enough, through, the following code produces infinite recursion
> def func() :any {
> return 1.0
> }
>
> def p1 := func <- run()
> when (p1) -> done(v1: int) {
> println("done called")
> def p2 := func <- run()
> when (p2) -> done(v2: int) {
> println("done-done called")
> } catch prob {
> println("done-catch called")
> }
> } catch prob {
> println("catch called")
> done(1.0)
> }
>
> We get
>
> catch called
> catch called
> catch called
> ...
>
> So "done" wihtin that outermost "catch" is bound to the outermost
> "done", which might contradict my previous assertion..
It is indeed.
> It's all a bit confusing, really :)
>
> Can someone help me out here?
Does the distinction between scoping rules and fully-qualified name assignment
rules clear it up?
--
Text by me above is hereby placed in the public domain
Cheers,
--MarkM
More information about the e-lang
mailing list