lisdude/toaststunt

;verb_code(here, "verb_name") doesn't care about inheritance

Closed this issue · 8 comments

anubi commented

The docs say this is by design (and follows for verb_info(), and verbs()), but I have to ask why! Inevitably, lambdamoo stuff is going to have object inheritance. Do I really need to write my own util function to while loop up the tree? Yes, I can do this, but this is going to be a lot less efficient than if it's done inside the underlying C++? Is there any way we could have a flag added to these functions, which cause them to check if a verb exists at all for the given object?

it makes sense to me. you find the verb on the object it was defined on because inheritance hierarchies can and do change dynamically.

anubi commented

I guess, it's just, I"m trying to architect things in moocode in an organizable way.

Suppose I derive a thing called a "weapon" ($weapon) from thing. There are six children of "weapon", a gun, a sword, an axe, a bow, and a spear, let's say. I create a "fight" verb on "weapon", so all the weapons inherit "fight".

If my player picks up a weapon, they could fight if I just check if isa(x,$weapon). But that's only if we keep to simple terms.

Even in this simple scenario though, there would be situations where you might wish to see if a parent of $rusty_sword (a child of sword which has durability for instance) has the verb "fight" if for some reason a $weapon or a $sword or a $book all had "fight" implemented in them. Yes, you could make them all inherit from "generic fightable thing" but that only works for up to one interface. I'm sure you can imagine other scenarios, where things are less defined by what they are and more defined by their common shared use/interface.

But instead, you now have to walk up all these objects and check them for the existence of the verb, which I'd think would eventually cause performance issues? Maybe not. Maybe it won't be a big deal, but if you want an abstracted code base where you're constantly doing this, it could add up over time. If you don't do the checking like i'm describing, you will need a bunch of concrete references in your code -- "is it a $weapon, a $book, a $shoe" and every time you add a new "thing" which can fight, you need to go edit that code, which is untentable in moo since you don't even really have an IDE for quick refactoring.

It gets even more complicated if you want to use "component" based design instead of inheritance.

Let's say I want to make a new kind of weapon, a "gunblade". Guns have the "reload" action and the "fire" action. So it's a gun AND a blade. It would be a mess to make my "gunblade" derive from either gun or from blade, because I want to inherit from both.

So the architecture I would prefer is to just have one thing called a $weapon and have no abstract children under it. Concrete children of $weapon get constructed (for players to pick up and use) and they have different properties appended to them, $weapon.action1, $weapon.action2. I would like to see if action1 defines a verb "Fight" which could get messy if action1's object is derived from multiple levels of inheritance.

If this sounds entirely insane to you, let me know what's crazy about it.

The only reason I'm even being annoying about this is just 'cause I'm used to hellcore's codebase having the native ability to tell you if an object has a verb (period). You can certainly get by without it, but it does introduce boilerplate I wish wasn't there. So if you don't wanna add it, oh well.

i'd implement a generic "fight" verb on $weapon, and redefine the verb appropriately in each child ($book, $sword, etc.). that approach should work all the way to $gunblade. generally in OO, introspecting the inheritance hierarchy as part of basic behaviors is an anti-pattern ("am i a this, then do that, or if i am a something else, then do..."). that's what polymorphism is supposed to give you.

the mix-and-match style of composition is trickier, but you should still be able to get quite far with multiple-inheritance. if you really need to quickly see if an object defines a verb you can use the respond_to builtin.

Todd said it all! Even with a flag, this could become dreadfully confusing, especially when the concept is transferred to a full-on list of verbs. What if there are multiple verbs defined along the hierarchy? Should verbs(OBJ) list duplicate verbs, break all tradition and return object-verb tuples?

For what it's worth, here is a snippet of code that I use on ChatMUD in $object_utils:has_callable_verb to leverage respond_to, while still maintaining API backwards compatibility. You can use this wherever -- I merely updated the existing LambdaCore verb.

return `{respond_to(object, verbname)[1]} ! E_TYPE => 0';

If the first item in the returned list is 'this', you know the verb is defined (and executable) on the target object. Otherwise, it's on an object somewhere further up the chain.

anubi commented

Oh wow. respond_to is exactly what I need! Not sure how I didn't find it. Thank you so much.

Though it's unclear to me how MO works then? I'll have to read up on it. I was under the impression if I make root > cat > tiger and a root > lizard > dragon and then I decide I want a "chimera" I have to either do root > monster > chimera (where a chimera is a dragon-cat) and copy-paste code from tiger and dragon, or I can go root > cat > tiger > chimera and just copy the dragon verbs I want into it (neither approach is ideal or good for longterm design). I know I'm being vague and you might think these things are all similar enough to live under a more simplified tree, but what I'm planning to do is a little more than that, which I think will necessitate more abstraction.

Is there actually a way to make a tiger-dragon? I didn't think you could have two delineated parents.

You can (theoretically) achieve your intended results by taking advantage of multiple inheritance. This feature is available in ToastStunt, but from what I've seen/heard, it has the potential to become very messy. I could be wrong though, as I haven't bothered to touch it in years. You may wish to simply derive races from a generic game/race player, Then you can simply create different descendant generics from that to act as races.

You could also just define all verbs (regardless of race) on 1 player parent. You can then add code to race-specific verbs that checks a player's race (could be a string reference or something along those lines) and responds accordingly. This has the potential to get messy, but it would also make your ideas of cross-races possible without having to worry about creating potentially large parent/descendant trees.

In any case, you're at risk of over optimizing. The beauty of MOO is that it's flexible to shape and customize, but that comes at the price of more overhead. Regardless, there are multiple ways to implement your desired functionality. All have their advantages and disadvantages. You can also use descendants of $feature to make available certain command sets to the player based on their race if you so choose, but that approach also comes with its set of issues and error-checking requirements. I personally think that Tod's suggestion makes the most sense, and is a good compromise between flexibility and management/maintenance.

If I'm recalling correctly, overlaps are handled by hierarchical precedence, but as soon as trees divert (and potentially convert) that may become very difficult to follow.. That's not MOO-specific imho..

I always like to use multiple inheritance in MOO like I use traits in PHP.. It can work very well for individual packages of functionality, but parents should ideally be 'self-containing' and not inheriting anything else (or at least not interfering with other branches). it may get really messy when trees are overlapping..

Again, that's not MOO specific, just something that you have to deal with in OO. This is where software architecture kicks in

anubi commented

Thanks so much for your input! It helps a lot.