ID:260840
 
This is part bug report and part feature request, but moreso a feature request (the feature would fix the bug).

I noticed that some built-in procs have names that may commonly be used for variables. Often the compiler gets it right and there's no issue, for example:

world << "A"
var/sleep = 0
sleep = 10
sleep(sleep)
world << "B"


It knows that the assignment "sleep = 10" refers to the local variable, not the proc. However, this next example does give an error:

A/proc/print()
world << "hello, world!"

B
var
A/text = new /A()
proc
print()
// error:text :bad format string
text.print()

mob
Login()
var/B/b = new /B()
b.print()


Since situations like this are usually handled correctly, I'm not sure if this one is a bug. If I reference a member variable of text instead of a proc there is no error.

Whether it's a bug or not, it's fixed by specifying "src.text.print()", but it doesn't seem like that should be necessary.

This is where namespaces come in: If the text() proc was instead referenced as BYOND.String.format(), this wouldn't be a problem. Similar to namespaces in C# or packages in Java, you could declare (for a file) which namespaces you are using. And if the default was to include all namespaces this wouldn't break old code. Since the . operator is used for other things I don't know if it's the best choice here.

When a variable is referenced, the compiler has to check if the name refers to a local variable, a member variable, or a global variable. You'd also need to check for the name in declared namespaces.

This also provides a way to group procs by something more than similarity of their function. For example, I was looking for the copytext() proc but never would have guessed that name (it's substr in most other places, but I'm not bitter, I found it eventually). I found findtext(), whose help page links to findText() but not copytext(). If all string functions were in the BYOND.String namespace, these functions would be easier to find.

By letting users assign global procs to a namespace this would also address problems like this one [link]. Let's hope Mobius doesn't mind:

// library code
proc
RGB()
set namespace = Mobius.Color
// code

// referencing the library
proc
test()
world << Mobius.Color.RGB()


This could be implemented now (minus the set namespace bit) by using global variables, but this could get messy. For example:

var
MobiusNamespace/Mobius = new /MobiusNamespace()

MobiusNamespace
var
MobiusColorLibrary/Color = new /MobiusColorLibrary()

MobiusColorLibrary
proc
RGB()
return 1

proc
test()
world << Mobius.Color.RGB()


Declaring namespaces for each proc makes it easier to code and maintain. Also, having namespace declarations as part of the language would make it very easy to have auto-completion of namespaces in the editor (I'm sure code completion is requested a lot). With code completion, if the current token is "t." you'd need to figure out what kind of object t is and what members it has. With namespace completion you just need to know if "t" is a namespace (or a prefix of a namespace) and what it contains. Since namespaces are declared explicitly you'd have this information.

This would make libraries and built-in procs much easier to use.
Forum_account wrote:
I noticed that some built-in procs have names that may commonly be used for variables. Often the compiler gets it right and there's no issue, for example:

... example here ...

Since situations like this are usually handled correctly, I'm not sure if this one is a bug. If I reference a member variable of text instead of a proc there is no error.

Whether it's a bug or not, it's fixed by specifying "src.text.print()", but it doesn't seem like that should be necessary.

I don't understand this. You say there's an issue, you give an example, then you say that "situations like this are usually handled correctly." and then "it's fixed by..." Is there a problem? If so, what is it? Can you provide an example where it is not handled correctly?

Similar to namespaces in C# or packages in Java, you could declare (for a file) which namespaces you are using.

Or like C/C++, which Byond is created with.

And if the default was to include all namespaces this wouldn't break old code.

Or "no namespace." You wouldn't want to require everything to have a namespace. At least, I wouldn't think you would want to.

Since the . operator is used for other things I don't know if it's the best choice here.

Byond is based on C++ which uses :

I'm sure code completion is requested a lot.

Actually, though I don't visit this section of the forum with great frequency, I don't think I've ever seen anyone request that, which surprises me now that I think about it. Possibly because a lot of Byond users don't have experience with other IDEs and don't know such nifty things exist. In my oppinion though, it gets annoying when autocomplete popups block the view of what I'm working on and sometimes interfere in other ways.

Namespaces would be neat to have. I'm not sure how necessary they are, but it would probably be a nice addition. Probably better would be static functions, then you could have something that worked basically just like a namespace for what you want but was more powerful. That would be awkward with the way Byond does object type references though. Might look something like /string.copytext() or /string:copytext().

Pending that, you could make your own library. I know that would not free up all the functions you want, but it would be a start.
var/String/String = new
String
proc/copytext(T, Start, End)
return copytext(T, Start, End)

proc/lowertext(T)
return lowertext(T)

... etc.

You could put a bunch of stuff like that in a library, even if you only used it for yourself. Then you could include it in all your projects and you could always do String.copytext() and if you wanted a heirarchy of these things, you could do something like
var/BYOND/BYOND = new
BYOND
var/String/String = new
...
In response to Loduwijk
Loduwijk wrote:
I don't understand this. You say there's an issue, you give an example, then you say that "situations like this are usually handled correctly." and then "it's fixed by..." Is there a problem? If so, what is it? Can you provide an example where it is not handled correctly?

To start, here's an example of how it is handled correctly:
var/name = "global"

Object
var/name = "object"
proc/print_name(name)
world << name

mob/Login()
var/Object/o = new /Object()
o.print_name("argument")


The line we're concerned with is world << name. The proc takes an argument called "name", the object has a member variable called "name", and there's a global variable called "name". What is the output?

That code will output "argument" since the first scope that is checked is the local scope. If we remove the name parameter the code will still compile and the output will be "object". This indicates that the scope of the member variables is checked after the local variables. If we remove the member variable it will output "global". This indicates that the global scope is checked after local and member variables.

Here is the example of when this does not work:
A/proc/print()
world << "hello, world!"

B
var
A/text = new /A()
proc
print()
// error:text :bad format string
text.print()

mob
Login()
var/B/b = new /B()
b.print()


The compiler thinks that text.print() is referring to the built-in text proc. However, since there is a member variable called "text" it should be matching the member variable. Since the behavior is inconsistent it seems like there is a bug here. Despite the existence of a workaround, a bug is a bug.

And if the default was to include all namespaces this wouldn't break old code.

Or "no namespace." You wouldn't want to require everything to have a namespace. At least, I wouldn't think you would want to.

Including a namespace is different than assigning a namespace to a proc. If the copytext() function was in the BYOND.String namespace, this is how you'd have to reference it:
proc
something(txt)
txt = BYOND.String.copytext(...)


By including a namespace (or using it, or importing it, whatever you want to call it) you can reference the proc without specifying the namespace:
using BYOND.String

proc
something(txt)
txt = copytext(...)


The using BYOND.String lets you reference all members of the BYOND.String namespace as though they weren't in one (so you don't have to type BYOND.String all the time).

Introducing the namespace feature would break a lot of old code. This would be a good reason to never implement the feature (but I'm not letting the developers off easy!). If all namespaces are used by default then old code would not break (since using all namespaces would prevent the need to reference functions by namespace)

Probably better would be static functions, then you could have something that worked basically just like a namespace for what you want but was more powerful. That would be awkward with the way Byond does object type references though. Might look something like /string.copytext() or /string:copytext().

Static methods aren't more powerful, they're just different.

Namespaces would be applied to built-in procs. This provides a way to group functions. If you were new to DM and wanted to find a string function you could look in the BYOND.String namespace. If you want to see what built-in functions exist you could also look in the namespace.

I was surprised to find that the substring function is called copytext. I was also surprised to find that there's no string split function. Even a small amount of programming experience prior to using DM will give you expectations about what DM should have and what it might be called. The more the language differs from your expectations (and the harder it is to identify these differences) the less appealing it is. Aside from being handy, namespaces would make the language more appealing and easier to pick up.
Not seeing how a few keywords being reserved means we need namespaces. Also not seeing how namespaces would actually make anything simpler... you justify it by saying that you would be able to search the reference by them, but that would be considerably easier to do by simply providing a new way of browsing through the reference.
In response to Garthor
Garthor wrote:
Not seeing how a few keywords being reserved means we need namespaces. Also not seeing how namespaces would actually make anything simpler... you justify it by saying that you would be able to search the reference by them, but that would be considerably easier to do by simply providing a new way of browsing through the reference.

Features don't have to make things simpler. Sometimes new features just introduce new features.

Namespaces would also act as a simple way to satisfy many of the requests for static methods. By using namespaces instead of type paths the syntax could be cleaner too.
In response to Garthor
Garthor wrote:
Not seeing how a few keywords being reserved means we need namespaces. Also not seeing how namespaces would actually make anything simpler... you justify it by saying that you would be able to search the reference by them, but that would be considerably easier to do by simply providing a new way of browsing through the reference.

I'm mostly with this line of thought. I don't think namespaces make anything simpler until you already know what you're doing, which is not the majority of BYOND users.

BYOND teaches object-oriented programming without being quite as demanding as languages like Java or C#. You learn how objects (a sensible item in almost every game) can have parents and children, without having the inheritance structure forced at you in every context - such as namespaces.

Namespaces are really just inheritance and categorical grouping for members/methods, but I don't really think it's that necessary for BYOND.

<small>Personally I'd rather see better naming conventions for built-in methods and variables. Having procs with unreliable case and convention, like "step_away()" "SwapColor()" "Swap()" "text()" "text2ascii()" was confusing at first, and doesn't teach very good ettiquette.</small>


~Polatrite~
In response to Forum_account
Forum_account wrote:
Static methods aren't more powerful, they're just different.

Actually, they are more powerful. You can use them exactly the same as a namespace if chose to do so, but they're not limited to that. That is, anything you want from namespaces for functions could also be accomplished by object's having static functions, but they are not limited to that. That is, the usefulness of a namespace is entirely contained within a proper subset of the usefullness of static functions.

(static function > namespace function) because static function gives everything the namespace function gives plus more

I was surprised to find that the substring function is called copytext. I was also surprised to find that there's no string split function. Even a small amount of programming experience prior to using DM will give you expectations about what DM should have and what it might be called.

Indeed, though again, you could make a library for this. In fact, it would probably be a good idea to start making libraries for a bunch of this stuff and try to get a good documentation style going, like the javadocs. I understand this is not the built-into-Byond answer that you might like, but it could work for the moment.
In response to Polatrite
Polatrite wrote:
Personally I'd rather see better naming conventions for built-in methods and variables. Having procs with unreliable case and convention, like "step_away()" "SwapColor()" "Swap()" "text()" "text2ascii()" was confusing at first, and doesn't teach very good ettiquette

It's that way because Byond is based on C++, not on Java! As much as my preferred language is C++, Java has it beat, hands down, when it comes to naming conventions, documentation, and reasonable design of APIs.
In response to Polatrite
Polatrite wrote:
I'm mostly with this line of thought. I don't think namespaces make anything simpler until you already know what you're doing, which is not the majority of BYOND users.

It is a legitimate concern that a new feature might confuse people, but that isn't always the case. If you won't be required to use the feature it won't confuse people who don't understand it. They'll just ignore the feature.

To use a recent thread as an example: There was a post about negative list indexes ([link]). This is a useful feature that does make lists more complicated, but negative indexes wouldn't be mandatory. You would still be able to index into lists like you always have. People who don't understand negative indexing wouldn't be confused, they'd simply not make use of the feature.

Namespaces are really just inheritance and categorical grouping for members/methods, but I don't really think it's that necessary for BYOND.

BYOND isn't necessary. You could argue that no feature is necessary, but that doesn't mean we can't brainstorm.

<small>Personally I'd rather see better naming conventions for built-in methods and variables. Having procs with unreliable case and convention, like "step_away()" "SwapColor()" "Swap()" "text()" "text2ascii()" was confusing at first, and doesn't teach very good ettiquette.</small>

Agreed!
In response to Loduwijk
Loduwijk wrote:
Forum_account wrote:
Static methods aren't more powerful, they're just different.

Actually, they are more powerful...
static function gives everything the namespace function gives plus more

Here is how you would have to use a static method (obviously using a pretend syntax):

Something
static
method()
// do something

mob
Login()
world << Something.method()


But with namespaces it's possible to do this:

using Something

proc
method()
set namespace = Something
// do something

mob
Login()
world << method()


This is an example of something that can be done with namespaces that cannot be done with static methods. Clearly, static methods do not give everything and more.

The reason is that the static method is a new identifier in an existing scope. Namespaces create a hierarchy of new scopes. By declaring which namespaces you use, you tell the compiler/interpreter which scopes to look for identifiers (in addition to the default scopes it would check).

DM seems to be the first programming language for many of its users. Part of the motivation to add features like this (i.e. features that exist in more popular languages) is to allow DM users to learn about more programming concepts.
In response to Forum_account
Forum_account wrote:
> Something
> static
> method()
> // do something
>
> mob
> Login()
> world << Something.method()
>

But with namespaces it's possible to do this:

> using Something
>
> proc
> method()
> set namespace = Something
> // do something
>
> mob
> Login()
> world << method()
>


As I recall, you used C# and Java as examples. I don't know about C#, as I have only used it a little bit, but Java doesn't allow any such methodology. At least, if it does, I don't know about it.

Still, that is in no way something that could be accomplished by namespaces but not static functions. If you're going to allow people to set a different namespace for every function, or for a certain scope, or just at will and change it as much as they want in a function, or whatever, there's no reason you couldn't do the same for static methods. I recall VB had (don't know if VB.NET still has it or not) a "with" statement (I think that's what it was called). We could borrow this idea and this would allow you to access variables and functions of a given structure without specifying its name.

MyObject
var/static/stuff = "whatever"

proc
static/myStaticFunction()
...

proc/stuff()
set namespace = MyObject
world << stuff
myStaticFunction()

Or something of the nature. Could even use with.
proc/stuff()
with MyObject
world << stuff
myStaticFunction()

And if you wanted to let "with" be changable, you could have an endwith, or with could have its own scope that you need to indent under, or whatever.
proc/stuff()
with MyObject
world << stuff
myStaticFunction()

Personally, I think C++ goes about it the right way. It uses the same syntax for both, so it doesn't matter what way you want to look at it.
void myFunction()
{
std::string myString;
MyObject::myStaticFunction();
}

Hmm, this makes me wonder... is it part of the C++ specification that you can use "using" with an object? Probably not, but since they tried to make it all work the same I wouldn't be surprised. I'll have to test that out later and see. That'd be cool if I could do "using MyObject"

One more thing, though it's just a silly little nitpicky thing. You mentioned the ambiguity in
var/global/myVar

obj
var/myVar

proc/stuff(myVar)

Just don't forget that this exists in other languages too. Obviously, the global part doesn't exist in Java since there is no such thing as a global variable, as everything has to be a class member, but everything else (including the global part) applies to other languages too, such as C++. This is one reason (though obviously not the only reason) we need the "this" pointer, which is the equivalent to Byond's "src"

And one last thing, just an FYI that you might like. It's nothing secret, but I've noticed some people don't seem to know about it. In Java, you can create a class that basically mimics a set of global variables.
import static MyClass.myStaticMember;

public class Tester
{
public static void main(String[] args)
{
System.out.println(myStaticMember);
}
}

If you do that, you do not need to qualify myStaticMember as MyClass.myStaticMember. You can even use the wildcard * with this.
import static MyClass.*;

And all of MyClass's static variables will be available for you to use unqualified.

For example...
import static javax.swing.WindowConstants.*;
import javax.swing.JFrame;

public class Test
{
public static void main(String[] args)
{
JFrame frmMain = new JFrame("My Tester Frame"),
frmSub = new JFrame("My Subframe");
frmMain.setDefaultCloseOperation(EXIT_ON_CLOSE); // instead of WindowConstants.EXIT_ON_CLOSE
frmSub.setDefaultCloseOperation(DISPOSE_ON_CLOSE); // instead of WindowConstants.DISPOSE_ON_CLOSE
frmMain.setVisible(true);
frmSub.setVisible(true);
}
}

Just something I thought might interest you in case you didn't already know, since we're on this topic.
In response to Loduwijk
Loduwijk wrote:
One more thing, though it's just a silly little nitpicky thing. You mentioned the ambiguity in
> var/global/myVar
>
> obj
> var/myVar
>
> proc/stuff(myVar)
>

Just don't forget that this exists in other languages too. Obviously, the global part doesn't exist in Java since there is no such thing as a global variable, as everything has to be a class member, but everything else (including the global part) applies to other languages too, such as C++. This is one reason (though obviously not the only reason) we need the "this" pointer, which is the equivalent to Byond's "src"

Other languages do not have this problem. This situation is only ambiguous if there are multiple possible behaviors. In your example, an identifier called myVar inside stuff() can only refer to one thing: the myVar parameter. If the identifier is found in the local scope, no broader scopes are checked. This behavior is well-defined and not at all ambiguous.

The original problem is that DM does have some unexpected behavior, so there is some ambiguity.

The first code bit in [link] shows that DM correctly finds the name identifier in the local scope before all others. With that identifier absent the member variable is found next. With that identifier also absent the global variable is found. This is not ambiguous behavior, it is well defined. The local scope is checked first. If a match exists in the local scope it is used (there is no other possible behavior). If a match exists in a broader scope it is ignored.

The problem is that in the second code bit of [link] it seems to miss the text member variable and move onto the global scope.

In response to Garthor
I would actually support the namespace notion, but not for the reasons indicated in the request. Namespaces could help prevent naming conflicts between multiple libraries and between libraries and user-code, and could even help within single projects where several developers are working. If Naruto: Kamehameha no Jutsu has 3 programmers that aren't necessarily the most organized (as is the norm with BYOND's projects), the code-work may get sectioned off among the developers only to be combined and find that there are many naming conflicts that require a large amount of (potentially error-prone) work to fix.
In response to Forum_account
Forum_account wrote:
Other languages do not have this problem. This situation is only ambiguous if there are multiple possible behaviors. In your example, an identifier called myVar inside stuff() can only refer to one thing: the myVar parameter. If the identifier is found in the local scope, no broader scopes are checked. This behavior is well-defined and not at all ambiguous.

Two things. First, I was referring to your call for the need of namespaces, not of the possible bug you mentioned; I can see where my statement itself could have been the thing that was ambiguous ;) Second, and more seriously, it is ambiguous. A series of events can have known reactions and therefor lead to a predictable outcome, but the grammar that the compiler uses is still ambiguous when it comes to certain things, such as this. If you have the same identifier referring to multiple things, it's ambiguous; the compiler just gets to have a set of rules that decide how to deal with it.

Regardless of semantics though, as I said, I was referring to the "need" for namespaces. I'm not disagreeing that they would be nice, rather I'm pointing out that the problem of having an identifying name that could potentially mean different things (though obviously which one gets used is defined, but that doesn't matter) is not unique to Byond. Of course, in C++ you DO have the ability to use a namespace.

Still, I would rather be able to (last plug for it) have static functions. On that note, static variables that are accessible without the need to instantiate an object would be nice too.
In response to Polatrite
Polatrite wrote:
Personally I'd rather see better naming conventions for built-in methods and variables. Having procs with unreliable case and convention, like "step_away()" "SwapColor()" "Swap()" "text()" "text2ascii()" was confusing at first, and doesn't teach very good ettiquette.

There are already good naming conventions: lowercase for functions, UpperCase for methods. That is: any proc belonging to an object is in UpperCase, whereas any proc that is global is in lowercase. Functions also must use underscores to separate words, because they don't use case to indicate where a new one begins. The 2 for the conversion functions is a bit iffy but at least is consistent among them.
In response to Loduwijk
Loduwijk wrote:
Second, and more seriously, it is ambiguous. A series of events can have known reactions and therefor lead to a predictable outcome, but the grammar that the compiler uses is still ambiguous when it comes to certain things, such as this.

The code may be ambiguous to you, but it should never be ambiguous to the compiler. If it was, that would mean that it's possible to run the compiler twice with the same settings and same input and get two functionally different programs. This would be quite a problem.

Code may be ambiguous to a person if the person doesn't understand how the compiler works. For example:

var/a = 1
var/b = 1
var/c = a+++b


To the compiler this code has a distinct interpretation, but to a person the last line is likely to cause confusion.

Still, I would rather be able to (last plug for it) have static functions.

I hadn't thought about static methods much, what would the benefits be over using existing techniques as a workaround? For example:

var
ClassName = new /ClassName()

ClassName
var
x = 5
proc
method()
// code

mob
Login()
ClassName.x = 7
world << ClassName.method()


Obviously having a syntax for this that is recognized by the compiler would make the syntax cleaner (possibly, the slashes in type paths don't always look nice). Are there any particular uses for static methods you have in mind?
In response to Forum_account
Forum_account wrote:
The code may be ambiguous to you, but it should never be ambiguous to the compiler. If it was, that would mean that it's possible to run the compiler twice with the same settings and same input and get two functionally different programs. This would be quite a problem.

Code may be ambiguous to a person if the person doesn't understand how the compiler works. For example:

As I said, that's not what I meant. It can be ambiguous and still give the same results every time by adding special rules to deal with the ambiguity. For an example, what happens if you have (not in Byond, obviously) "if a then if b then c else d" which if does the else go with? Depending on the grammar that defines the compiler's parsing, it could go either way, and it is called an ambiguous grammar. The string needing to be parsed, if we call it X, could go first to "if a then X" then X could go to "if b then c else d" in which case the entire thing would be "if a then (if b then c else d)" or it could go initially to "if a then X else d" in which case it would end up as "if a then (if b then c) else d" Most compilers will have a special rule to deal with this that says "The else goes with the closest if." So it still compiles the same every time, because it has special extra rules that tell it what to do in the specific ambiguous case.

But none of this adds to the conversation, and it doesn't really matter. There's not really a need to argue over semantics.

I hadn't thought about static methods much, what would the benefits be over using existing techniques as a workaround?

Pretty much exactly the same answer you would give if I asked you that same question with "static methods" replaced by "namespaces." In fact, the workaround I showed for namespaces is basically the same thing as what you just showed for static functions. They serve similar needs and have only minor differences.

Are there any particular uses for static methods you have in mind?

A lot of normal design patterns require static functions. As just one simple example, there is often a need in software to ensure that one, and never more than one (sometimes none is allowed depending on the issue), instance of an object is in existance at any one time. In this situation, the object generally does not have a public constructor and has a static function that returns the reference to the single instance of the object.

Java is an excellent example of the use of static methods. Java does exactly what you're requesting, just not with namespaces. If you want to use some method that handles strings or integers or whatever, you have to do something like Integer.parseInt(someString). And as for my example above, when you are running a Java program, you have access to 1 Runtime object, and you have to call the Runtime.getRuntime() method to access it.

Of course, you could make global variables and functions in Byond to accomplish the same thing, but then we run into the exact problem your entire thread here is trying to avoid; having a billion global variables and functions that should be somehow attached or related to a specific object, namespace, or whatever you want to use to categorize them.
In response to Loduwijk
SAX and DOM XML parsers are usually also good examples of sensible static method use, as they typically implement the flyweight pattern (internally or externally) to reduce the memory footprint of the ordinarily large schema validating parsers.
In response to Kuraudo
If people are really that unorganized, they can just use fake "namespaces". For example, in objective-C, people usually use 2 letter prefixes when naming classes (ex. CCMenu instead of Menu).