Open IsaacOscar opened 5 years ago
Good question! although I note Kernan says
CyclicImportError: R2011: Module "a" transitively imports itself. The chain of imports a -> b -> a cannot be satisfied as it would require accessing a before it was ready.
I generally try to check language level things across both kernan and e.g. amg.
We're pretty keen on fixed, comprehensive order of evaluation. This would change the order of evaluation depending on which file was run first. Also, modules are nested inside dialects, and your transformation doesn't account for those.
Yes I see the problem with respect to evaluation order. I was thinking of this myself, and came up with a requires "x" as b
stateement, thats like an import
, but will ensure that "x" is evaluated first. (if it can't be, e.g. you have cyclic dependencencies of requires
, then you will get an error). This is important if you want a module's initialisation code to read a def
declared by b
, naturally you would want b
to be initilised first.
However, often a module just contains a bunch of methods and type-declarations; but no real initilisation code. This is where it's particular usefull to suport recursive modules.
One option is to not initilise a modules variables until after everything that "import"s it has been initilised. Then you can get an error message if a module depends on it's imports being evaluated first, when they should be using "require" first.
As for other side-effects (e.g. I/O), I don't personally see the problem with the evaluation order depending on which file is compiled first, as one is likely to compile a project from the same main file each time.
As for dialects, that's a good point. Perhaps we could add support for dialects in objects?
In which case outer
would correspond to the dialect, and outer.outer
would correspond to the enclosing object (since you're not supposed to acccess the dialect of a dialect).
As for dialects, that's a good point. Perhaps we could add support for dialects in objects?
yeah. the syntax is (mostly) clear. the fun is actually the interaction of two or more dialects - how much of the enclosing scope or dialect do you keep? Currently that's a low priority for me, but there's certainly research around dialects (like making them go fast in Moth without hardcoding everything). One could look at this as part of that I'm sure...
In which case
outer
would correspond to the dialect, andouter.outer
would correspond to the enclosing object (since you're not supposed to acccess the dialect of a dialect).
We still hates outer my precious!
(see https://github.com/gracelang/language/issues/140#issuecomment-357420869)
Why not self1
for outer
? And self2
for outer.outer
, etc?
In fact my type-checker is using a similar representation for it's ast: self == outer0
, outer == outer1
, outer.outer == outer2
. Etc.
(Thus I do not support constructs like object{}.outer
, which one may interpret as equivalent to self
).
Of course this1
would be even better!
Why not
self1
forouter
? Andself2
forouter.outer
, etc?
we still hates outer my precioussssss
(Thus I do not support constructs like
object{}.outer
, which one may interpret as equivalent toself
).
you can't have x.outer
any more than you can have x.self
.
Sure, encapsulation and all that... but x.outer
makes perfect sense!
Actually, I think ..
is a better syntax, instead of self.foo, you just write .foo
, and instead of outer.foo, write ..foo
.
If you're going to have a language with nested objects, why wouldn't you wan't to easily access and call methods on your enclosing object?
As for the linked issue, I don't even see why you need confidential at all... Isn't lexical scoping sufficent? E.g.:
method C {
def my_field = /*some computation*/
object {
method public_method { my_field + 2 }}
Now the result of C has it's private state (i.e. my_field
) perfectly encapsulated.
Also, if you allow def
's to be recursive, then then only reason you'd ever need self/outer is to disambigoute shaddowing:
method bar { ... }
method C {
def res = object {
method foo { ...; return res }
method bar { outer.bar } }
res }
As a side note, if you allow def x = e
to evaluate to x
(instead of done
) the last line above won't be needed
Sure, encapsulation and all that... but x.outer makes perfect sense!
it makes sense, but it's still a bad idea.
If it's not the syntax, what is you're problem with outer? If you're going to have a language with nested objects, why wouldn't you have it?
because - with a semantic definition for nesting, you don't need it.
O'CAML names "self" for each object explicitly with extra syntax.
In Grace you can name objects internally or externally
def externalName = object { def internalName = self ...
if encapsulation is calculated on the dynamic relationships between objects, then you don't need special syntax. I think a type checker would still need to track those relationships, ditto a VM, although caching may fix things.
for what it's worth, this is again research - James
This conversation seems to have gone off the rails. Outer is not a message, and never has been. object{}.outer
is not allowed. If you think that we should change the language to make it a message, open an issue on the language issue tracker, so that I can remind you that an object built using inherit
or use
has more than one outer
.
Then delete this comment and all the garbage about outer.
I understand that the grace spec forbids such modules, but I am curious as to why
The “why” is that the original implementor of Grace was adamant that recursive dependencies are a bad idea. Certainly they are confusing to novices, but we do have use-cases (#282) in setting up the environment where they can be useful. As the OP points out, recursive objects are actually rather common, and attempts to read the value of an uninitialised def
or var
var are already caught.
If I have a file
a.grace
with:and a file
b.grace
withCompiling either one of them with
mgc
causes the compiler to hang without printing anything.I understand that the grace spec forbids such modules, but I am curiouse as to why. Why not just make them syntax sugar for objects, e.g:
my above code could be treated as equivalent to