-
Notifications
You must be signed in to change notification settings - Fork 25
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
Local scope in Julia #3821
Comments
It sounds like option 1 is the 'right' choice in the sense that if we go with option 2, we wouldn't be able to generate purely executable Julia scripts. Am I understanding this correctly? |
I'm not quite sure what you mean. As long as we add If you mean that scripts require a main function and won't run in the global scope, then you are correct. This might conflict with #3512, but at the same time it's no different than the C++ target. Let me know if you think this is something we should avoid. My bigger concern with option 2 is that it doesn't help external modules that define global variables, for example here. That isn't a perfect example, as the Python version uses a class, and neither target seems to actually use the The only benefit of still doing option 2, is that it would allow us to safely generate the # Option 1
a = 1 # Needs to be global for some reason
while a < 5
global a # Need to declare at top of while loop, because there's no function to declare at
a += 1
println(a)
end # Option 2
a = 1 # Needs to be global for some reason
function main()
global a # can safely put this at the top of the function, which might be easier to implement
while a < 5
a += 1
println(a)
end
end
main() To be honest I'm not completely sure, but my guess is that we have more infrastructure in place to put I hope this clears up my reasoning. |
We briefly spoke about this in person, but I think I still stand with option 1. Option 1 better captures the intent of a 'unmodular, unbundled'-style program (e.g., one-shot/use-style scripts, as in #3512), unlike Option 2, which would force some amount of modularization. The issue with the comparison to C++ is that it is not a scripting language first, unlike Python and Julia. That being said, 'unmodular, unbundled'-style programs are rather unserious for large projects, so I think it's a nice feature to support for show and tell, but not necessarily something we would expect to seriously use for any non-trivial project. |
Sounds good @balacij. I'm not exactly sure, but I think we'll have to add a new field for variables denoting whether they're locally or globally defined. I was hoping that we'd be able to reuse Here are my current thoughts, then:
I think this would be worth briefly discussing in #3815. |
The conclusions in the last post above (about |
I'm trying to make the changes I proposed with adding For context, I added class ScopeSym r where
type Scope r
global :: r (Scope r)
local :: r (Scope r) Then I added it as a dependency to Drasil/code/drasil-gool/lib/GOOL/Drasil/ClassInterface.hs Lines 126 to 129 in ea4cef9
to: var :: Label -> VSType r -> r (Scope r) -> SVariable r
staticVar :: Label -> VSType r -> r (Scope r) -> SVariable r
constant :: Label -> VSType r -> r (Scope r) -> SVariable r
extVar :: Library -> Label -> VSType r -> r (Scope r) -> SVariable r Because none of the current renderers use the new data, it was pretty easy to get most of it to the point where it compiles, even if there are some things I'll need to go back to one Julia is in the picture. With C++ though, I'm having issues with the Drasil/code/drasil-gool/lib/GOOL/Drasil/LanguageRenderer/CppRenderer.hs Lines 270 to 273 in ea4cef9
to: var n = pair2 (var n) (var n)
staticVar n = pair2 (staticVar n) (staticVar n)
constant n = pair2 (constant n) (constant n)
extVar l n = pair2 (extVar l n) (extVar l n) Basically, I just switched the For some reason though, this doesn't typecheck. I get a bunch of errors, but here's what I get for
From the first error it seems like it might have something to do with Type Families, but I'm not sure. I've tried a bunch of stuff to get it working, but it always gives the same or similar errors. Does anyone know what might be wrong here? |
The problem is that I suspect that the "right" fix will be to instead add the argument here var :: Label -> r (Scope r) -> VSType r -> SVariable r and then change your calls to var n sc = pair1 (var n sc) (var n sc) |
I just realized now, I'm not sure if it actually makes sense to have As far as I can tell, each language has more or less the same semantics of what is 'global' - i.e. if it's inside a function, loop, class, etc. it's local; if it's outside everything it's global. That is good, as I think it means expressing scope consistently across GOOL is possible. The issue is that different targets enter scopes differently. The obvious case is the I can think of three ways we can handle this:
I'm not really sure what to do here. Option 1 is relatively simple but not ideal. Option 2 feels to me like how it 'should' be done, but it's hard for me to guess if it's simply difficult or if it's completely infeasible for us to do. Option 3 might be the simplest, but it feels 'wrong'. Edit:I'm also just not sure if it makes sense at all to have the |
If we want "scope-polymorphic variables" (for their convenience) then I think we should introduce 'pre-variables' for that purpose. These would need to have their scope specified 'later' when they are used in a known context. More generally, the notion of scope should be used with more intent. In other words, rather than tying it to the language, we should tie it to "what we're trying to say" - and then render than appropriately for each language. The important concepts that need to drive the 'scope' notion are those of scripts, libraries and (modular) programs. So 'scope' arises in the middle of our translation from the intended code, in an intended form, for a specific programming language. |
Hmm, I'm not sure if I understand what you're saying. Do you mean that instead of assigning a variable a If that's what you mean, then that certainly would help simplify things. Is that what you're getting at? Or did I misinterpret what you said? |
Scripts and functions and loops should know what scope they need their variables to be, so they should control the scope. I was originally thinking that they would choose between |
I've been trying to wrap my head around how to get started again on this issue. Next StepsIt sounds like the first step is to take the work I've done with
I'm not sure exactly where to go once we have that working correctly. I think we should be able to follow addExceptions to get started with adding global declarations to the top of a scope. The thing I'm not sure how to handle is that in GOOL, the datatypes used for the various constructs that introduce a new scope are quite heterogenous. Functions are
Just to zoom out a bit, our
As far as I can tell, this is the behaviour we want to have. I did a few tests of weirder situations, and this way seems to hold up. Questions
|
Is this issue still 'live'? It feels like there has been some progress on it. |
Right, thanks for reminding me. I've been meaning to come back to this. It is still 'live', as the Julia renderer needs to use the scope of variables in order to add Drasil/code/stable/gooltest/julia/HelloWorld/HelloWorld.jl Lines 185 to 188 in 6c4ac76
Because As far as I know, there are still two things left to do:
|
I finished the first point, and created a PR for it (#3888). To get started on the second point, I noticed that Julia is pretty lenient about where and how often you declare globals. This means that we could just add a global declaration directly above every use of a global variable. That would be massively overkill and not look very idiomatic, but it would get the code working while we work on a proper fix. |
See my comment on #3888 . The idea is right, but since everything is in the |
I'm just trying to think through how we handle scope from
The issue is that in Potential solutions:
Of these two solutions, I'm leaning towards the second one. It seems to me like if we tell GOOL the scope of a variable when it is declared, we shouldn't need to keep reminding GOOL what its scope is. This would also lighten the load on |
My last comment is still a good summary of where we're at currently, if we don't want to do anything too drastic right away. With that said, I had another thought.
I'm sorry that I keep bringing up different ideas - it's just that the problem keeps proving to be more complicated than I thought it was. I guess we have three options now:
I'm not quite sure where we should go. In one way option 2 is at least a step in the right direction from our current path. We're running low on time, but we might have time to get it working. Whatever we do, I hope I can leave |
Just another update: I did a test run of option 2, and it seems doable. I got it generating the same (I think at least; I haven't compared them) code as #3903. There was a weird issue I ran into. When looking up the scope associated with a variable name for Projectile, it crashes if I don't add a check. For some reason, somewhere in there Julia gets a call to assign a variable with an empty name the value
|
I just thought I'd give an update, since it's unlikely that I'll get further this summer. We implemented Option 2 and merged the changes in #3899. This means that the generated Julia code is fully-functional, which is a great step. What's left is to implement Option 3. To recap:
One last thing to think about is to move a = 0
b = 0
while a < 10
while b < 10
println("a = $a; b = $b")
global b += 1
end
global b = 0
global a += 1
end could be rewritten as: a = 0
b = 0
while a < 10
global a, b
while b < 10
println("a = $a; b = $b")
b += 1
end
b = 0
a += 1
end This would need to wait at least until the above changes are made and might require other changes, but I personally think it looks a bit better. |
Eventually, what we'll probably want is for both Yes, the rewritten code is nicer. |
I've been working some more on the Julia render, and I discovered that it's stricter with local scope than other languages. A minimal example is the following:
This throws an error, because the
while
loop introduces a 'soft local' scope (see here for details), and local scopes cannot refer to global variables without explicitly stating that they are. There are two ways to get the intended behaviour:1. Using
global
declaration2. Using a function to define functions in a local scope
If variables are defined in a local scope, then nested local scopes are free to access them.
Analysis
Both options have difficulties.
static
vsdynamic
to keep track of which variables are global vs local. I think that this wouldn't work for any logic occurring in the global scope, though.State
can be used to addglobal
declarations to the top of a scope, similar to how we do imports currently.The text was updated successfully, but these errors were encountered: