gracelang / language

Design of the Grace language and its libraries
GNU General Public License v2.0
6 stars 1 forks source link

what's the scope of defs/vars in methods & blocks #141

Closed kjx closed 6 years ago

kjx commented 6 years ago

Do fields in methods introduce a name at their point of definition, or the whole scope? Both Kernan and amg run 'test' and print 5. Isn't the "a" request ambiguous? Andrew's minigrace gives an uninitialised variable error on 'test1' while Kernan prints 45 then 54.

class superclass {
    def a = 45
}

class subclass {
    inherit superclass
    method test {
        def a = 5
        print(a)
    }
    method test1 {
        print(a)
        def a = 5
        print(a)
    }
}

subclass.test
subclass.test1
apblack commented 6 years ago

In your example, I believe that both of the definitions of a are shadowing errors, since a is already defined in the enclosing object scope. I'm not surprised that minigrace doesn't catch this; we should add it to the list of minigrace issues (assuming that we all agree). The a request is not ambiguous, because a locally-declared identifier always takes precedence over a reused or enclosing definition of the same identifier — that's the first rule of lexical scope.

If the definitions were for a fresh variable, then test1 would be guilty of accessing an uninitialized variable. Which is how minigrace treats it.

To answer the question in the issue title: the scope of any declaration is the whole of the scope in which it is declared. That is, a def on line 2 of a block reaches "back" to line 1 as well as forward to the end of the block. That's what it says in the spec. So I don't think that this is an issue at all.

kjx commented 6 years ago

the scope of any declaration is the whole of the scope in which it is declared. ... That's what it says in the spec

OK yes it does - thanks for pointing that out. On the whole, I think that having the rules for methods & blocks be the same as for objects simplifies things. It makes methods & blocks a little more complicated than "point of declaration" semantics (as in Java) but having the two "a"s in test1 bind to different names is confusing, to say the least.

I believe that both of the definitions of a are shadowing errors

Hmm. The spec says it is a shadowing error

declare a parameter (but not a method or field) that has the same name as a lexically-enclosing field, method, or parameter.

By my reading of the spec, then neither of those are shadowing errors, because the shadowing declaration is a def, not a parameter; and the declaration that is being shadowed is in the superclass, not some lexically-enclosing field, method, or parameter. This definition of shadowing, btw more or less follows Java.

The point of my questions is to clarify whether the spec is right, or even work out what it should say. I'm trying to come up with a very simple operation model for the semantics.

apblack commented 6 years ago

Hmm. The spec says it is a shadowing error (to) declare a parameter (but not a method or field) that has the same name as a lexically-enclosing field, method, or parameter.

I started off my comment by writing, in response to

Do fields in methods introduce a ...

that methods don't have fields, only local variables. But then I read the spec, and realized that we use the term fields to refer to temporary variables in methods as well as fields on objects. (So I deleted that remark.)

Similarly, when I read that it is a shadowing error to declare a parameter (but not a method or field), I thought that we were disallowing shadowing by local names that were not externally visible. The reason for not disallowing shadowing by methods and fields of objects is that the object interface is determined by those names, so the programmer is not free to change them. This is not true for local variables in methods and blocks, so we should disallow shadowing thereby.

I agree that this is not what the spec currently says, but I think that it is what we have previously discussed. I know that when I implemented this stuff in minigrace, I was aware that I was not doing a very complete job.

I think that we might also want to think carefully about whether all defs and vars are fields, or whether we should reserve the term "field" for defs and vars in objects — the def and var declarations that implicitly declare methods. I can see arguments both ways on this.

apblack commented 6 years ago

Surprise! We are not consistent. In the Section on Fields we say:

Variables and definitions (var and def declarations) immediately inside an object constructor create fields in that object.

So, in this context, we are restricting the term "field" to mean a var or def inside an object.

apblack commented 6 years ago

Because I would like the terminology that I use in SmallGrace to be consistent with the Spec, I would like to make the Spec internally-consistent in the way that it uses the term "field" sooner rather than later. Specifically, I propose that we restrict the use of "field" to defs and vars in objects (including modules and classes), and not use that term for defs and vars in blocks or methods.

If we agree to this change, then we need a name by which to refer to the defs and vars in block or method scope. Smalltalk calls these "temporary variables", which is nice because it hints at the fact that they go out of existence when execution leaves the block or method. But it's not strictly accurate, because they can be captured by a block, in which case they do not go out of existence when execution leaves the block or method.

The other name that comes to my mind is "local variables", because they are local to the block or method in which they are declared. But this isn't any different from fields in objects, which are also local in scope (although not in lifetime).

What do you think?

kjx commented 6 years ago

"local variables" doesn't work because they aren't variables. "local declarations" or "local attributes" doesn't work because that means "declarations directly in an object rather than inherited declarations".

Do we need a common phrase for "def and var" declarations overall? Or "def and var declarations within objects (but not within blocks or methods)"? What about the names introduced by parameters?

I'd be tempted to use field to mean declarations (attributes??) for defs, vars, and parameters. "Object fields" are fields in objects.

This also emphasizes the SIMULA heritage of the close relationship between objects and closures. I mean, we're not doing the full BETA; objects and blocks and methods are different things. but still.

apblack commented 6 years ago

"local variables" doesn't work because they aren't variables.

I was using the term "variable" in the mathematical sense, meaning a name that takes on different values in different contexts, rather than exclusively referring to vars. You are right, though, that's not the way that it's used in the spec, where we talk about "constants" and "variables". I really should integrate my grammar into the spec, since that would give us uniform terminology.

We could use the name "temporaries" to talk about constants and variables in a method or a block. I dislike using the term fields for temporaries, because that term has an established meaning in the OO community. We should, as far as possible, be teaching the existing vocabulary rather than inventing our own.

kjx commented 6 years ago

I really should integrate my grammar into the spec, since that would give us uniform terminology.

well yes, but not necessarily terminology we'd all agree with. Still, I guess that cuts the gordian knot rather than lead towards endless discussions.

JLS9 doesn't explicitly define "field" but it does say only "class variables" and "instance variables" are fields. ECMA Eiffel says "A direct instance of a non-basic type is a sequence of zero or more values, called fields. There is one field for every attribute of the type’s base class."

We should, as far as possible, be teaching the existing vocabulary rather than inventing our own.

Where there is common vocabulary, and except for "request". Often, as with initialisation and assignment (#143), it seems the terminology is encoding the structure of the underlying conceptual model.

The question is how much are we just codifying the "existing" language as currently in the spec or minigrace, or how much are we still open to revising/simplifying/complicating things.

apblack commented 6 years ago

I really should integrate my grammar into the spec, since that would give us uniform terminology. well yes, but not necessarily terminology we'd all agree with.

I quite agree. Perhaps we should all look at my grammar before I go ahead and spend quite a lot of effort to put it into the Spec?

Actually, maybe we can re-use @kjx's fantastic Perl script to embed the grammar without actually editing the source? I don't recall how that stuff works.

apblack commented 6 years ago

The question is how much are we just codifying the "existing" language as currently in the spec or minigrace, or how much are we still open to revising/simplifying/complicating things.

I think that we are doing the first three things! It's hard to understand what a proposal to revise or simplify is doing unless we have a clear description of the status quo.

I'm not really interested in complicating things, though. I'm more interested in seeing how far we can go with the simplest language possible.

kjx commented 6 years ago

I think that we are doing the first three things! It's hard to understand what a proposal to revise or simplify is doing unless we have a clear description of the status quo.

me too, for particular values of "three"

I'm not really interested in complicating things, though. I'm more interested in seeing how far we can go with the simplest language possible.

again, yes - but what that does is throw the question of "simplicity" into sharp relief.

kjx commented 6 years ago

It's hard to understand what a proposal to revise or simplify is doing unless we have a clear description of the status quo.

which is what I'm working on here - https://github.com/kjx/inheritator2 It's by no means there yet, but it is a start.

KimBruce commented 6 years ago

I'm not sure what we are supposed to be looking at here. There is a one-line readme and then a bunch of Grace code. What am I looking at and how should I make sense of it?

kjx commented 6 years ago

You probably shouldn't look at it yet - as I said, "It's by no means there yet". I need to fix the code and the readme. I just wanted to point out it was there.

That said: for example, declaring variables and definitions in objects is around lines 647-660 of jeval.grace:

method declare(name) asDefInit(expr) {
    def box = ngDefBox
    addLocalSlot(name) asMethod(box) 
    initialisers.add(initialise(box) to(expr) inContext(self))
  }
  method declare(name) asVarInit(expr) {
    def setterName = name ++ "():=(_)"
    def box = ngVarBox
    addLocalSlot(name) asMethod(box) 
    addLocalSlot(setterName) asMethod(box) 
    initialisers.add(initialise(box) to(expr) inContext(self)) 
  }

which hopefully doesn't look too bad!

kjx commented 6 years ago

the scope is whole block; we're adopting Andrew's terminology for "field" and "attribute"

inheritator2 will never be worth anyone looking at