Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

in which scope should the argument to inherit and use clauses be evaluated? #68

Closed
kjx opened this issue Feb 23, 2016 · 10 comments
Closed

Comments

@kjx
Copy link
Contributor

kjx commented Feb 23, 2016

#58 contains some nasty examples of using self in use or trait clauses.

method printme (x) { 
  print(x)
  return x
}

class foo {
   print "FOO"
}
object { 
   uses self.foo
   method asString { "evil" }
   class foo {
     print "SATAN"
   }
}

Should we just say these expressions are evaluated in the surrounding scope?

@kjx kjx added the 1:now label Feb 23, 2016
@apblack
Copy link
Contributor

apblack commented Feb 24, 2016

I think that they have to be evaluated in the surrounding scope. They clearly can't, for example, request methods on self, since neither self not its methods exist at the time the inherits and uses statements are evaluated.

It would make sense to locate these clauses outside the braces that demarcate the object's scope, except that in a module, there are no braces and there is no outside.

Regardless of what syntax we adopt for a manifest constant, such a constant also will beed to be evaluate in the surrounding scope.

Unfortunately, this gets very complicated very quickly. Do we allow the keyword self to appear in an inherits statement? If so, does it mean the current object (which would be an error), or the surrounding object, which would be ok, but surely confusing. If we don't allow self, what is the right way to refer to the outer object? outer? But if we say that inherits is evaluated in the surrounding scope, then shouldn't outer mean the outer's outer?

This is really horrible.

@KimBruce
Copy link
Contributor

I suggest no restrictions, but a strong warning (in the reference manual and perhaps a warning message emitted by the compiler) that it is evaluated in the outer scope. Only an idiot programmer would use self in the inherits, but, hey, we let idiots do all sorts of strange things. I think our notion of nested classes/objects leads to some complexities that other languages avoid, but most will use them with care (I hope!)

@kjx
Copy link
Contributor Author

kjx commented Feb 25, 2016

It would make sense to locate these clauses outside the braces that demarcate the object's scope

well perhaps we should move them out.

except that in a module, there are no braces and there is no outside.

Modules are already special. You can only have imports at the top level. We could allow inherits/uses clauses inside modules without breaking everything.

Arguably the names introduce by "import" needs to go into an additional scope sandwiched between the dialect and the main module scope. We could run module inherits clauses in that scope too.

@apblack
Copy link
Contributor

apblack commented Feb 25, 2016

You can only have imports at the top level.

If so, we should put that in the spec!

The idea that modules are just objects, but objects that are named by a extra-language mechanism, was one of the great insights of Grace's design. Let's not spoil it by making too many differences between modules and other objects.

Saying that imports must be at the top-level is OK. Changing the syntax is not.

@kjx
Copy link
Contributor Author

kjx commented Feb 26, 2016

Changing the syntax is not.

why not? #61

I think there is an argument that *inconsistent syntax between modules & object constructors is problem --- but as above I argue they are already inconsistent because the object { } is omitted for modules. That said: I can see an argument that modules could be "the body of an object { } constructor, prepended with import statements" --- arguing in favour of keeping the inheritance/uses clauses inside that body. On the other hand, if the arguments to those statements are executed in the surrounding scope, that breaks another principle, that "things are executed where they appear".

So we have to choose.

@apblack
Copy link
Contributor

apblack commented Feb 26, 2016

Rather than saying that inherits is executed in the surrounding scope, we could say that the inherited value cannot refer to anything declared in the current scope. This is subtly different! For example, rather than changing the meaning of self, it says that we can't utter self, and that outer in an inherits statement means the immediately enclosing object, as one would expect, and not the second-outermost enclosing object.

@kjx
Copy link
Contributor Author

kjx commented Mar 7, 2016

@kjx
Copy link
Contributor Author

kjx commented Mar 7, 2016

(dunno why I can't see this on github (yet?)

On 8/03/2016, at 9:02am, Andrew Black notifications@github.com wrote:

No, because that would give outer a counter-intuitive meaning. We should say that parent expressions can't refer to self, implicitly or explicitly. We also need to say that parent expressions are manifest,

we do:

The argument of an inherit or use clause is restricted to be a Manifest Expression that creates a new object, such as a request on a
class or trait. The argument cannot refer to self, implicitly or explicitly. The object reused by a use clause must be a trait object.

which may imply that they can't refer to self, if we pick a reasonable definition of manifest.

manifest currently also stops implicit self.

I added some consequent discussion to #67

cheers

James

@KimBruce
Copy link
Contributor

KimBruce commented Mar 7, 2016

Maybe I’m confused here, but I don’t see why parameters (either regular or type) to a class used in an inherits clause have to be manifest.

If I define:

class sup(arg1,arg2) {

}

def x = o.m(73) // o may itself have been passed in from elsewhere

class sub(rg) {
inherits sup(rg, x)

}

then sup is certainly manifest in the inherits clause, but rg is not. Similarly, the value of x is certainly not manifest, yet can be used in the inherits clause.

[My earlier suggestion of anything from the outer scope being OK wasn’t quite right either, because rg isn’t technically in the outer scope, but hopefully you know what I meant.]

I’m also not certain why you would want type parameters to be manifest either. If I am defining a list of T, it’s not uncommon to want to define a subclass with some extra features (e.g., a length method). You want to keep this as a parameter that can be matched with the one from the superclass.

On Mar 7, 2016, at 12:02 PM, Andrew Black notifications@github.com wrote:

Should we just say these expressions are evaluated in the surrounding scope?

No, because that would give outer a counter-intuitive meaning. We should say that parent expressions can't refer to self, implicitly or explicitly. We also need to say that parent expressions are manifest, which may imply that they can't refer to self, if we pick a reasonable definition of manifest.


Reply to this email directly or view it on GitHub #68 (comment).

@kjx
Copy link
Contributor Author

kjx commented Mar 8, 2016

Can we talk about that at #47

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants