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

Compiler hangs when compiling circular imports #285

Open
IsaacOscar opened this issue Jun 4, 2019 · 10 comments
Open

Compiler hangs when compiling circular imports #285

IsaacOscar opened this issue Jun 4, 2019 · 10 comments

Comments

@IsaacOscar
Copy link
Contributor

If I have a file a.grace with:

import "b" as b

and a file b.grace with

import "a" as a

Compiling 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

def b = object { 
   def a = outer
   /*rest of b.grace*/ }
/*rest of a.grace*/
@kjx
Copy link

kjx commented Jun 4, 2019

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.

@IsaacOscar
Copy link
Contributor Author

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).

@kjx
Copy link

kjx commented Jun 4, 2019

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, and outer.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 gracelang/language#140 (comment))

@IsaacOscar
Copy link
Contributor Author

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!

@kjx
Copy link

kjx commented Jun 4, 2019

Why not self1 for outer? And self2 for outer.outer, etc?

we still hates outer my precioussssss

(Thus I do not support constructs like object{}.outer, which one may interpret as equivalent to self).

you can't have x.outer any more than you can have x.self.

@IsaacOscar
Copy link
Contributor Author

IsaacOscar commented Jun 4, 2019

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.

@IsaacOscar
Copy link
Contributor Author

IsaacOscar commented Jun 4, 2019

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

@kjx
Copy link

kjx commented Jun 4, 2019 via email

@apblack
Copy link
Contributor

apblack commented Jun 5, 2019

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.

@apblack apblack changed the title Compiler hangs when compiling recursive modules Compiler hangs when compiling circular imports Jun 5, 2019
@apblack
Copy link
Contributor

apblack commented Jun 5, 2019

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.

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

No branches or pull requests

3 participants