This is a list of 16 changes I'd like to see about DM and BYOND. Most of my gripes about DM fall into these two categories:
1. Lacking or missing built-in objects.
2. Features not supported by the syntax.
I understand that these things may just be a consequence of DM's long, slow evolution. Unfortunately, they make DM very difficult to use at times.
I have a feeling that many of these features were left out because they would be difficult to add at this point and the justification is that you're "keeping DM simple." Unfortunately this can backfire. Having fewer features keeps the language simple and makes certain tasks easy, but the lack of features also makes some tasks more difficult.
Purpose
The first section focuses on ways to improve and fix up built-in features. I'm not really asking for additional functionality to be added (not much, at least), I'm making suggestions about how current features should work. I'd really like for this to start a discussion of how these features should be implemented (how they should behave) and figure out how we can get them implemented. I've tried to address some of these problems with my Interface and Handy Stuff libraries, but I'd like for this to grow into a standard BYOND library (with multiple people contributing; contributing ideas at least, if not code) that supplements the built-in features.
Many of the features can be addressed with libraries and it's often the case that a library-based solution is easier than having the staff modify DM itself. It's also very likely that a library-based solution might run into a problem that requires a minor change to the DM language - this is much simpler than asking the staff to implement all of these features as part of DM itself. This can often only be realized after some discussion.
The second section focuses on the DM language. This is a wishlist of language features I'd like to see. I don't expect that any of these will be added, but the first step of improving the language is admitting that it has problems. I suspect that language features are neglected because they're very difficult to add and the staff doesn't realize how useful they would be and how many features are lacking.
Contents
Objects That Are Missing or Lacking:
• Icons
• Overlays & Images
• A /color Object
• Interface Controls
• Click Events
• Hub Scores
• Remove usr
• A "toString" Kind of Proc
Syntax and Language Improvements:
• Return Values and List Elements as Expressions
• Return Types
• Lists of a Type
• Zero-based Indexing
• Procs as First Class Objects
• Accessors
• Try/Catch
• Overriding Built-in Procs
-- Objects That Are Missing or Lacking --
Icons
Right now an /icon object could represent a set of icon states or just a single state. A state could be a directional icon, animated icon, both, or neither. MAKE A DISTINCTION BETWEEN THESE!! This is an excellent example of how having fewer object types doesn't always make things simpler.
You could have Icon, State, and Frame objects. An Icon object has an associative list with an entry for each icon_state in the file: Icon.state["state name"] gives you the /State object.
Each state object could have a 2D array to hold the /Frame objects (the indexes representing the frame # and direction). state.frame[NORTH][3] is the 3rd frame of the animation for the NORTH direction.
Overlays & Images
These should be complete, well-documented DM objects.
Images should have a viewers list so you can easily modify who can and cannot see the image as a property of the image itself. It doesn't make sense to have the images list maintained by the client because their mob or eye might change. If you want the image to be visible to the player regardless of their mob, add the client to the viewers list. If you only want it visible to the mob they're currently controlling, only add the mob to the viewers list.
Changing an image/overlay's properties should take effect immediately. For example, the current behavior for changing an overlay's pixel_x variable doesn't do anything, you have to remove the overlay and re-add it for the change to take effect.
A /color Object
The rgb proc could be a shortcut to creating a /color object, much like the icon proc for creating icons. When converted to text it would be represented as #RRGGBBAA. Having an actual object to represent the data lets you do things like this:
var/color/c = rgb(51, 51, 102)
// this prints "#333366"
world << c
c.green = 102
c.blue = 153
// this prints "#336699"
world << c
This is an excellent example of the cost of DM's simplicity. The idea of having objects with variables attached to them is something a complete newbie would have to get used to. You might think that introducing /color as another type of object that a newbie will have to remember would be confusing - but consider the alternative. If a newbie wants to increase the blue component of a color string generated by the rgb() proc they'd need to figure out how copytext and hexadecimal work. Saying c.blue += 30 seems a lot easier.
Interface Controls
I think I've made it clear elsewhere why the current implementation is an embarassment. This is similar to icons: having more object types would actually make things simpler.
Having interface controls is a huge feature but all of the documentation is shoved into a few over-crowded reference pages (winset, winget, winclone, winexists, winshow, output, and the skin reference). The /icon object has fewer functions but many more reference pages. Having /Label and /Button objects gives you a nice way to split the documentation into manageable pieces (i.e. pages for "vars (Label)", "vars (Grid)", etc.). It also creates a better API which eliminates the need for winset/winget/etc.
Click Events
Passing arguments as a large string that the programmer needs to parse is awful. This is a clear sign that you're missing an object to contain the event arguments. Like interface controls, this object lets you have separate help pages. Instead of the catch-all "mouse control" page you could have "MouseEvent", "vars (MouseEvent)", and "icon_x var (MouseEvent)" pages to distribute the information in a way that is clearer and more consistent with the rest of the reference.
I also don't like the way keyboard events are handled. It would seem to make more sense to have in-code ways to manage macros instead of the macro editor. It seems like the macro editor is a cheap way to avoid making changes to the language itself.
Hub Scores
It's okay for GetScores and SetScores to deal with associative lists. You're dealing with the website, there's no way they could return an object to represent a player's scores (so compile-time checking is not feasible).
Require that developers define score categories on the hub. Each category would have a type (number, text, time, datetime, etc.) so that the server can handle the details of the type instead of the DM program. The developer can specify that a "time" type of score is displayed using the "mm:ss" format but the call to GetScores would return the number of ticks as a number, not as the "mm:ss" formatted string (if they want this formatting in DM they can apply it themselves, going from formatted string -> ticks is much harder).
Scores should also have internal names and display names. Internal names are how the DM program references the scores, display names are shown on the standings page.
Remove usr
The developer doesn't define what usr is so it will always be confusing. Its use can almost always be avoided. For the few situations it cannot be avoided (or where avoided it makes code less intuitive) provide a reference to what usr would have been. For example:
obj
Click()
world << "[usr] clicked [src]"
// this would become:
obj
Click(MouseEvent/e)
world << "[e.usr] clicked [src]"
A "toString" Kind of Proc
The toString proc would be a member of the /datum type so that any object could override it.
Color
toString()
return rgb(red,green,blue)
This way when you write: world << "[color_obj]" it outputs the "#RRGGBB" representation of the color instead of "/Color".
-- Syntax and Language Improvements --
Return Values and List Elements as Expressions
List elements and return values are treated differently. You can't do this:
world << mob.get_target():x
world << some_list[3]:x
This can make code look cleaner:
var/Vector/v = new(1,0)
v = v.scale(2.5):rotate(30)
Return Types
This way you don't have to use the : operator for the previous request. Here's an example syntax:
mob/proc
mob/get_target()
for(var/mob/m in oview(4,src))
return m
With the previous feature this would let you do things like:
player << player.get_target().name
The compiler knows that get_target() returns a mob and that mobs have the name variable so this code is okay. Omitting the return type would result in the current behavior (no compile-time type checking can be done on return values).
Lists of a Type
Example syntax:
// declare a list of mobs
var/list/mob/list_of_mobs = list()
You can then do this:
list_of_mobs[1].experience += 1
Note: This doesn't guarantee that all objects in the list are of the specified type. It only tells the compiler to treat the element as that type. It saves you from having to typecast the element but doesn't do any runtime checking, that's still up to you.
Zero-based Indexing
Index arithmetic often works out better this way. It's also much more common. DM will always be an oddball language because it has a specific purpose, you don't need to go out of your way to make it more of an oddball. One-based indexing can be the default because it might make more sense to newbies that list[1] is the first element, but having the option would be nice for more experienced programmers.
Procs as First Class Objects
objects should have a procs list which mimics the vars list. This can be read-only if you'd like (not that you can append to the vars list, but you can say vars["x"] = 3).
You should be able to refer to procs by name. Example:
mob/proc
some_proc()
other_proc()
var/p = src.some_proc
p(3) // calls src.some_proc(3)
These types of things are generally possible with the call proc but the syntax looks horrible because you have to deal with type paths or strings. p(3) looks more like a function call than call(src, p)(3).
Accessors
Example syntax:
mob
var
health
get
health()
return health
set
health(h)
health = max(0, h)
When you assign a value to the mob's health the set accessor is called and the argument is the new value. When you refer to the mob's health the get accessor is called.
Try/Catch
Take your pick: http://en.wikipedia.org/wiki/Exception_handling_syntax
Exceptions let you jump multiple levels up the call stack. This can be very handy. You might have a large set of procs to handle a turn-based combat system. When the player ends their turn they might be three calls deep. Instead of needing a lot of special cases to check for this you can just throw an exception indicating that they ended their turn.
Overriding Built-in Procs
You can override the default behavior for a mob's Login proc but you can't override the default behavior of get_dist. This would be handy for some library-based fixing up of the language.