ID:302547
 
Apparently, the unacceptable practice of using parent_type to organize code has been spreading around BYOND. Most likely due to Forum_account's use of it in his libraries. This needs to be ended, before it gets any further out of hand.

First, lets cover the basic concept, then an overview of its pros and cons.
Simply put, the parent_type variable can be set (at compile time only) to change a type:
obj/MyObject
parent_type=/mob
//Your /obj is now a /mob instead!
//"WTF?" is indeed the correct response here.
Pros:
+ Alternative (though not superior) organizational options
+ Save insignificant amounts of time when typing paths
Cons:
- Broken standards
- General confusion
- Less informative code

Lets look at some examples of the confusion this usage would cause:
obj/MyObject
parent_type=/mob

mob/verb/Find_Objs()
new/obj/MyObject(src.loc)
for(var/obj/O in world) world<<O
Even though this object is typed as /obj/MyObject, a loop trough all /objs in the world would not find it, because it now exists as a /mob. However, if we were to search for its specific path for(var/obj/MyObject/O in world) then it would still be found. We could also find it by looking for /mobs for(var/mob/M in world).

obj/MyObject
parent_type=/mob

mob/verb/Variable_Confusion()
var/obj/MyObject/O=new
world<<O.key
Even though our "O" variable is typed as an /obj, it will not be able to reference /obj specific variables. Instead, it would be able to reference the /mob's "key" variable. This becomes a much larger, more confusing issue the further you get into the production of your game, as you will have countless variables and/or procedures defined specifically for one type.

Another example, using top level /datums:
mob/verb/Null_Icons()
for(var/SomeType/S in world) S.icon=null
Using proper standards, /SomeType here should be considered a top level /datum. This causes several fundamental problems that a competent developer may be able to diagnose and/or resolve; top level /datums, like /SomeType, wouldn't exist "in world", and they don't have an icon variable (which even the compiler would complain about).
These are the correct standards that should be understood across BYOND. However, if we want to pointlessly break these standards, debilitating the common understanding of our code, we could use parent_type for a type conversion.
SomeType
parent_type=/obj
What would this accomplish? Well, it would make our for() loop work, by making our code over complicated and confusing in the process. Lets look at the correct way to handle such a setup:
obj/SomeType

mob/verb/Null_Icons()
for(var/obj/SomeType/S in world) S.icon=null
As you can see here, the most advantageous feature of using parent_type in this way is saving 4 keystrokes; /obj/SomeType vs /SomeType. Alternatively, you save a line when defining the type correctly, and your code becomes generally more informative and consistent with the commonly understood BYOND standards.

Lets throw in another example, based on a recent "tutorial":
gun
//vars & procs
parent_type=/obj
magazine
parent_type=/obj
ammunition
parent_type=/datum
Assuming proper formatting standards were being used, you would know that /gun/magazine and /gun/ammunition would have access to /gun class vars & procs. However, since parent_type is being used to change them to a base /obj and /datum, this actually wouldn't be the case. This example suffers from the same issues that I explained earlier, in the /obj to /mob example.

Moral of the story? Using parent_type in this way provides no real benefits, it makes your code less understandable, and it is never necessary. Don't use it!
The art of how to not use parent_type wrong. It's perfectly fine to use it when you know what you're doing.
In response to Kaiochao
Kaiochao wrote:
The art of how to not use parent_type wrong. It's perfectly fine to use it when you know what you're doing.

Its use is always wrong, but feel free to provide an example otherwise, if you can think of one.
It's a code organization method, and as such falls under the umbrella of personal preference. Personally, I've found it useful for preventing naming conflicts in libraries and for creating nested types. I've never used it to re-type a /obj as a /mob, or vice-versa; that's just silly.
In response to DarkCampainger
DarkCampainger wrote:
It's a code organization method, and as such falls under the umbrella of personal preference.
Personal preference is one thing, if it doesn't break the common understanding of how code works, but when your types function in a different convention than the norm, it becomes a bigger issue.

Personally, I've found it useful for preventing naming conflicts in libraries and for creating nested types.
Though it could help prevent naming conflicts, this leaves your library more open to user based errors. Your /SuperLibrary type may be unique, but it would still fall under an /obj (or whatever), which that a user could be using in some unexpected way without even realizing that your library involves them.

I've never used it to re-type a /obj as a /mob, or vice-versa; that's just silly.
The /mob to /obj examples were extreme to get the point across. However, retyping one type of /obj to another would suffer from the same confusing inheritance access issues.
Falacy's fallacies. Adorable.

There are numerous examples where the use of parent_type is favorable over the alternative you're giving (which is practically nothing).

The first would be in cases where you have large sprawling class trees. Any variable references to objects of a class in such a class tree can end up making your code very painful to read.

For example, this probably isn't uncommon:
obj
item
equipment
weapon
sword


This is correct according to you; however, if you need to do a for each loop through a container of these objects...
proc/Example(var/list/container)
for(var/obj/item/equipment/weapon/sword/S in container)
//do stuff here

This isn't clean in the least, and is ugly to read.


On the other hand, if you use parent_type...
obj
item
equipment

weapon
parent_type = /obj/item/equipment

sword
parent_type = /weapon

proc/Example(var/list/container)
for(var/sword/S in container)
//do stuff here

Take note that there isn't any line in the code in my example that is excessively long or difficult to read.

The big point here is that easy-to-read code is also easy to find mistakes in. Small things like typos or operation order errors are significantly easier to find if the code is not ugly. This is the reason why many experienced developers have been using parent_type.
It's of no consequence, really, that you haven't figured it out though.

The second case in which parent_type has no acceptable alternative would be IainPeregrine's style of multiple inheritance in DM. Iain did write an article about it but I can't find it at this time. I will post on this later once I find the article.

Additionally, there's already been previous articles written about parent_type. You're wasting forum space by making a new thread.
In response to D4RK3 54B3R
D4RK3 54B3R wrote:
Falacy's fallacies. Adorable.
Yay for BYOND trolls never being banned

There are numerous examples where the use of parent_type is favorable over the alternative you're giving (which is practically nothing).
When referring to the "practically nothing alternative" that I provided, you mean the standard BYOND formats that every competent developer has used for years now?

The first would be in cases where you have large sprawling class trees.
For example, this probably isn't uncommon:
obj/item/equipment/weapon
sword
Well, lets fix that layout for starters.

This isn't clean in the least, and is ugly to read.
It is not "ugly to read", it may be long, but it is a direct and informative path to the actual base of the object, instead of just referring to a magical /Sword, which nobody besides yourself (if even yourself) will know whats going on.

On the other hand, if you use parent_type...
Take note that there isn't any line in the code in my example that is excessively long or difficult to read.
The parent_type lines are, and though you have cut down on the length in your for(), you have also made is worthlessly uninformative to anyone without a full insiders knowledge of your source. When BYOND noob #30,000 comes asking for help on the BYOND forums, and wants to know why his /Sword won't equip, good luck helping with that.

The big point here is that easy-to-read code is also easy to find mistakes in. Small things like typos or operation order errors are significantly easier to find if the code is not ugly. This is the reason why many experienced developers have been using parent_type.
It's of no consequence, really, that you haven't figured it out though.
If anything, that makes you sound like the novices, and I like how you noobtards keep accusing me of not understanding parent_type, even though I have been the one explaining it to you.

The second case in which parent_type has no acceptable alternative would be IainPeregrine's style of multiple inheritance in DM. Iain did write an article about it but I can't find it at this time. I will post on this later once I find the article.
If multiple inheritance was possible through parent_type, then that may be an acceptable use. However, it doesn't seem to accomplish that, and you can't find the imaginary article about it. It certainly doesn't provide that functionality through basic usage.

Additionally, there's already been previous articles written about parent_type. You're wasting forum space by making a new thread.
Those posts are in support of its use, if anything, they are the ones that should be deleted.
What appears to be your key argument revolves around people not using parent_type in the manner that it was intended. Anyone that actually understands parent_type can agree that the following is NOT a proper usage:

obj/myObj
parent_type = /mob


Said usage will get you beaten over the head with a stick. Now, let's go on to more proper usage:

Weapon
parent_type = /obj

Sword
Gun
Hammer


This is a valid usage of parent_type. Think of it as private inheritance (the default for classes) in C++, except without the actual formalities of private inheritance provided by C++. You choose not to expose the base implementation of the /Weapon. This allows the base implementation to be changed with minimal changes outside of the /Weapon datum because the code outside of the /Weapon datum only knows about what exactly is inside of the /Weapon datum.

Now let's break down into your two arguments against this. Refer to the following snippet:

mob/Login()
..()
new /Weapon/Sword(src)
new /Weapon/Gun(src)
for(var/obj/O in world)
world << O.name


Two arguments reside in this one snippet. The first obvious one is that the loop will pick up the Sword and Gun, both of type /Weapon, and output their names. This is a valid argument, but do you have a practical example of where this is a problem?

The second argument is that it's not clear what having a reference to a /Weapon actually entails, var-wise. This part goes back to the point of not exposing the base implementation. The idea is that you are creating your own type- the /Weapon. How it interacts with the base class is of no importance to anything outside of the /Weapon. As such, you create your own accessor procs and use those to sort of define what properties the /Weapon actually has.

For the sake of argument, we'll go back to our previous /Weapon class and make a few clarifications:

Weapon
parent_type = /obj
proc/getName() return name
proc/getIcon() return icon
proc/setIcon(icon/i) icon = i

Sword
Gun
Hammer


Alright, so from the perspective of a developer outside of the /Weapon datum, we've got two properties: 'icon' and 'name'. How these are implemented doesn't actually matter because we've chosen to expose them through getName, getIcon, and setIcon. In reality we'd have more properties and whatnot, but that's besides the point. So now, we've got some other code somewhere else:

var/Weapon/Sword/S = new
world << s.getName()
s.setIcon(/* My Icon */)


This is fine and dandy. For the sake of argument, let's do some admittedly stupid modifications to the /Weapon:

Weapon
parent_type = /mob
proc/getName() return key
proc/getIcon() return icon
proc/setIcon(icon/i) icon = i

Sword
Gun
Hammer


So now, a /Weapon is a /mob and the 'name' is actually the key. This doesn't affect anything outside of the /Weapon because we properly encapsulated these properties and chose not to make everything else dependent upon a specific implementation of /Weapon.

You're right in saying that most people won't use parent_type properly because its usage is largely validated by the design that you're employing. Claiming that EVERYONE should avoid it ALL of the time, however, is technically inaccurate and just plain not right. There are many valid uses of it if you've think it through.
How is that beneficial compared to just using a standard /obj/Weapon tree?

You mention creating procs so that external users can easily access your "custom" class, but this is somewhat counter intuitive. Also, if you were making each of these things their own class /obj /Items /Weapons /Sword, instead of just /obj/Items/Weapons/Sword as a single path, backtracking to find possible issues would be tedious if you didn't know the parent_type inheritance.

Also, you say it won't have to worry about outside effects, but if a user does some global object alteration, your "encapsulated" weapons could be effected. Its variables aren't actually protected from outside influence just because you created procs to access them. Any variables or procedures being created for /obj would also get attached to your parent_type=/obj objects
Falacy wrote:
How is that beneficial compared to just using a standard /obj/Weapon tree?

By dropping the type-path you allow for that base implementation to be changed without having to go through and change every instance of /obj/Weapon to /mob/Weapon or /obj/droppable/Weapon or however you choose to change the base implementation. The previously mentioned shorter type-paths argument is also still there, but that's just a nice side effect.

Also, you say it won't have to worry about outside effects, but if a user does some global object alteration, your "encapsulated" weapons could be effected.

This is another thing that you have to take into consideration when employing parent_type. If the defaults for the base implementation's values will affect your class, then you supply reasonable defaults either at compile-time or through New() parameters.

Falacy wrote:
You mention creating procs so that external users can easily access your "custom" class, but this is somewhat counter intuitive.

This is incredibly common practice outside of BYOND, though.

Also, if you were making each of these things their own class /obj /Items /Weapons /Sword, instead of just /obj/Items/Weapons/Sword as a single path, backtracking to find possible issues would be tedious if you didn't know the parent_type inheritance.

You don't parent_type everything, though, just the base. Note that I parent_type'd the /Weapon, but /Weapon/Sword and /Weapon/Hammer are still visible children. You don't bastardize the type-path completely, just to the point where you have reasonable separation from the mostly irrelevant (to the end-developer) base implementation.

[...]

Any variables or procedures being created for /obj would also attached to your parent_type=/obj objects

This is true. BYOND doesn't provide the access rights that makes this design flawless, the best you can do is rely on naming conventions and developer conformity to obscure away these base variables.
In response to Audeuro
Audeuro wrote:
This is incredibly common practice outside of BYOND, though.
It is, but it is usually done for encapsulation. You could create a private health variable, and then a public function to modify that variable, for example. This provides an additional layer of security and error checking.

the best you can do is rely on naming conventions and developer conformity to obscure away these base variables.
Rely on them to not use parent_type =P

I have edited an additional example into the original post, since multiple people seemed to think that the /obj to /mob parent_type conversion was absurd.
In response to Falacy
Falacy wrote:
It is, but it is usually done for encapsulation. You could create a private health variable, and then a public function to modify that variable, for example. This provides an additional layer of security and error checking.

Indeed, and it's used in almost the same manner here. The key differences are that we don't have access rights to stop them from modifying the variables directly and that we'll also use them to provide access to the super class's variables that we allow them to use. Th former is why we use non-standard naming conventions (_myVar) to ward off people from trying to modify the variables directly.

Separating types where this occurs also helps with this because if you don't see /obj repeatedly in the type-path then you're not going to be thinking immediately about how to modify that /obj. You'll be thinking instead, "What can I do with this /Weapon?"
Personally, I define almost everything via parent_type for the sake of habit and organization. Makes everything easier to navigate for me.
There are situations where using parent_type makes code more confusing. There are also situations where using for loops makes code more confusing. I wouldn't make it a rule to always avoid those things just because they could be confusing.

The key to using parent_type is to use it when to object's full type path isn't important. If the full type path was always important, you'd be demanding that we call it /datum/atom/movable/obj instead of just /obj. Simply calling it /obj hides its true path, but you use it in many of your examples - the full path can't be too important to you. Most of the time, it isn't.

Here's an example of how you can use parent_type intelligently. In a project you use mobs for many things - players, enemies, projectiles, special effects (visual things, like explosions). Because so many things are of the /mob type, you'd never write this code:

for(var/mob/m in container)

It doesn't make sense to iterate over all players, enemies, projectiles, and special effects at the same time. You'd almost always use a more specific type, like /mob/projectile. Because of that, it's hardly ever important that the projectile and player types both inherit from /mob - it's about as important as knowing that they inherit from /atom/movable. So, it makes fine sense to set parent_type to make /projectile behave like a root-level type.
If this
sometype
parent_type = /dbchjs/fdvs/fdvf/fvfdv/ewrwetf/wefcv/fdv/wcewdcsd

is well established, why would I bother having /dbchjs/fdvs/fdvf/fvfdv/ewrwetf/wefcv/fdv/wcewdcsd/sometype? I don't think you can convince me that we should be using /datum/atom/movable/obj instead of /obj.

There is nothing wrong with using parent_type. However, the rule of thumb is to have parent_type only at top level paths.

This
obj/MyObject
parent_type=/mob

is unreasonable.

In response to Forum_account
Forum_account wrote:
There are situations where using parent_type makes code more confusing. There are also situations where using for loops makes code more confusing. I wouldn't make it a rule to always avoid those things just because they could be confusing.
You tried to make this argument before, it is nonsense. "Code makes your code confusing". No, it really doesn't, not when done properly. If adding a for() loop to your code makes it confusing, then you are either doing it wrong, or don't understand the basic concepts. There is also no viable alternative to a for() loop, unless you're suggesting manually looping through a list with an index counter instead, or just never looping at all? Types can be properly and intuitively assigned without using parent_type.

The key to using parent_type is to use it when to object's full type path isn't important.
The type is always important, so you should never use parent_type. Glad we can all agree.

If the full type path was always important, you'd be demanding that we call it /datum/atom/movable/obj instead of just /obj. Simply calling it /obj hides its true path, but you use it in many of your examples - the full path can't be too important to you. Most of the time, it isn't.
It is important to know that /mobs inherit from /movable, and can therefore Move(). It wouldn't hurt (probably would have been a good thing) if BYOND had designed their primary types to be full paths from the beginning, not a feasible change at this point. BYOND is severely lacking when it comes to internal conventions and standards, as well as providing a general understanding of things to its users, such paths would have improved understandability. However, since the core atom types are a commonly understood BYOND standard, it is more acceptable to use their shortcuts.

Here's an example of how you can use parent_type intelligently.
Your example of a loop being too confusing because of /mob/Projectile is ridiculous, and if anything, /Projectile is an even more confusing prospect.

I'm not sure why you would be using /mobs for projectiles, but that just goes to prove how important not using parent_type is. If you're using some library that provides you with /Projectiles, that are some reason parent_typed to /mob, most people would probably never realize this. Combat and general interaction is usually designed around mobs, something that most people would probably be befuddled by when their players can attack /Projectiles and then have a chat with them (or whatever common mob interactions may be defined).

it's about as important as knowing that they inherit from /atom/movable. So, it makes fine sense to set parent_type to make /projectile behave like a root-level type.
As I mentioned, it is pretty important to know that your projectiles can move.
Falacy wrote:
The type is always important, so you should never use parent_type. Glad we can all agree.

Then how come you don't follow your own... advice[?]?

You're "basically" using parent_types most of the time when you code. It'd be a safe bet to say that most, if not all, of your publicly available code never covers the entire path to any given object literally/explicitly.

I'm quite sure most of us, including you, type
for( var/obj/a )

instead of,
for( var/atom/movable/obj/a )


Which is exactly one of the benefits that parent_type provides, which is, once again, "hiding" the type path of a class/object.

Falacy wrote:
As I mentioned, it is pretty important to know that your projectiles can move.

It's pretty important to know that your objs and mobs can move, yet you don't seem to emphasize that practice in your own code.
I'm not even sure that accusatory nonsense even deserves a response, but I'll give you one anyway.

For starters, using the full path of a built in type won't even naturally compile. Your for( var/atom/movable/obj/a ), for example (which isn't even the full path, because you left off /datum).

As I mentioned in a previous post, these types are standard to BYOND, and therefor generally understood, so their use is more acceptable. However, if BYOND had designed their types to require a full path, it probably would have been beneficial for the language as a whole.

Also, I never use the parent_type variable to foolishly change the type of a subtype that I have just created.
In response to Falacy
Actually, you could if you use the art of changing parent_type
datum/atom/movable/obj // how about making a class with a path that is "intuitive"?
parent_type = /obj // breaking "broken standards" = fix?

Then again, I still prefer the plain old /obj.

Anyhow, it's just like choosing between with straw or no straw for coke.. or pepsi.. err, whatever. The thing is that the liquid shouldn't go through the nose. There's a proper way of drinking with or without straw.
In response to D4RK3 54B3R
D4RK3 54B3R wrote:
On the other hand, if you use parent_type...
> obj
> item
> equipment
>
> weapon
> parent_type = /obj/item/equipment
>
> sword
> parent_type = /weapon
>
> proc/Example(var/list/container)
> for(var/sword/S in container)
> //do stuff here
>


This is actually not a very good example. The use of parent_type is not to arbitrarily shorten paths, but emphasize the independence of a class from its superclass. How '/sword' behaves is not immediately apparent; is it a piece in a puzzle? a decoration? a piece of equipment? maybe even some kind of flying sword monster? Note that while it's cumbersome to type for(var/obj/item/equipment/weapon/sword/S in [list]), the purpose behind that loop is a lot clearer than the purpose behind for(var/sword/S in [list]), as is the behavior of the 'S' we're looking for. The second version doesn't tell me all I want to know about this S, the first tells me that and more.

The problem with a loop such as
for(var/obj/item/equipment/weapon/sword/S in container)


Is that it tells me slightly more than I want to know, and the purpose of the loop becomes ambiguous. The more unnecessary information a snippet feeds me, the less its function is clear. In here I'd say the only really useless piece of information is that the sword is an '/obj'; the fact it's an /obj is completely irrelevant. What I want to know is that the sword is an item and an equipable weapon. So this loop should really look like so:

for(var/item/equipment/weapon/sword/S in container