ID:287671
 
I'm a programmer. As such, I'm impatient.

Many people don't understand the true complexities of programming. There is more to code than just writing code. Programming is a unique field, a crossroads that few other fields dare to travel: It's where mathematics meets engineering meets art. A good programmer must know the mathematics of what they're doing, of how to analyze algorithms to understand what works best for them. A good programmer must be a knowledgeable engineer, able to write robust, reliable, efficient code in a way, and documented in a way, that is precise, concise, and clear. A good programmer must be an artist, able to write code that is elegant, extensible, maintainable, and usable. The first and second are clear to many users. But what about the art of programming?

I am impatient. People who do not understand good design principles, people who can not write elegant code, extensible code, maintainable code, useable code. They annoy me. People who can not design good code. And, thus, I grow weary of helping them, sometimes going so far as to just outright stop. But this shouldn't be so. There should be no reasons why the average user of BYOND, the average DM programmer, can not write code that implements good design principles. The Art of Code is my attempt to help users with this, to understand the art of programming, and the beauty of code. These tutorials will be aimed at a slightly more-advanced audience than my So You Want To Program tutorials, but will be written as clear as possible nonetheless. So, let's begin, shall we?

My first tutorial on this series is going to focus on namespaces and inheritance as applied to DM. These two language features are dinstinct in most object-oriented programming language, but this isn't so in DM. In fact, the language muddles the two so much that few people recognize the difference that exists. Nonetheless, the distinction is still present, and not even that far from the surface.

Inheritance

First off, let's discuss inheritance. To most DM programmers, this should be a topic you are already familiar with. Inheritance is one of the main features of object-oriented programming languages. Certain types of programming constructs, called classes, can inherit their features from other classes. The inheriting classes are called subclasses, and the inherited class is called a superclass. For example, below is a typical example of classes and inheritance.

animal
/*
* This defines the animal superclass. We define a few variables and procs for the
* subclasses to use.
*/


var
hunger
thirst

bathroom

proc
Speak()
// How the animal 'speaks', or vocalizes. Some animals just don't.

Eat()
// Some animals eat meat, others eat plants. Others do both.
hunger ++

Drink()
// How the animal drinks. Some sea animals basically do it when they eat.
thirst ++

Bathroom()
// How the animals uses the bathroom.
bathroom --

Move()
// How the animal moves.

Die()
// Called when the animal dies.
del src

animal/dog
/*
* A dog is a type of animal, so we'll use it.
*/


Speak()
world << "[src] barks!"

Eat(animal/a)
world << "[src] eats [a]."
a.Die()

..()

Move()
world << "[src] walks."

animal/fish
/*
* A fish is a type of animal, but one quite different from a dog.
*/


Speak()
// Fish don't really have a vocalization, so nothing happens.
return null

Eat(animal/a)
// We have a carniverous fish here.
world << "[src] eats [a]."
a.Die()

Drink() // It drinks when it eats.
..()

Move()
world << "[src] swims."

animal/bird
/*
* Another type of animal, different from a fish and a dog.
*/


Speak()
world << "[src] chirps."

Eat(plant/berry)
// This is a berry-eating bird.
world << "[src] eats [berry]"
berry.Eaten()

..()

Bathroom()
world << "[src] goes to the bathroom on a nice, clean car."
..()


And there we have a small example of how inheritance can work in DM. This is the way most people work with, but as we'll see later, this isn't the only option available to DM programmers. It's still simple, and intuitive, but not always the kind of thing we want.

Namespaces

At first glance, namespaces are a feature that does not appear to be present in DM. Before I go any further, let me explain what a namespace is. Think of a namespace as an abstract kind of 'container' that holds a lot of similar things together. For example, if you have a Gun, maybe you'll have a few different things associated with it. There might be the Gun itself, ammunition, and maybe Magazines or Clips. You could hold all of this together in a Weapons or Guns namespace. Why is this useful? Maybe there is ambiguity in your code. Maybe your code also has other types of magazines, like the kind that you can read, or other kinds of clips, the kind that you can use to hold two things together.

Without a namespace, this becomes ambiguous. Obviously the Magazine or Clip can't inherit from a Gun. That makes no sense, as they don't share any features with a gun. Thus, what are your options? You could rename them, but that's not a good choice. With namespaces, you can store the Clip and Magazine away under the Guns or Weapons namespace, and you have absolutely no conflict. Your readable Magazine and your holds-things-together Clip can exist and there doesn't have to be any conflict whatsoever.

But as I said, it doesn't appear that BYOND has namespaces. So, why am I talking about this at all, then? Well, the thing is, BYOND does have them. In fact, you use them all the time. It's just that they are not immediately obvious. In most other programming languages, the separation between inheritance and namespaces are immediately apparent, because the way they are both handled is entirely different. BYOND, though, combines both of these things in its typepath system. A typepath, by default, indicates inheritance, but it is not the same inheritance. In fact, inheritance in BYOND can be overwritten. How?

The parent_type Variable

You may have used the parent_type variable before. Most people use it to see what the typepath of the parent is. For example, in our example above, the parent_type of each /animal/dog, /animal/fish, and /animal/bird is just /animal. When your code is compiled, this variable is automatically set to what the superclass is. The thing is, this isn't the only way to use this variable, and in fact this is more of a minor way of using it.

Here's the best thing about the parent_type variable: It can be set by the programmer at compile time, and this changes what the class inherits from. Thus, the programmer has the ability to entirely change the default method of inheritance in DM. Why is this useful? Because it can simultaneously be used to create namespaces. As I mentioned above, the notions of inheritance and namespaces in DM are tied together through the typepath system. Without explicitly overriding it, when you have something like /foo/bar, by default /bar.parent_type = /foo. But, by using the parent_type variable it is possible to turn /foo into a namespace. In fact, unlike any other programming language that I know of, /foo simultaneously becomes both a class and a namespace.

Implementing Namespaces

Now, back to the gun example I mentioned earlier. How would you implement this in DM? Below is one possible way.

gun
/*
* Here we implement the gun namespace/class. It inherits from /obj, but it becomes a top-
*level class. Thus, to create a new gun object we just have to wrote new /gun. It is
* not necessary to know that the /gun
* is a type of /obj, and thus this information can be left out, if a user
* so wishes.
*/


parent_type = /obj

var
// The name of the gun.
name

// The type of ammunition the gun uses.
ammunition_type

// The type of magazine the gun uses.
magazine_type

// The magazine currently being used. May be switched out.
gun/magazine/magazine

// The mob holding the gun.
mob/holder

proc
Load(magazine/m)
if(!istype(m))
// If it's not at all a magazine type, just return.
return 0

if(!istype(m, src.magazine_type))
// If it's a magazine, but the wrong kind, temporarily Jam() the gun.
src.Jam()
return 0

if(!istype(m.ammunition, src.ammunition_type))
// If it's the wrong type of ammunition, jam the gun.
src.Jam()
return 0

// If everything goes well, load the magazine.
src.magazine = m
src.magazine.gun = src
return 1

Jam()
// Not actually implementing this, as the way it jams could really vary, and
// it isn't really necessary to actually implement it.

Fire(angle)
// Attempts to fire a bullet in the given angle. This can be overwritten to,
// for example, result in an inaccurate or unreliable gun.
return src.magazine.Fire(angle)

magazine
/*
* Now we implement the magazine.
*/


// As with /gun, /gun/magazine derives from /obj.
parent_type = /obj

var
// The type of ammunition the magazine stores. Just store a
// typepath here.
gun/ammunition/ammunition

// The gun the magazine is loaded into.
gun/gun

// The amount a magazine holds. Should be an integer greater than zero.
max_size

// The amount currently being held.
size

New()
// Create a new ammunition, as indicated by src.ammunition, and set
// src as its magazine.
src.ammunition = new src.ammunition(src)

return ..()

proc
Reload()
// Reloads the magazine.

if(size < max_size)
src.size ++
return 1

else
return 0

Fire(angle)
if(src.size > 0)
// The amount of ammunition in the magazine.
src.size --

// The bullet fires.
return src.ammunition.Fire(angle)

else
return 0

ammunition
/*
* And now we implement the ammunition.
*/


// A type of datum. Ammunition only says how to model individual bullets, rather
// than being something that exists in the world.
parent_type = /datum

var
// The damage the bullet does.
damage

// The stability of the bullet. How much it wobbles in the air. A percentage.
stability

// The magazine the gun is attached to.
gun/magazine/magazine

New(gun/magazine/magazine)
src.magazine = magazine

proc
Fire(angle)
// Whatever happens here. Presumably, a bullet object would be
// created (perhaps as a /gun/bullet, or an /obj/bullet, or whatever
// solution works best).


And here we have a very basic namespace for guns, with a top-level /gun object and a few classes in the namespace that handle actually firing and etc. the gun. Note that neither /gun/magazine or /gun/ammunition are subclasses of /gun: /gun/magazine is a sublcass of /obj and /gun/ammunition is a subclass of /datum. While it may be confusing at first, and is an example of BYOND's muddling of the difference between inheritance and namespaces in its typepath system, it can result in far better organization of code. Below is one potential example of how we could utilize this namespace to implement a weapon.

gun/RPGLauncher
/*
* This implement a Rocket Propelled Grenade launcher.
*/


name = "SP4NK3R"

ammunition_type = /gun/ammunition/RocketPropelledGrenade
magazine_type = /gun/magazine/RPGMagazine

Fire(angle)
if(prob(5))
// The gun is kind of unreliable. There is a 5% chance that the gun
// will explode rather than fire an RPG.
src.Explode()
return 0

else
return ..()

proc
Explode()
// Do explosion stuff here.

gun/magazine/RPGMagazine

max_size = 2
ammunition = /gun/ammunition/RocketPropelledGrenade

gun/ammunition/RocketPropelledGrenade

damage = 50

// 85% stable.
stability = 0.85

Fire(angle)
// Fires a new rocket in the angle indicated.
new /bullet/rocket(angle, src.magazine.gun.holder.loc, damage, stability)


And, with this model would could extend the /gun/magazine/RPGMagazine class, to create subclasses that are special types of RPGs. For example, maybe there is a Napalm RPg or an Acid RPG. These would, presumably, still exist in the gun namespace to avoid any conflict outside it.

Summary

In conclusion, BYOND's typepath system is simply a highly-complex namespace system. The system can influence inheritance and such hierarchy, but this is only a default, overwriteable behavior, albeit one that simplifies things a great bit, but one that is not at all necessary. Sometimes, overwriting this behavior is useful, and even necessary, to write elegant, extensible, and easily-maintainable. Namespaces and inheritance are both powerful language features, even more powerful when used together, and while they can be confusing, they can also be one of the most powerful tools for defeating ambiguity and increasing clarity when designing code.

Beyond The Tutorial

As an aside, note that the typepath system makes it simple for their to be namespaces within other namespaces, allowing there to be a hierarchal system that breakdown differently at each level. This can be useful for extremely complex systems, where things are tied together and where collisions or ambiguity are a very conceivable and real possibility.

Namespaces are also very useful in libraries. They allow for the possibility of a number of different classes to be packaged under one namespace to avoid annoying and difficult-to-resolve collisions between a library and a user's own code. When a collision occurs, typically the only real solution is for a user to rewrite all of their own code to fix it, which is often more difficult than just implementing the library themselves! This makes namespaces a very useful tool in library development.
Good job, nice article.
The inheritance example is pretty big. I understand inheritance but I'm not sure what I should be looking at there. It might be better to have a single, concise example (ex: just one proc) and relate it more to game development (ex: make it about attacking, or something people will use in games).
Yeah, that's a good point. I wrote some of the tutorial at about 4:30 AM and I was tired and... not in the best of judgement. I'll change it a bit.
Hah, how about that! Never realized you could use parent_type in THAT way. Thanks! :)
Many people are hesitant to use parent_type or are even actively against it, but there's no reason that you have to use area, turf, obj, and mob as the only base atom types. If your game has items, make a type called "item" and set its parent type to "/obj". Many people would just deal with referring to items with types like /obj/item/weapon/sword when they could easily simplify things and make it /weapon/sword instead.
Popisfizzy, keep up the good work. Both of your tutorials have been a good read, the first one gave a different approach to locations and its good to have those kind of thoughts available. As for this name space never thought to use parent_type like this, thanks!
It will be useful for organisation.
Keep up the good tutorials they help people in various ways even if its just a different approach which can lead to more doorways, thank you!
Know your effort is appreciated even if it isn't said :).
Falacy wrote:
You do realize that still uses obj as your base type?

I guess we should get rid of /area, /turf, /mob, and /obj. Since all they are is /atom/area, /atom/turf, /atom/movable/mob, and /atom/movable/obj. Then we can have type paths that look like /atom/movable/mob/goblin/kingGoblin, and we have to type out huge redundant type paths every time we want to refer to something.

Unless you can think of a reason why we shouldn't do this.

(edit: Though ultimately it should be /datum/atom/movable/mob)
Falacy wrote:
Well that wasn't long at all. When your code has more comments than actual code; you're doing it wrong.

I can agree with this to a certain extent. The "principle" behind commenting is to only comment when it's necessary, or to comment(if you're distributing a library) so that a developer can understand how a program works. I certainly don't see that in this article; Fizz practically hit all the main points. Sure, commenting on functions for this certainly isn't necessary, since it is obvious.
Drink() // It drinks when it eats.
..()


As for your definition of "bad examples", whether you like it or not, that's how inheritance works. My simple guess here is that you've never been exposed to or used an OO programming-language.
Falacy wrote:
Well that wasn't long at all. When your code has more comments than actual code; you're doing it wrong.

That was intentional. The idea is simple to get across, and thus doesn't need a lot of code. It needs more comments to clarify what is going on and the logic behind it than what it needs actual code. For future reference, there is no ideal, constant comment-to-code ratio. A piece of code needs as many comments as is necessary for the context it is being used in. In a tutorial, this is a lot.

There is so much unrelated and/or superfluous information, bad examples

Where? Please point it out piece-by-piece so I can shoot it down.

and the fact that this isn't even a good design concept in DM to begin with

Namespaces are useful in every object-oriented language, and they are present in a good god damned many. They are as useful in DM as they are in C++ or Java. Yes, they require an awkward syntax compared to other languages, but they are nonetheless present and intended to be used that way (or else parent_type would not be writeable).

I know it tweaks your nipples that you don't understand the use of namespaces, but they are useful nonetheless.

You do realize that still uses obj as your base atom type?

He clearly does, because that's how inheritance works. I'm pretty sure Forum_account understands this more than you do.

Because referring to it as an item and object gets the point across that it is indeed an item and object, not just some magical weapon type that you have to assume is inheriting who knows what from who knows where.

It is unnecessary to know that a /weapon is an /obj, just like it's not useful to know if a particular implementation of a stack or queue is inherited from a deque or is used as a wrapper around a deque. One of the basic principles of object-oriented programming is that it is not necessary to understand how a class works internally to use it, so long as you can use its interface. This includes what it is inherited from.

One of your biggest problems with all this is that you've never used a 'more typical' object-oriented language, where namespaces and inheritance work far more differently. DM is the only languages I know of, quite possibly the only language in existence, where the normal method of inheritance gives you information about what a subclass' superclass is. Hell, most languages have no way of doing it except possibly by assigning a subclass to a variable declared as a superclass, and that's wonky and very error-prone. You just have had the training wheels, along with a lot of other people, and now that someone has suggested removing them for someone else you're flipping out that that's not allowed.
I don't know about you, Falacy, but this certainly emphasizes more "code-readability" than what your standard approach is; everything is organized, and you can easily see where the inheritance is occurring.

Grenade
parent_type = /obj

var
maximum_dmg // The maximum damage at the center of a explosion.
minimum_dmg // The minimum damage at the end of a explosion.
radius // The radius of the explosion.

// Let's define the type of grenade here.
M67
// Assign all the properties for the M67 grenade-type we inherited from the Grenade.
maximum_dmg = 25
minimum_dmg = 17
radius = 7

Gun
parent_type = /obj

var
damage // How much damage the weapon inflicts.
spread // Functionality for spreading bullets.
speed // How fast the gun shoots bullets. (i.e, fire time)

MP5
damage = 1.4
spread = 1.5
speed = 0.20