I'm thinking about tree structures, and I feel that I don't like them. It's like when you have a shop, then you try to put all products to tree-like catalog, and then you need to place one product to multiple categories, now you have multiple routing, bla-bla. I don't feel like everything in the world could be put to a tree.

Instead, I like idea of tags. I would like to store everything with tags. With tags I could do much more. I can even simulate trees if I want. I want to have tag-based filesystem!

But hey - modern OOP paradigm with inheritance is based on tree. I want to see how it is when you don't have such basement.

Closest thing I found is mixins in some languages. Do you know what else is also about this ideas?

@Desolate With data it's not a problem, but I want a language, I want a tree-free environment, not affected by tree idea at all. I'm wondering if it's possible...
–
user16131Feb 6 '11 at 21:33

Ok, cool, then I'd agree with Peter's answer, but I'd also put in annotations as a language feature that lets you do tagging
–
Desolate PlanetFeb 6 '11 at 21:38

1

@kolupaev: Certain things are modelled well by trees, other things aren't. Inheritance has it's place, tags have other uses - take both where appropriate, everything else would be useless ideology ;)
–
DarioFeb 6 '11 at 21:56

9 Answers
9

That's not what tags represent. The categories in a shop don't represent is-a relationships, it's more of a "belongs to" or, in programming terms, "supports feature xy". Tags can change, they can be added and removed as you need it - base classes can't, as the object needs it! Objects don't depend on the tags you give them, but they depend on their base classes to make sense.

You can put some car in category "green cars" or "cars from the 1960's" - no problem, it doesn't care - but it can never cease to be a vehicle!

So as you see, inheritance trees and your tags model entirely different things. One is about the object itself, the other one on how we relate objects together.

True, there are cases where it makes sense for an object to have more than one base class, but these are rare and as supporting true multiple inheritance causes many other problems (that's why lots of OO languages disallow it), it's a reasonable way to model is-a hierarchies through trees.

On the other hand, your tags are represented through interfaces (for which implementing multiple ones is allowed in practically every OO langauge I know) - or better - through typeclasses, which can be added or shadowed as you need it.

I agree with you that inheritance is often overused in places where interfaces would fit better, but as both are in principle orthogonal concepts, both have their appropriate use-cases.

Edit:

I'm again stressing typeclasses or concepts as they're probably the answer you were searching for, though the question works on the false premise that tags are to replace inheritance.

Interfaces are good, but they don't add anything to objects, they are describing objects. Mixins, for example, would give specific abilities to class, behaviors, etc.
–
user16131Feb 6 '11 at 21:46

@kolupaev: Edited the answer. Take a look at type classes. Do you know .NET'S extension methods? Tags describe too, as typeclasses do. Adding functionality once you got that generic description is just a matter of syntactic sugar.
–
DarioFeb 6 '11 at 21:50

@Kolupaev - Interfaces are virtually analogous with Tags, they add categorization via the API contract that they enforce. The main point being that all objects will not respond in the same way when the same method is used on them.
–
SlomojoFeb 6 '11 at 21:52

I think the heart of your issue lies in the language's type system. In languages that use structural typing or duck typing, the type of the object is determined by the structure (or just part of the structure in duck typing) of the type instead of having to declare the type using inheritence. So all it takes to create an object of a certain type is to "tag" the object with a certain structure. This also avoids many of the issues with multiple inheritence.

Scala is a language with structural typing, and Ruby uses duck typing.

"When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck."

Roles in Perl6 come close. They specify a named (or "tagged") set of behaviors. Perl 5 has the incredible Moose, which implements the same concept. The are somewhat like java interfaces, but with implementation not just method definition. The role concept was based in part on smalltalk traits.

most dynamically typed language handle polymorphism separately from inheritance. not like your "tagging", but simply "if the object can do this, then it can be used here". If it doesn't, a run-time error is generated.

Google's Go language accomplishes a similar thing with interfaces. It's somewhat similar to the Java concept of Interfaces, but detached from inheritance: if an object definition has all the required methods, the compiler allows it to be used. In effect, interfaces are type 'specifications', that allow the compiler to strictly check at compile time if the substitution is acceptable without relying on a common ancestor.

Check out C++, it has multiple inheritance. Of course, whether this counts depends on your definition of "modern" ;-)

Its successors, Java and C# disallowed multiple inheritance for very good reasons, but you still can implement multiple interfaces in these. IMO interfaces (especially marker interfaces) are much closer to the concept of tags than (abstract or concrete) classes.

For me aspected-oriented programming was always the closest thing to tags in OOP. Aspects are essentially tags that infer some behavior upon a method/class, and one method can multiple aspects. Unfortunately Heijlsberg has several times expressed a certain distate for AOP so it's doubtful if we'll ever see more support for it in the framework. There are however some pretty good thirdparty libraries if you want to experiment with it

Ontologically, inheritance is represented by a directed acyclic graph of is-a relations between classes. Because the is-a relation is transitive (from its definition) the graph must be acyclic (or rather, if there is a cycle the only thing you can conclude is that everything in a cycle is actually the same thing). Many languages apply further restrictions (e.g., forcing a tree instead of a DAG) due to the experience with the complexity of C++'s inheritance, but that's not inherent whereas the restriction to DAGs is.

But there are other possible relations, provided the general transitivity requirement is dropped. This is why delegation is such an important software pattern; it's the basis for many relation implementations.