Language theory: specifying const types

| | Comments (7)

Turn the wayback machine's hands back to the late 90s, and you'll notice that REALbasic has very few datatypes. Basically, there's Boolean, Integer, Single, Double, String, Color and Object. If you wanted to make a constant, you really only had to pick between Boolean, integer, float, string or color -- there's no ambiguity there, since it's very easy to ignore Singles. ;-) So the original Const statement in REALbasic didn't allow you to pick the type, it just inferred it at compile time from the right-hand side of the assignment.

Now we come back to today's REALbasic, and there are significantly more datatypes. You've got size constraints for integers, as well as signed vs unsigned. You've got things like Currency and a host of other new datatypes. So perhaps it's time for REALbasic to allow you to explicitly specify the type of a constant. This is especially important now that REALbasic has the opportunity to warn you about implicit type conversions, etc.

So I've been playing around with the language concept of explicitly typed constants, just to see how they look and feel. For example:


Const foo as Single = 1.0

This would create a constant whose type is Single, which is useful if you need to pass that constant off to a method which expects a single. By doing something like this, you would remove the need to say: CType( foo, Single ) when calling the method just to avoid conversion warnings.

At first blush, this seemed like a pretty straight-forward thing to add to the language. It's easy to understand, easy to explain, and follows the design principles of REALbasic. However, it does come with some interesting edge cases that require more thought. For instance, if you can explicitly set a constant's type, then what happens if the user picks Ptr as their datatype? There's no way for you to type a literal that can be assign to a Ptr, so a literal assignment won't work. My first thought was to just make use of type casting, or type conversion a la CType, so that you could say:


Const foo as Ptr = CType( &h00000000, Ptr )
// Or
Const bar as Ptr = Ptr( &h00000000 )

However, this won't work either -- some type conversions require work to happen at runtime, even if they involve constants or literals. Of course, we could implement that behavior, but it's actually a lot harder than you might think. You see, it's an M by N conversion problem in many cases, because an Int8 could go to Int8, Int16, Int32, etc or vice versa. Whereas at runtime, the conversion is more straight-forward since it can just use assembly and ignore the types.

But there's still another problem to consider: what about the fact that you could now specify datatypes which don't make any sense as constants. For instance:


Const foo as Window = nil

Should that compile? It makes no sense to have a constant window... but it's "legal" code. Ok, so replace Window with some structure type. Now what do you get?

At what point does this syntactic convenience become difficult to explain? Right now, it's very easy to work with since the type is always inferred. There's very little explanation needed (it helps that it's been the only way to create a constant since Day One, too). Now we'd have to say "only some constant types actually make sense", or report compile errors for constant types that are out of the ordinary set. That may not be the end of the world, but it certainly isn't as straight forward as it may have seemed initially.

Would it make sense to have an explicit white list of constant types for use in the As clause, and if the type isn't on the white list, it's an error? I would say that the list could be sensibly restricted to: Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64, Currency, String, Color, Single, Double and Boolean. I'm on the fence about Currency due to implementation details, but it seems sensible to include in the list. Also, would it be confusing that you're not allowed to typecast or use CType on the right-hand side of a constant assignment? I mean, you can't do it today... but today, you can't pick the type anyways, so casting or conversion doesn't really spring to mind as something to do.

So what are your thoughts on the topic?

7 Comments

I think the "As" syntax plus a whitelist makes the most sense. When you're in the constant editor your choices are limited by the IDE anyway, and "Const w = Window1" is already an error, so technically there has always been a whitelist.

That said, one of the first things I tried in 2008r3 was using CType for Const assignments. The fact that CType is sometimes compile-time, sometimes run-time is a little confusing though, so I'd rather avoid it whenever possible.

@Frank -- One thing to keep in mind about CType in terms of compile vs run time is that it's no different than casting or implicit conversions. The truth is that some things need to be converted at runtime, regardless of whether you use type casting or CType.

You can take the approach that C takes with constants - const simply means "this variable cannot be changed". Therefore a constant should be able to have any value of any type, even a calculated one or an initialized object. However it only gets this value from it's initializer when declared, and can not be changed thereafter. I guess this is the approach that makes the most sense to me in the context of RB.

In this view, these should all be perfectly valid:

Const NullWindow As Window = nil
Const NewWindow As New Window
Const SomeString As String = "This is a const string"

I also like the As syntax plus a whitelist of types with compile errors if you attempt to use a non-whitelisted type. In fact I often type "Const foo As Integer = 5" because I never remember the "odd" syntax that's there now, so I'm used to compile errors :-)

@Isaac -- the difference is that in C, const is a modifier and in RB, const declares a variable. So you can have a method which is modified to be constant in C, but in RB that wouldn't make sense (a method defined as a variable??). If we could do it all over again, I think we would go the C route with Const as a modifier instead of as a declarator. However, I think it's a bit too late to be changing the semantics of constants.

How do you currently store the value of a constant?

I don't think this changes the semantics of constants at all. I'm not suggesting that Const become a modifier - you're exactly right a constant method in RB doesn't make sense.

Rather Const should be a version of Dim that creates an unmodifiable variable, which is what it looks like it does now anyway (since it probably is not a simple substitution macro if it has a "type").

@Isaac -- constants are currently their own symbols in the symbol table (local or global). Declarations made with the Dim statement are their own symbol in the appropriate table as well. However, the way we've got things setup now isn't conducive to the idea of a Const Dim (even if that's not the syntax). For instance, with the idea that you could do:


Const foo as New Window

This requires the allocation to happen at runtime obviously. But what it takes away is the ability to do constant folding with Const statements because they now require runtime support. Unless we wanted to add a lot of tracking that says "this is a constant we know at compile time" and follow that logic around everywhere.

It's not that we *can't* do a refactoring like that. It's just that it's very, very little bang for the buck.

Leave a comment