With all the recent (insanely cool) features which have been added to the DreamSeeker client, I find myself wishing the DM language had some additions, too. Below are two features I always sorely note the absense of when programming. Though I wish for them now, I'm sure that Lummox JR will be able to point out why I shouldn't (for more info, search the developer forums for "strong typing", and you'll find some misinformed posts from myself).
Both of these features concern the boundry between hard and fast aspects of the language, and objects. Imagin if there were no boundry between the two, and the if() statement was actually an object. Hard to envision? Read on.
TopLists are the coolest thing in DM. Ever. Like all objects, lists have vars which save state information and procs to do nifty stuffs, but they also have these awesome square things that you can put on the end of them []. Not only that, but you can put stuff inside those [] and stuff will happen! You might think I'm being facetious, but I actually do think lists are this cool. It's like they have a default proc that's called every time [] is put at the end of them. The best way I could describe it is like this:
list
proc/default[index]
return src.item_at(index)
The great part about this is that we don't have to actually type list.default[argument], we can just type list[argument] and DM knows that we want to execute the list's default behavior. Again, this is the coolest thing in the entire DM language. Unfortunatly, we can't do it anywhere else.
In my current project, I have an audio object, which is very complicated and has a lot of private variables and procs. However, it only has one public proc, called play_sound('file'). I've written the audio object to act as a module, and this is the only point of interaction. Wouldn't it be great if I could do something like in the following?
var/audio/audio
world/New()
.=..()
audio = new()
mob/verb/test()
audio('working.xm')
Along the same lines, I often use a Queue object made by Theodis (find it here, it's very useful). The Queue object is like a list, but it keeps track of the order that items entered in. This is great when you want to know which item has been in the list longest (like if you have a list of waiting players, and you want the next one in line). It's not so great if you want to find an item in any other position. (Readers from England and other countries outside of America are wondering why I went through all the trouble of explaining a concept so simple as a Queue). Sometimes I'd really like to treat a Queue just like a list and append [] to the end of it. However, DM won't let me. Instead, I have to define a proc, item_at(index), and type Queue.item_at(index).
What I'm getting at is that I want proc, named "default" or something else appropriate, which is called whenever parenthesis are appended to the object without a proc name.
datum
proc/default()
Queue
default(index)
var/item_at_index = do_finding_stuff()
return item_at_index
client/verb/test()
var/Queue/Q = new()
Q.Add(lots_of_stuff)
world << Q(3)
Top
Statements are some of the most basic parts of a language, unlike highly abstracted language componants like objects. Statements are commands that the language understands directly, without having to look through object trees, resolve references, or call procs. The most important part about a statement is that it handles control flow. Examples of statements include if(), switch(), and for(). Like procs, all of these statements accept argument-like parameters; unlike procs, they also accept a block of commands to be conditionally executed (optionally between {}).
As some of the most basic features of the language, statements are completely incapable of being modified or customized, but wouldn't it be great if we could? I often find myself defining a count() macro just so I don't have to type "for(var/index = 1 to some_value)". In this case, I don't care what index is at any given time, I just want to make sure that the commands are run some_value number of times.
#define count(N) for(var/__index__ = 1 to N)
mob/verb/test(N as num)
count(N)
world << "This message will be displayed [N] number of times."
annoyance++
Let's imagin for a moment that we could define our own statements; what would they look like? Statements seem to be able to accept arguments, so let's say that we'd define them as procs. Statements also seem to 'accept' a list of commands they will conditionally execute, like the code block {world << "This message [...]"; annoyance++} in the above example. This is where it get's tough. This list of commands is basically a custom proc which is 'passed' to the statement when it is run. So, let's treat it as a proc, too :)
custom_statement
proc/count(times)
for(var/index = 1 to times)
~~()
// ~~() represents the block of commands.
mob/verb/test(N as num)
var/custom_statement/C = new()
C.count(N)
world << "This message will be displayed [N] times."
annoyance++
~~() is just an example of what the command might look like. I chose it because of it's similarity to the parent proc, ..(), and because ~~ doesn't mean anything else in DM. The important thing to note is that, however we name it, ~~() is a non-proc block of code that can be 'called' as though it were a proc.
TopThis is a fairly obvious combination of the above two features. Once we have a default proc which we can call simply by placing parenthesis after an object, and a proc (~~()) which we can call to execute a block of code thereby allowing us to make custom statements, we can make objects which are statements all by themselves. Remember where I talked about making an if() object? Well, here it is:
/* Because 'if' is a reserved word we have to use another name.*/
condition
default(test)
if(test) // ie, if test evaluates to TRUE
~~()
Is it sad to think that this is the sort of thing I dream about?
Top...It is not meant to be. Certain shortcommings have yet to be worked around, and the language currently cannot evolve exactly as I've outlined above.
First of all, ~~(), though a powerful idea, doesn't allow for complicated control flow, the sort of thing statements are made for. For example, there's no way to write an 'if(), else if(), else' block just using one statement/object and an ~~(). You'd need some sort of multipart object with multiple ~~() attachments. Really, I have no clue how you'd allow for such complicated custom structures, and until such a way is devised, there is no purpose in tying the language to a half-implementation.
The second big problem is the ambiguity that a default proc would introduce. Imagin that an object has both an audio var and an audio proc. If DM then encounters the line "audio('file.dm')" what should it do? Should it execute it's own audio() proc, or should it execute the default proc of it's audio var? The only fixes to this would be to either have a set preference rule for vars and procs, or to disallow vars and procs of the same name (which would break old code).
Unfortunatly, it seems these features, conceived with the dream of making the Dream Maker language customizable, are doomed to remain just a dream. But, what if?
Top