ID:2826138
 
Applies to:DM Language
Status: Open

Issue hasn't been assigned a status value.
I've looked at a few ways to better scope helper containers in large projects but they always come back to

/proc/MyLib_Thing()

or

/datum/MyLib
var/global/datum/Mylib/MyLib = new

- both of which are somewhat ugly. The latter is impacted by world init order and if the former uses proc-static vars the same - plus inherent leaking of any shared values.

I'd love to have some kind of namespace marker that gates access to procs behind a prefix without needing an instance of something, proc name prefixes, or other specifically user code space strategies.

For example,

/static/Math
var/const/PI = 3.14159
proc/Floor(num)
return round(num)
proc/Round(num)
return round(num, 1)

...

var/indianaPi = Math.Floor(Math.PI)


where Math cannot be instantiated in user code and is accessible simply as Math, a magic global.

I believe this would help solve and improve several things:

The byond library ecosystem could be improved. Someone providing a function library can literally do that, rather than a mess of stuff and free variables or an awkward bring-your-own-instance.

Running /static/foo/New() prior to world/New and prior to *instantiable* types means you always have these things available (so long as they don't demand one another, but that's tomorrow's problem).

--
As a stretch idea related to "How to break world.init", a numeric initialization order member on datums would be neat. Given a default 0 and simply sorting everything that's created before world/New into groups according to that number, where within a group stuff is ordered the same as current, but groups are run in an order determinable by game authors.
I've actually been thinking about namespaces for some time and how BYOND might implement them. I haven't come up with too many great ideas so I'm open to syntax suggestions.

The static keyword won't really work for this; that way lies madness. I also see no need to gate the proc and type names as such; I would much prefer if, instead, namespacing simply changed the way scope resolution was done so that 1) supplying a namespace would limit searches to within that space, and 2) any code within the namespace proper would prefer matches within the namespace.

For the most part what I have in mind might be fairly easy to do already, with one small problem: type paths would necessarily include the namespace in their path, which is far from ideal. And code like list += new/thing, where /thing is defined within the namespace, would then be unable to work without some significant help.
type paths would necessarily include the namespace in their path, which is far from ideal. And code like list += new/thing, where /thing is defined within the namespace, would then be unable to work without some significant help.

I'm not sure that's actually a problem? You can already use parent_type to turn long typepaths into short ones, and procs can be wrapped if nothing else; yes, you would ideally want to add an alias or import keyword to make it cleaner, but it can be done. And importantly, those aliases would be under the local project control, eg, if the library decided to name something
/lib/Math/datum/Number/Complex/floatingPoint
and you were going to use it a lot, you could decide your own name for it without breaking compatibility at all:
/complex/parent_type = /lib/Math/datum/Number/Complex/floatingPoint
Or more nicely, perhaps:
import /lib/Math/datum/Number/Complex/floatingPoint as /complex
// or
alias /complex = /lib/Math/datum/Number/Complex/floatingPoint


For the syntax to write a namespace, I would probably use a preprocessor start/end declaration, and have an automatic singleton with the same name that serves as the root object; "global" vars within the namespace would be vars on the singleton instead:
#library "Math"
/var/const/Pi = 3.1415 // This can be accessed as Pi or Math.Pi
/Math/var/const/Tau = 6.2831 // This too, because /Math == /
/Math/New() // Namespace equivalent to World/New; not for making more.
#endlibrary
// Pi is now only accessible as /lib/Math.Pi, etc

Granted what I just said implies that /lib/Math is both an object (the singleton) and also a node in the type tree, which is stupid, but I think that's a solvable problem, I'm just tired and I've rewritten this post like five times after overthinking it repeatedly.
Here's a syntax, dunno if possible but it'd be pretty clean.

#namespace Math
var/PI = 3.14159
proc/Floor(i) {return round(i)}
proc/Round(i) {return round(i,1)}
#endnamespace

mob/Login()
world << Math.PI
world << Math.Floor(3.469)
world << Math.Round(4.673)


Could show in the object-tree as.

/namespace/Math/var/PI
/namespace/Math/proc/Floor
/namespace/Math/proc/Round