Forward referencing

| | Comments (11)

What should the following REALbasic code do:


#if Foobar
Const Foobar = false

MsgBox "Foobar!"
#endif

If forward referencing is allowed, then this code should not exist in the resulting application (though it should compile). If forward referencing is not allowed, then this code should fail to compile entirely.

So if you're in the camp of people that claim the above code should fail to compile, then you've also placed yourselve in the camp that claims this code should fail to compile as well:


Sub SomeMethod( test as Integer = kSomeConstant )

Without ubiquitous forward referencing, this code may or may not compile, depending on how your project is rendered out. Since you have no control over the order items are rendered, you cannot assume that constants have been rendered before methods in any given class. And it's not just limited to code items within a class -- what if kSomeConstant is a global constant that lives in a different module? You can't be assured that the module has been compiled before the method in another class.

Forward referencing is a really sticky problem from a linguistic perspective. It might be nice to have in some circumstances, but it makes programming a lot harder in practice. You suddenly have to worry about the order you compile project items, the order of code items within a project item, etc.

11 Comments

I've always assumed that things like constants would be defined first (and that assumes more than a one pass compiler I suppose)

So that your first example would be analogous to

Const Foobar = false


#if Foobar

MsgBox "Foobar!"

#endif

I can understand how/why this assumption might be incorrect but the behavior of the compiler seems to have always been such that this works.
From your post I'm guessing it does not do more than one pass and define constants first ?

Hmmm; good problem. I'd like the first to be a compile-time error (so that scope does what I think it should) and the second to work. But I don't see a way to do it without special cases, which can be a long-term maintenance problem for you.

I don't see why this necessarily has to be the case. It seems to me that what you're saying somewhat like C/C++ functions prototyped ahead of main(), then implemented after the fact: You prototype foo() so the compiler knows that foo() exists when it encounters it in main(), then you actually implement it later on.

Why can't RB take an iterative approach? Viz, scan through to setup constants in its symbol table first, then compile methods, classes, etc.? Of course, if the constant to which you're referring is defined within the method whose parameter references it, then I agree with your assertion.

@Norman -- the RB compiler is more than one pass, but now you're talking about more than two passes. First you'd have to do a pass to resolve constants, then you'd have to do a pass to resolve #if statements (which seems quite backwards to me -- I'd say the operations should be reversed).

@Charles -- yeah, there are some special cases we already have to deal with in regards to this problem (declares come to mind since they are a type of prototype, but have to resolve later in the compile process).

@Adam -- REALbasic already takes a two-pass approach and basically does exactly that. First, declarations are mined out of the source code, and then the entire source code is parsed to be actually compiled. The trouble with constants is that they can be defined within a method itself, so it boils down to: which should happen first? Constant resolution or #if resolution? The current answer is #if, which I think is reasonable. But this could be extended to other areas too: what about classes within classes? And so on.

Aaron
I guess I just look at the first example you posted and it looks like the constant is being used before it's defined. And much like any C compiler that's not permitted.
Using a constant as the default parameter would then require ordering of the code to be compiled so that all constants are parsed and scoped accordingly.
I'd certainly not expect
Sub SomeMethod( test as Integer = kSomeConstant )
const kSomeConstant = 125
end sub
to give me the default value of 125 but if I declared a module or class constant with that value I might.
Are you suggesting that the above actually give me a value of 125 as the default ?

Currently the compiler seems to work as though constants come first in the compilation process and visually in the IDE they do as well - I realize that's completely arbitrary.

I wonder if a survey of how it's handled in other languages would be useful.
Java has reasonable forward reference semantics as do C and C++. Not sure about Ruby Python etc.

@Norman -- I'm not saying that it will give a value of 125. In fact, I think the compiler flags it as an error because of the forward referencing problem (if you use a regular constant declaration instead of a const in the method itself). It used to be that the compiler would sometimes give you the proper value, and sometimes give you zero depending on the rendering order.

We could certainly modify the language to be more like C/C++ for declaration vs definition, but we're a BASIC variant and that'd just be strange. Or, we could just let it be order-dependent, but that sucks too because then you have to get your project item ordering just right, and can still get into situations where you're totally screwed. For instance, ClassA uses a constant in ClassB, but ClassB uses a constant from ClassA.

Basically, forward referencing sucks. But at least you know what it is and why things work the way they currently do.

I do know what forward referencing is .. even before you wrote this :)
It's a pretty standard part of C / C++ / Java.
I just assumed based on observed behavior of the RB compiler that it was C/C++ like in that it had a couple passes for declaration and definition. Probably a bad assumption but it's always seemed to behave this way. At least I've never had it NOT work as I expected it to work :)

As for the ClassA / ClassB issue with a couple passes that should be soluble.

However, the example you had with the constant definition inside the method still seems fundamentally like it should NOT compile ... ever ... even with forward referencing since the constant is contained in the methods scope. For that to compile it should be in the enclosing scope level.

At least that would be my expectation.

@Norman -- I agree with you that the example code I had posted should never compile. It was just an interesting way to pose the question because it really showcases the issue.

Aaron,

First I don't have the right background or experience to fully understand the issues here so take my comments in that light.

I realize supporting the use of constants as defaults parameters may not be easy and may need to be treated as a special case...

BUT IMO it is worth it even if the order of compilation needs to be at least loosely defined. I have found not being able to use constants very frustrating from when default values for parameters were first Introduced...

I think it's something most starting out would intuitively expect to work in RB even if (like me) they do not come from a C or Java background or have background designing compilers.

Obviously constants used to assign default values should only be constants defined at the module or class level. Perhaps a first step as a prelude to a more comprehensive solution would be to allow modules or class constants to be used only method definitions within that class or module... I realize that could violate expected scope rules though.

As I said I don't have a good understanding of the issues involved... but would it really be that problematic to have a first pass to define in order:

1) Framework level constants in the Realbasic module
2) Class level constants defined in framework classes
3) Class level constants in the blessed App class
4) User Module level constants with module order undefined
5) User Class level constants with order of classes undefined

Defining such an order could also set the stage for derived (calculated) constants that depend on other constants in the future.

- Karen

@Karen -- sure, we could special-case constants to make it work. But then people would complain, "what about enumerations?" And so forth. Special cases are a very slippery slope. Not saying that we'd never make something like this work, mind you. It's just pointing out that it's not quite as easy as saying "but this can be done that way."

Also, by defining an order like you've suggested, we're bound to that order forever and ever. That can make future improvements more diffucult, if not impossible. Leaving things hazy can be annoying, but it can also make future improvements possible.

Leave a comment

Disclaimer

I'm currently an employee of REAL Software. My blog is mine. The opinions represented in this blog are mine as well and may not represent my employer's opinions. All original material is copyrighted and property of the author.

REALbasic® is a registered trademark of REAL Software, Inc. REAL SQL Server™ and Lingua™ are pending trademarks of REAL Software, Inc. All rights reserved.