###### tags: `Guide To Contibuting` # Variables A variable is exactly as it sounds. It's a thing that changes. We use it in our code to hold **stateful data**, of what a *thing is at the moment*. Basic declaration: `var/i = 3` `var/sometext = "text"` `var/datum/generic_datum = new` `var/untyped_variable = list()` `var/datum/more/specific/kind/of/thing = new` These hold data. Want to change it? Simply reassign it, but do not redeclare. `i = 5` `sometext = "othertext"` `generic_datum = thing` `untyped_variable = null` ## Initialization Using a `=` in a definition initializes the variable to that value when the code runs. **Doing this in an object's variables definition or globally initializes it instantly before anything else runs for the object's creation, or the server's start, respectively.** Avoid doing this in object variables when possible. We like being able to control our init order as it leads to less unexpected behavior from someone touching someone else's code. I won't suggest whether or not to do this in global variables, as that generally depends on codebase. ## Types You'll quickly notice that I'm using different kinds of var's here. Just doing `var/whatever` creates a generic variable. BYOND is **dynamically typed.** It will *let* you put anything into any type of variable. However, **always** cast your variables if possible. If something is only meant to hold a certain type of thing, **put the type in.** It lets other coders know what it's meant to hold (though you should comment your code anyways). ### Type checking `istext(thing)` returns TRUE or FALSE depending on if the input is some kind of string. `isnum(thing)` is the same but if the input is a number `istype(thing, typepath)` checks if an **instantiated object** is either that typepath, or a child of that path. More on that later. `ispath(thing, typepath)` checks if a **typepath** is either that typepath, or a child of that path. The typepath isn't an object. More on this later. **Always check types.** Just because you can do something doesn't mean you should, and this is very much good practice. Infact, our code linter (automatic checker) will flag your PR as failing if you are too careless, and it detects you are trying to do something awful. ## `static`, `global` These two terms are the same thing in BYOND. - Usage of global as globals is usually banned in our codebase. The information is included here anyways but more on this later. - If you are a coder reading this who isn't coding for Citadel Station or /tg/station where this guideline originates from you are free to call me silly and ignore this because only /tg/code deriviatives tend to enforce this. More on this later. - Usage of global as statics is usually banned in our codebase, *because it's inaccurate to what it actually is.* Use `static` instead when defining a static variable on an object. So first of all Defining a variable usually has you doing this: ```DM /datum/someobject var/objectvar = 5 /proc/globalprocthing() var/procvar = 3 /datum/someobject/proc/objectproc() var/procvar = "what" /datum/someobject/childtype objectvar = "look, I overrode the variable!" var/otherobjectvar = "Yay!" ``` Yes? If you do it on the *top level, above any definitions or proc scopes,* ```DM var/thisisaglobal = "This is bad practice on /tg/code!" var/global/soisthis = "Woo!" var/static/otherglobalvar = "Do not ever define a global variable this way, use global and not static!" /datum/datumdef var/thisisnot = "I'm not a global" ``` `thisisaglobal` is a **global variable.** It's accessible from anywhere in the code, **is initialized/instantiated before almost anything else**, and **only one copy of it exists.** For the love of all that is holy, use `global` if you decide to do this. Do not use `static` on a global variable, it is inaccurate to what a static variable is. Now, ```DM /datum/mydatum var/static/made = 0 /datum/mydatum/New() made++ to_chat(world, "[type] has been made a total of [made] times globall!") ``` Making two of these will say 2, instead of 1. Why? You've made two objects, right? It's because **static** variables only exist **once** per definition. Every copy of that datum made accesses this copy when editing or accessing it, **including subtypes.** The same result happens if you did `var/global/made` instead of `static` but **DO NOT DO THIS**, as it is not what a "global" variable means. Just because BYOND doesn't enforce basic *sanity* doesn't mean you shouldn't either! ## `const` This makes a read only variable. Since BYOND has negative amounts of optimizations, **this is banned in this codebase.** Use #defines, more on that later. Those don't have a performance overhead. **Accessing constants has the same performance overhead at time of writing as a normal variable even if it is a numerical value that never changes. Never use them.** The only possible time this could be potentially acceptable is when you want a constant datum on a global or static, but even then, **we do not use those for good reasons.** ## `tmp` This marks a variable as temporary, meaning it won't be saved by default save/loading functions. Unfortunately, no one bothers to use them, since nothing we use right now reads if a variable is tmp at all. If you want to, though, you're fine to use it for transient variables that track something like a cache or whatnot, instead of instance state data like a battery's charge or something. ## **Variable Optimization** - Compile vs Runtime This is only applicable to variables defined in the scope of an object. Example: ```DM /datum/example var/one = 1 var/two = "two" var/datum/three = new var/list/four = list() var/five = 5 + 5 ``` `one`, `two`, and `five` are set at **compile** time. **They use no memory during runtime (when the server is running) other than a single copy per override, unless the variable is changed. This is useful for optimization and for performance.** `three` and `four` are set at **runtime**. Since both lists and datums are objects, it is impossible to determine their reference (location in BYOND memory) at compile time. They are set when the object is made before *anything* else including New() is called, in a hidden initialization function. They will always use memory for the reference they hold. - This isn't something most of you will have to worry about any time soon, but if you're doing something that's making a list on every `/obj` for example, it's worthwhile to consider if it's better making a typelist or datum or something, as remember, the above only signifies memory used to hold the reference. The object made will also be held in memory, and it can quickly add up. - This means it's *entirely* acceptable to say, have `var/power_efficiency` on some kind of machine and override it on its children. As long as it's not changed at runtime (this isn't necessarily bad, just know when to change variables and when not to, you **cannot** avoid changing variables all the time, it's just being smart about it) it doesn't take any extra memory at all! - **Exception**: Static variables only hold one copy, ever, even if it is a reference/not set at compile time! Use this to your advantage when possible.