Today was one of those days... I happened to notice a silly little issue with the compiler a few weeks ago that I wanted to tackle: you can have a class and a module with the same name at the same namespace level and the compiler won't flag it as an error. Seems like a small bug, especially given the fact that modern incarnations of the IDE won't allow you to get into this state. However, since older IDEs seemed to have allowed it at one point, and RBScript has no way to prevent it, I wanted to make an error for it.
Famous last words: "it's simple!"
My first pass at fixing the bug actually caused massive problems. You couldn't compile any projects that contained windows or menu bars. I found that to be very odd, because those should still be legal. If you recall from previous discussions, they're a bit odd in that they're actually a module with an inner class. This particular problem lead me on a deeper tour through the code, and I found out that a fairly old subsystem of the compiler was doing more work than it probably should. Asking the root namespace "can you find something with this name" was searching the root namespace, and if it didn't find anything, it would search for modules containing global members! Oops, so that's why my fix hadn't worked.
This got my cognitive wheels spinning. What would happen in the case of classes declaring types, since that's a new feature of the compiler. What should happen with this code:
Module Test
Global Class Test
Public Structure Wahoo
i as Integer
End Structure
End Class
Public Structure Wahoo
s as String * 10
End Structure
End Module
Dim t as Test.Wahoo
Contrary to what you might think, this code could actually be unambiguous. It's easy to explain why, and scales well with the concept of importing namespaces: the compiler could prefer a fully-qualified namespace to an imported namespace. In this case, Test.Wahoo is a fully-qualified identifier. If the user wanted the class' namespace, they should use Test.Test.Wahoo instead. However, I'm not 100% certain that's the route I want the language to go. We want to discourage people from polluting the global namespace, and this is rather contrary to it. Still deciding...
However, this lead me to my next "bug." It turns out that you cannot use the fully-qualified name to get the class' structure. In fact, you can't get to it at all! This bug turns out to be another incarnation of the first one -- the lookup code was doing too much work. In the case of Test.Wahoo, it would find the module's structure and work. But in the case of Test.Test.Wahoo, the lookup for Test.Test would fail!
So while looking into fixing that bug, I found an even more scary problem -- the implications from having a window be comprised of a module and a class. Armed with the knowledge that we want to prefer qualified namespaces, check this code sample out:
Module Window1
Global Class Window1
Public Shared Sub Foobar()
End Sub
End Class
Public Sub Wahoo()
End Sub
End Module
Window1.Foobar
That code is essentially what you'd get when adding a shared method to a window in the IDE. However, it's relying on the current implementation details of the compiler to work. Were I to fix my previous example (so that Test.Test.Wahoo would work), it would break this example! Even if we solved the problem by implementing classes within classes (which would allow us to rewrite the underlying window code), we'd still break existing code that is similar to the way our window code works. I just think there's a lot less of that out there than people who use windows and menubars!
Really, what needs to happen is that the entire mess we call a namespace search needs to be cleaned up. The code: Test.Test.Wahoo should really be returning sets of information that are handled once the entire expression is complete, because that's the only time we can tell what the user really means. In the case of Window1.Foobar -- if Window1 was determined as we parsed it, it could refer to the module or the class, depending on what we find to the right of the ".". In this case, we find Foobar, and so it's clear the user's intention is to call the method on the imported Window1 class. But if we found Wahoo instead, then it's clear that the user intends to call the fully-qualified name for the module's method. The only time it'd be ambiguous and require the user to be more explicit should be if the import causes a naming conflict. But without the concept of lookup sets, that cannot happen. (Sorry, this is looking more and more like a note to myself!)
Basically, there's a few things to take away from this ramble:
1) Global sucks, don't use it. If you have to, be careful not to conflict with existing names at the root namespace because there are problems with it. These problems are not yours (they're mine), but they're likely to exist for a while still. The solution may be to explicitly allow it, or to explicitly disallow it. So until then, work at your own risk!
2) In RBScript, don't name classes and modules the same things at the same namespace level. It's a bad idea, and don't shoot yourself in the foot just because the compiler lets you. Again, my problem, not yours. Just telling you what to watch out for!
3) The next time you find yourself saying "that should be easy for RS to fix", take a step back and realize that REALbasic is an incredibly complex piece of software and we work extraordinarily hard to make it stable, sensible and retain compatibility with older projects. Sometimes "simple" is really hard!
This reminds of a recent project I was debugging. It allows a bunch of user customization of objects with graphical representation and interaction (think "Little Big Planet" for PS3 on a much smaller scale)
I started getting reports that after a certain amount of time, objects that should be clickable could no longer be selected. The mouse click didn't go anywhere. Turns out after a marathon debugging session and too many profanities uttered in my mind...that basically, the visual representation and internal representation were getting of sync somewhat randomly. So the app didn't see anywhere for the click to go.
I dug in further, and found out that there was a deep flaw in my object interaction. Fixing the "root problem" caused other, worse problems. What I thought was a v1.1 type of thing turned very quickly into more of a v2.0 thing as I went back to the white board (literally) to redefine my entire scheme.
...and with that, I swore to go a little easier on my other fellow engineers and programmers the next time I thought something was "easy". I also have a newfound appreciation for the decision of letting a known bug stay in a given piece of software for a release so as not to cause larger problems, until the root can be worked out cleanly.
HA !
That ought to be easy - famous last words :)
This probably more for Norm but...
In 2008R 3.1 I have a project where a window has menu handler and method with the same name. It was an oversight. I don't know if it's supposed to be legal (is it?), but it complies and both get called appropriately so no problem. Except ...
I only realized i had used the name twice when using undo ... instead of being taken back to that method, I got taken to the menu handler.
- Karen
@Karen -- menu handlers are just methods, but we alter the name of them at render time in magical ways. It's not an error that it works because the signatures are probably unique to begin with (not to mention the fact that the menu handler may or may not be renamed for you). However, it might not be a bad idea to has less ambiguity in your project.
Hi Aaron.
Yes if I had realized it I would not have named them the same...
But my main point was that it confused the ID ... and I think it was actually the back button that took me to the MenuHandler incorrectly when it should have gone to the method .. which is why I said it was likely an issue for Norm (he works on the IDE right?)
- Karen
I'd have to try this and see if I could figure out why this might be.
I have an idea but I don't want to speculate here.
I quickly tried this and I'm pretty sure I know why it does this
if you check the "location" field in the IDE (at the top of the IDE on one of the toolbars) you'll notice that in this case both items have the same location path.
So when you undo the change what happens is that the IDE says "undo the delete and go to the location that this item is at"
Both having the same location the IDE goes to the one that is found first - the menu handler
Definitely a glitch and you should file a report so it doesn't get lost
Slightly off-topic here, but I think it would be great to have an equivalent of the C++ "using" statement in RB. Of course, it might be a bigger task than us users imagine!
It wouldn't let us do anything we can't do already -- just save us from typing fully-qualified class names all the time.
@Eric -- I totally agree, though I wouldn't bother with the confusing "using" keyword. ;-) I'd go with a combination; a "With" statement to allow for local, small namespace importing and an "Imports" statement at the project item level. It's a reasonably large task, but not impossibly so. Someday! :-)