In an object-oriented universe, everything revolves around the use of objects holding everything. Methods, variables, etc.. To modify data or call a method, you would do so from that object or interface. OOP works off of the "three pillars" which are:
- Inheritence
- Encapsulation
- Polymorphism
Inheritance brings the "is-a" relationship between objects where an object can be a derived type of a parent object.
Encapsulation ensures that entities are contained in a "bubble" or scope where there's a certain restriction on how or where you can access fields in an object.
Polymorphism allows the methods of an object to be overridden (rewritten) and overloaded (allowing the same method with multiple parameter data types), while also allowing the parent method to be called, optionally.
These three things created a structure of programming that allowed large groups to work in collaboration with the ease of the object notation simulating real-world scenarios. However, this same structure enforces restrictions and loss of flexibility to the programmer. Object-oriented languages are very strict in the regard that objects must exist for their fields to be invoked. An exception to this is the "static" attribute which creates a single "instance" of an object type which may be invoked just about anywhere, and referred to its type instead of an instance name (Class.Method, not ClassInstance.Method).
What I want to talk about is not approaching programming situations with objects in mind. Instead, let's say that objects are more abstract to the user?
Check this little example of DM code below:
item
var/name
An item at this point merely has a name, which is uninitialized. How would we create a new item? Would we use a method? Well, we could create a function which creates a new item:
proc/CreateItem(N="No Name")
var/item/i = new()
i.name = N
return i
What this proc does is give us a freshly baked item instance. CreateItem() is a global function that is accessible anywhere. What if we wanted to make a function to change its name?
proc/SetItemName(item/I, N="No Name")
I.name = N
It might seem redundant to make a function that only sets a single variable, but for the sake of consistency, we want to try and keep the object-oriented functionality as vague as we can get it. So far, making and setting the name of an item in front-end code would look like this:
var/d = CreateItem("Some Item")
SetItemName(d, "Another Item Name")
the /item type is nowhere to be seen in this example. At this point, an Item is an abstract class to the user. They know by its function names that it indeed has a name value. To grab that name value we can add:
proc/GetItemName(item/I)
return I.name
Our current function set (compared to OO):
// Imperative Notation
CreateItem(text)
SetItemName(item, text)
GetItemName(item)
// Object-Oriented Notation (exclusing methods SetName() and GetName())
item
var/name
New(T)
..()
name = T
return src
// Example of creating and setting the name in Imperative Notation
var/d = CreateItem()
SetItemName(d,"Flower")
// Example of creating and setting the name in Object-Oriented Notation
var/item/d = new()
d.name = "Flower"
It's up to you how you like your code. I personally dislike many OO practices and I find it really confusing when using stuff like inheritance (with large object trees) ("is-a"). I would generally prefer to use composition ("has-a") instead.
I think that with the direction you are going in, what you will need is a function that can seamlessly convert any given object instance into an instance of another type, without losing any information. For every object instance, there would need to be a second datum instance that serves to represent the interface for that particular object. This means that whatever happens to the datum should be reflected by the object and vice versa.
By converting each object into a kind of binary instance system, the different types that exist will become abstract, and serve only to produce a certain manifestation of the object when needed.
Similarly, object variables might also need to have a binary system as well, since you can't copy over values for vars that don't exist in another type. In this system, each object would have 2 vars lists. There would be the built-in vars, and then there would be the far more flexible, virtual vars, which allows new vars to be created whenever they are needed. When an object is converted from one type to another, if the new type doesn't have a compile time var that corresponds to the original, then the value is added to the virtual vars list instead.
This is all just a theory, so I really don't know how well it would work in DM. It may be easy to describe, but actually implementing it would be challenging, since it's such a large, sweeping change that engulfs the whole structure of the language. This might also help set the stage for a powerful, interpreted scripting language, but that's another story.