Ioke is a very object oriented language. The amount of concepts in it is also quite small. I’ve tried to factor the implementation into smaller pieces, which are then composed into the full object hierarchy. One of the side effects of this is that it is possible to make objects that can’t be handled generically, since they don’t have all those things you expect objects to have. There is no requirement on Ioke objects to actually have a core set of methods. Things can be totally blank. Of course, a completely blank object is not very useful in itself…
So what kind of things become problematic then? Well, lets first talk about blank slates. In Ioke it’s pretty simple to create one - you just create an object, and then call the method “removeAllMimics!”. What you have after that point is something that is totally empty (unless if you added cells to the object itself before hand).
Now say that you want to add back a mimic to that object. Or say you want to add a new cell. As it happens, “=” is just a method on the Base kind. To add a new mimic, you generally use either “mimic!” or “prependMimic!”. These are both methods on ReflectionBehavior. So none of these methods are available.
Another example is mixins. In Ioke, a Mixin is nothing other than a regular object, except that it doesn’t have Base or DefaultBehavior in its mimic chain. Instead, the root of all mixins is the kind Mixin. Now, Mixin actually defines a copy of “=” and a few other things from Base. But it doesn’t allow you to add new mimics to mixins for example. This is a bit annoying - but since you have access to “=” you can actually fix it, by coping the “mimic!” method from ReflectionBehavior.
There are other circumstances where you want to be able to handle any object whatsoever in a uniform manner, introspect on a few things and so on. That wasn’t possible.
I’ve been considering these problems for a while, and finally decided that the pragmatic way of solving it is to add some methods that can be used to take a part and modify objects from the outside. I call the namespace that gives this functionality Reflector. Now, remember that you shouldn’t use the Reflector unless you’re certain that your objects need to be handled in that specific way. It’s more verbose, and not as object oriented as the methods that exist on regular objects. But it does allow you to do certain things on all objects in the system.
The rules are simple - all methods on Reflector are based on other methods in Base or ReflectionBehavior. They have the same name, except that they all start with “other:”. They all take an initial argument that is the object to work on. So, say we have an object “bs” that is a blank slate. We want to add a cell to it. We can do that simply like this:
Reflector other:cell(bs, :foo) = 42
Here we set the cell “foo” on the blank slated object to the value 42.
The problem of adding a new mimic to a mixin is easily solved too. Say we have an object ExtraEnum that we want to mix in to Enumerable. We can do it like this:
Reflector other:mimic!(Mixins Enumerable, ExtraEnum)
And so on. You can see all the methods available on Reflector in the doks.
As mentioned, the Reflector is a pretty specialized tool, but it’s totally indispensable when you need it. I will pretty soon sit down and rewrite DokGen to use it, since DokGen is a typical example of something that really need to be able to handle all objects in a generic way.