ID:102684
 
BYOND Feature Requests and Changes

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.
I totally agree with this post.
So you want to make DM into a strongly typed language, and you want to add features that you and only a small handful of other people will use? The only thing on this list particularly useful is the overlays thing. Also, your toString() proc is easily doable, and a /color datum could be made fairly easily (you'd just have to use a proc to set each attribute, rather than setting each variable with . or :).

Regardless, I recommend C++ and a good library if you really need these features.

EDIT: Actually, I like your last suggestion, too, but it's probably not doable.
Tsfreaks wrote:
You know... it seems like the BYOND engine might do well to OpenSource itself.

This has been brought up a lot, and the answer as I last saw it was "the code is not clean enough," or something along those lines.
Jeff8500 wrote:
Also, your toString() proc is easily doable, and a /color datum could be made fairly easily (you'd just have to use a proc to set each attribute, rather than setting each variable with . or :).

You can write a toString proc and color object in DM but BYOND's built-in functionality won't use them. The rgb proc and winset/winget would still deal with strings to represent colors instead of color objects.

Regardless, I recommend C++ and a good library if you really need these features.

I'm not asking for these features because I need them, I'm listing these features to show ways that BYOND could be improved (whether it gives people ideas for libraries or gets the staff motivated to do something).
These ideas are cool by me. BYOND needs to stop being so losery.
No griping sbout client-side processing? I'm shocked.

Shocked!
Still waiting for an reply to that post in the forums.

Also i agree with you!
SuperAntx wrote:
No griping sbout client-side processing?

I don't consider this a feature that should already exist, at least not in the way that a fleshed out, well-documented /icon object should exist.

The language requests help to make DM more appealing and help make it possible for libraries to better address the lack of built-in features. Client-side processing is a fairly huge request that is likely to create more problems than it solves (though it would be nice!)

Edit:
Tsfreaks wrote:
Perhaps it would be beneficial to find out what it would take to get a second paid developer?

I don't think this is likely so that's part of the reason why library-based solutions to these problems would help. For example, the toString proc would go a long way towards making a user-defined /color datum be more easily integrated with BYOND's built-in functionality. By having the staff complete a small feature requests we can develop libraries to complete other, more in-depth requests. In fact, most of the features from the first section could probably be addressed in this manner, with minimal work done by the staff.
I would very much like to see the syntax changes listed (though I don't care too much for the last two). A toString proc would also be nice to see. I like most of the changes in the first section, but I think click evens and color objects might as well be implemented in a library. I'm also not sure I agree with removing usr and zero-based indexing (both of these were discussed before, actually), at least not without backward compatibility.
Removing usr is tricky - consider set src in whatever verbs. Do they all get a magic parameter that is equivalent to usr?

Making list dereferences and procedures typable is probably a good idea, and is a prerequisite for making them expressions (properly, anyway), so all for that.

First-class functions and a lambda function (you didn't mention lambda, but it should have been in there. :P) would be great.

I don't think exceptions are necessarily a good idea, though. It's a lot of new syntax and semantics for a feature of limited use, with the potential for great evil. For example, your example of exception use is a poor use of exceptions. They're not for flow control. They're for errors.
Giving /datums a variable called "name" will have that shown rather than "/datum". Seemed pretty similar to this "toString()" proc to me, but maybe not.
Name is nice but it's a variable, so you have to modify it a lot in the case of dynamic names.
Jp wrote:
For example, your example of exception use is a poor use of exceptions. They're not for flow control. They're for errors.

Error handling *is* flow control. Whenever you have a line like "if(!m) continue" to avoid cases where m is null you're using simple flow control to avoid en error. Because exceptions provide more advanced flow control they are often used for error handling but they are by no means limited to error handling. Exceptions have many applications.

Removing usr is tricky - consider set src in whatever verbs. Do they all get a magic parameter that is equivalent to usr?

An implicit global variable called "usr" is magic. Parameters are not magic.

The situation you described can be fixed by giving the mob a verb that operates on objects in their contents instead of giving the object a verb that becomes available to a mob when inside the mob's contents. Instead of src being the object and usr being the mob, src is the mob and the object is a parameter.

usr couldn't be eliminated entirely for backwards compatability's sake. The motivation here is to provide alternatives for all situations so the "don't use usr" mantra is correct 100% of the time instead of being a rule of thumb with many exceptions.


Kaiochao wrote:
Giving /datums a variable called "name" will have that shown rather than "/datum"

Ideally you'd want to have code that works like this:

var/color/c = new /color()
c.red = 100
world << "[c]"


Currently you'd need to either use a proc to set the RGBA components so the name can be updated or you'd need to output "[c.toString()]" instead. The goal is to have the object behave as its user (the developer) would expect it to behave while internally it works like it needs to.
Forum_account wrote:
Jp wrote:
For example, your example of exception use is a poor use of exceptions. They're not for flow control. They're for errors.

Error handling *is* flow control. Whenever you have a line like "if(!m) continue" to avoid cases where m is null you're using simple flow control to avoid en error. Because exceptions provide more advanced flow control they are often used for error handling but they are by no means limited to error handling. Exceptions have many applications.

Can you really say that someone ending their turn is an exceptional situation? That's the sort of flow control I'm talking about.

If something shouldn't be null, ever, throw an exception if it is. If you expect that sometimes it will be null, handle it without exceptions. Exceptions are slow, semantically odd, and very much error-prone. It's a half-tamed setjmp/longjmp, and you should be well aware of the dangers of that.

Their principle point of usefulness is I/O errors - if reading/writing to a file/network/other process/whatever fails, throw an exception - More urgent than an error code, so it will be handled in some manner, and it is a scenario that really shouldn't happen that often. Bad parameters to a function are another point where exceptions are sometimes used, but I think this is misguided - what are you going to do to handle the exception? If it's just going to bubble up to the top level and quit the program, just printing to stderr is probably a better solution.

Removing usr is tricky - consider set src in whatever verbs. Do they all get a magic parameter that is equivalent to usr?

An implicit global variable called "usr" is magic. Parameters are not magic.

'usr' is magic, yes, I agree. The point is that a verb-on-object solution needs to get a reference to the mob doing the verbing, and without usr, a parameter that gets magically added to all appropriate set src in * functions is perhaps the only other sensible solution.

The situation you described can be fixed by giving the mob a verb that operates on objects in their contents instead of giving the object a verb that becomes available to a mob when inside the mob's contents. Instead of src being the object and usr being the mob, src is the mob and the object is a parameter.

Not sufficient. Then the verb needs to be added and removed from the mob manually when it is available/unavailable, which is kind of inconvenient and definitely at a point where you're making the language more complex.

usr couldn't be eliminated entirely for backwards compatability's sake. The motivation here is to provide alternatives for all situations so the "don't use usr" mantra is correct 100% of the time instead of being a rule of thumb with many exceptions.

Perhaps a 'usr()' function, that is only meaningful in verbs that are set src in * appropriately, and maybe a few pseudoverbs like Click()? The benefit of making it a function instead of a reference is that you can give an error if it's called somewhere where it's not meaningful. Alternatively, a compiler warning.
Jp wrote:
Exceptions are slow, semantically odd, and very much error-prone.

DM doesn't have exceptions so I'm not sure how they could be any of those things.

Then the verb needs to be added and removed from the mob manually when it is available/unavailable

Why?

These two are almost identical:

obj
verb
Get()
set src in oview(1)
Move(usr)

mob
verb
Get(obj/o in oview(1,src))
o.Move(src)


I'm not sure the subtle differences warrant the confusion created by usr.

In many ways DM's design makes easy things even easier but has the consequence of making slightly more complex things even harder. The easy things didn't need to be made easier so I'm not sure this was worthwhile. This is an example of that. Many changes that a newbie might want to make to Get's behavior might seem simple, but because so much of it is handled by "magic" (particularly in the case that uses usr and set src in oview) the changes end up being much more complex.

Perhaps a 'usr()' function, that is only meaningful in verbs that are set src in * appropriately, and maybe a few pseudoverbs like Click()?

That just replaces the weird set of rules governing the use of usr with a weird set of rules governing the use of usr().

Mouse procs were the only case I could think of where usr is necessary for writing reasonable code. With the proposed changes to how arguments are passed to mouse events the MouseEvent object could just contain a reference to usr. That change was what first got me thinking about how to make usr completely obsolete.

Another issue would be usr as a default value for built-in procs. I suppose this is less of an issue because the usr variable would always exist for backwards compatability reasons.
Forum_account wrote:
Jp wrote:
Exceptions are slow, semantically odd, and very much error-prone.

DM doesn't have exceptions so I'm not sure how they could be any of those things.

I meant in general. They'll be all those things in DM, too.

Any construct that jumps across contexts is pretty much by definition all of those things.

Then the verb needs to be added and removed from the mob manually when it is available/unavailable

Why?

These two are almost identical:

> obj
> verb
> Get()
> set src in oview(1)
> Move(usr)
>
> mob
> verb
> Get(obj/o in oview(1,src))
> o.Move(src)
>

I'm not sure the subtle differences warrant the confusion created by usr.

Notice that in the version where the verb is on the obj, it's automatically added/removed from the 'commands' statpanel (if it exists) when there's a /obj in/out of range, whereas the version where it's attached to the mob always has it on the statpanel and simply doesn't call it if you click it and there aren't any objs in range. While the statpanel probably shouldn't be present in any modern BYOND-based games, modern BYOND-based games probably don't use verbs.

In many ways DM's design makes easy things even easier but has the consequence of making slightly more complex things even harder. The easy things didn't need to be made easier so I'm not sure this was worthwhile. This is an example of that. Many changes that a newbie might want to make to Get's behavior might seem simple, but because so much of it is handled by "magic" (particularly in the case that uses usr and set src in oview) the changes end up being much more complex.

I'm trying to think of an example of doing something complex here that BYOND's design makes hard. Coming up blanks. Can you give me one?

Perhaps a 'usr()' function, that is only meaningful in verbs that are set src in * appropriately, and maybe a few pseudoverbs like Click()?

That just replaces the weird set of rules governing the use of usr with a weird set of rules governing the use of usr().

There isn't a weird set of rules. There's just one - 'is this function call directly invoked by an action of the user?'. That's not difficult.

The benefit of usr() is that you can print out a descriptive error message and kill the function when it's called in the wrong place, rather than perhaps work most of the time.
Jp wrote:
I meant in general. They'll be all those things in DM, too.

According to you exceptions are only for error handling but are also error prone themselves. That doesn't make sense but you don't need to explain it. It sounds like you read some context-sensitive information about exceptions and you took that information to be universal. We can discuss how DM's syntax for exceptions would work or why they'd be useful, but I'd rather not debate what exceptions are (at least not here, make a thread on my forum if you'd like).

I'm trying to think of an example of doing something complex here that BYOND's design makes hard. Coming up blanks. Can you give me one?

Given this:

obj/verb/Get()
set src in oview(1)
Move(usr)


Change the verb so that instead of being presented with a list to choose from when multiple objects are in range, have the verb arbitrarily select a single object for you to get. Or, for that same situation, have the verb get all objects within range.

The easy case was made easier by having many aspects of the behavior be magically handled for you. The programmer is not explicitly defining the behavior, they're using lots of built-in behavior. The two proposed changes sound simple but result in more complex changes to the code than a newbie would expect.

Making the easy case even easier gives people a false sense of simplicity. This makes the harder cases seem much more complex because the complexity exceeds your expectations. Making the easy case easier is only beneficial if that's all a programmer will need to know. Because this simple behavior is often insufficient a programmer will have to tackle more of DM's learning curve no matter what.

The previous paragraph can be said about most of the topics in the original post: overlays, colors, interface controls, click events, and hub scores. For example, it's easy to perform an action when you click on an atom. If you need to know the exact pixel that was clicked you have to jump through more hoops than you'd expect.
Forum_account wrote:
Jp wrote:
I meant in general. They'll be all those things in DM, too.

According to you exceptions are only for error handling but are also error prone themselves. That doesn't make sense but you don't need to explain it. It sounds like you read some context-sensitive information about exceptions and you took that information to be universal. We can discuss how DM's syntax for exceptions would work or why they'd be useful, but I'd rather not debate what exceptions are (at least not here, make a thread on my forum if you'd like).

I'll leave the topic alone. I think maybe the thing to take away is that some people don't like exceptions.

I'm trying to think of an example of doing something complex here that BYOND's design makes hard. Coming up blanks. Can you give me one?

Given this:

> obj/verb/Get()
> set src in oview(1)
> Move(usr)
>

Change the verb so that instead of being presented with a list to choose from when multiple objects are in range, have the verb arbitrarily select a single object for you to get. Or, for that same situation, have the verb get all objects within range.

Good example. I think it's arguable whether BYOND's design makes that case /harder/ than it would be otherwise, but you're right that it doesn't necessarily make it easier. Simplest case is just to move it to the mob and then handle verb appearance/disappearance yourself. If you're not fussed about appearance/disappearance it's trivial, but then you miss the point of having the verb defined on the /obj.

I think DM provides sufficient primitives that these more complex cases are still reasonably simple, but you are correct that there are a number of cases where hoops must be jumped through. I'm certainly not disagreeing with you when you say that there are a number of refinements that could be made to the language - my principle beef is with exceptions with a minor aside vis-a-vis usr.

Really, what I'd like to see is a formal specification of the DM language so third-party tools can be made more easily.
Jp wrote:
Really, what I'd like to see is a formal specification of the DM language so third-party tools can be made more easily.

I'd like to see a way to make plugins for BYOND so that third-party tools could be integrated with BYOND's tools. If you make a DM code editor that's better than the current one you wouldn't have many people using it because they'd still need to use Dream Maker for its icon, map, and interface editors.

The idea of plugins is very similar to some of these syntax requests. BYOND would need some internal changes to support plugins but then third party functionality could truly correct problems with the default tools. Libraries written in DM can't truly correct some problems because you can't always provide a clean syntax for using the library - similar to how third party tools will never seem as useful until they can really be integrated with the existing tools.

I think DM provides sufficient primitives that these more complex cases are still reasonably simple

At the very least, making the easy case easier makes the complex case less intuitive. For other topics the complex case actually gets messy because of DM's simplicity.

Every color is specified as a string of the form "#RRGGBB". This keeps things simple but makes it difficult if you need to modify an existing color because you need to convert the string to an integer. A newbie might not know how to jump through these hoops and an experienced programmer might think they were doing something incorrectly because there shouldn't be any hoops to jump through. In this case DM's simplicity leaves everyone scratching their head.
Page: 1 2