Skip to content

. = ..(), or the art to write something simple in a complicated way

ShiftyRail edited this page Feb 11, 2019 · 5 revisions

Introduction

. = ..() is a common structure in the codebase. It uses an esoteric notation to do something relatively common, and simple, that we will try to explain below.

If you just want to know what the heck this means, and you are comfortable with (very) elementary object-oriented programming notions and ideas, just skip to the end (To remember).

Special byond prototypes

., the default return value

. is a built-in variable which exists in all byond procs. It is the default value being returned, and its initial value is null.

Unrigorously, you can view it as something being "added" to your procs when you hit compile. For instance :

/proc/thing()
    . = TRUE
    world.log << "Welcome byond!"

Gets compiled as :

/proc/thing()
    var/. = null
    . = TRUE
    world.log << "Welcome byond!"
    return .

It should be noted that return, return . or nothing at all at the end of a proc are strictly equivalent and will yield the same result.

..(), calling the parent

..() is, in a proc, the way to call the parent's version of this proc. Even for a proc that wasn't defined previously, ..() is valid syntactically (it'll just do nothing at all).

Example :

/datum/proc/thing()
     world.log << "Welcome byond!"
/datum/child/thing()
     world.log << "I'm the child datum!"
     ..() // Will display, "Welcome byond!"

..() implicitly passes any arguments the "main" proc being called had. Essentially,

/datum/child/thing(var/arg1, var/arg2)
     // Something
     ..(arg1, arg2)

and

/datum/child/thing(var/arg1, var/arg2)
     // Something
     ..()

Are equivalent. It is preferred not to pass the arguments unless you want to change them.

BYOND WARNINGS

Below are two very common errors/bugs due to byond strange syntax and structures. Try not to do them.

  1. Overriding a proc that already exists

Byond allows you to override a proc in the same "type", not just for the children types. You can also override said proc in any file. The proc the further down in the file tree is the one that actually gets compiled.

Essentially :

/datum/proc/getTotalHealth()
     return health
// Further away in the codebase...
/datum/getTotalHealth()
     return (..() + extra_health)

Is completely valid syntax, and the later proc will be the one that gets compiled. You should avoid this coding practice, as it can makes things harder to debug and understand what's going on in the code. In such the above example, ..() is simply /datum/proc/getTotalHealth()

  1. Named arguments

This situation is better explained with a concrete example. Let's assume we have a proc defined for a religion, with some subtypes :

/datum/religion/convert(var/mob/living/preacher, var/mob/living/subject)

/datum/religion/retard/convert(var/mob/living/preacher, var/mob/living/subject)

Then, we code a functionality on the main proc, adding a new arg, but we forget to update the child, which makes it look like this :

/datum/religion/convert(var/mob/living/preacher, var/mob/living/subject, var/can_renounce = TRUE)
        // Do something
/datum/religion/retard/convert(var/mob/living/preacher, var/mob/living/subject)
	. = ..()
	if (subject)
		subject.adjustBrainLoss(100) // Welcome to the club

Then, we'll try to code something calling convert like this : convert(mob_1, mob_2, can_renounce = TRUE)

This will compile correctly, however, because in /datum/religion/retard/convert we omitted the can_renounce variable, this will runtime and BYOND will not understand what is happening (and will produce a rather unhelpful error message).

You must remember that if you try to use named arguments (like can_renounce here), you have to define those in all children of the procs. However, if you do not use named arguments, extra args are kept.

To remember

Now that we saw what . and ..(), we have to combine both.

. is the default value being returned.

..() calls the parent.

. = ..() calls the parent, store the value returned, and at the end of the proc, this value will be what's returned.

Bonus

A rarer, but no less esoteric, byond construct is ..... (yes, 5 (five) dots). It returns the "type" of the proc being called, which can then be passed around (for instance, to call it manually). For instance :

/datum/x/y/proc/test()
      world.log << "[.....]" // /datum/x/y/proc/test
/datum/x/y/z/test()
      world.log << "[.....]" // /datum/x/y/z/test