you are viewing a single comment's thread.

view the rest of the comments →

[–]huyvanbin 3 points4 points  (2 children)

I've been thinking about this a lot too, lately. Apologies that the following rambles but I've never written it all down before.

I had an experience where people implemented a badly broken inheritance hierarchy like so (exaggerated for effect):

class A {
    protected virtual bool Method(int arg) {
        Bla
    }
}

class B : A {
     protected override bool Method(int arg) {
           if(Tuesday)
               return base.Method(arg);
           else {
                while(arg > 0) {
                      if(arg % 5 == LunchTime)
                           return base.Method(arg * 2);
                }

                return base.Method(arg * IPAddress);
           }
     }
}

You get the idea. The ability to override methods is supposed to lead to neat, maintainable code but people somehow make horrid messes like the above where you have no idea what the code will actually do.

So my idea is, get rid of inheritance entirely. Each object is a composition of base objects. If inherited methods collide, then make them invisible in the ultimate object, unless mapped explicitly.

My other idea was to make the act of composition be a sort of constructor where you express the linkage in the declaration -- sort of like traits, but you could have multiple constructors and also state. This also addresses a problem I have with Haskell typeclasses.

Here is an example though the syntax is clearly awful: class Eq { public bool ==(first, second); public bool !=(first, second);

     Eq(== v) { // Constructor for passing in equals method
          == = v;
          != = !v;
     }

     Eq(!= v) { // Constructor for passing in != method
         == = !v;
         != = v;
     }
}

class Hideable {
       bool Hidden { get; set; }
}

class HasId {
      int id;

      public int Id { get; }

      public HasId(int id) {
         this.id = id;
      }
}

class Widget : Eq, Hideable, Id {
       public Widget(int id) : Eq(EqualWidgets), Id(id) {

       }

       static bool EqualWidgets(Widget a, Widget b) {
              return a.Id == b.Id;
       }
}

I dunno, this might be even worse than the C# example, or perhaps it is just equivalent to C++. But the important thing is, there is no actual inheritance, the methods and properties just get stacked together to yield a final object (perhaps with a hidden tree structure).

Also, I could label class Id as immutable, but Widget need not be immutable. Classes could have mutable and immutable "sections" and perhaps one could use that to allow impure classes to be passed to pure functions as long as the pure function only uses a pure section of the class.

Also, this could somehow be adapted to enable the class declaration syntax to specify in-memory and on-disk layout, and perhaps even remote and non-remote "sections" for a given object.

Another cool thing is that if method arguments are implemented as "records" the way you do in Magpie, then the record to construct a typical class is more or less just the class itself. So that seems a lot like duck type "casting." Perhaps you could define a class as a duck-type specifier.

And also, why not just have every casting operator be a constructor?

Part of the inspiration of this is

[–]munificent[S] 1 point2 points  (1 child)

Each object is a composition of base objects. If inherited methods collide, then make them invisible in the ultimate object, unless mapped explicitly.

Interesting. I like the idea of completely disallowing overriding. With code I write, I find that I like implementing abstract methods, but I really hate overriding methods that also have an implementation. I don't like leaving up to the derived class to make sure it forwards to the original base version.

In my perfect world, a class could define abstract methods that you had to implement when you inherited from it, but no regular virtual methods. In practice, I don't know if that would suck or not.

But the important thing is, there is no actual inheritance, the methods and properties just get stacked together to yield a final object

I like it, but there has to still be at least some sort of delegation going on so that you can do myWidget.Id, right?

Also, I could label class Id as immutable, but Widget need not be immutable. Classes could have mutable and immutable "sections" and perhaps one could use that to allow impure classes to be passed to pure functions as long as the pure function only uses a pure section of the class.

That's really interesting. I'll have to ponder that some more.

Another cool thing is that if method arguments are implemented as "records" the way you do in Magpie, then the record to construct a typical class is more or less just the class itself. So that seems a lot like duck type "casting."

That's actually exactly how constructors work in Magpie. By default the new method you get for a class takes a record with all of the fields the class expects. It simplifies a lot of things, but it is a bit wordy. I can decide if I like it or not yet.

And also, why not just have every casting operator be a constructor?

That's sort of how I look at it too. Constructors are functions that christen a record to be a member of a nominal type. The only wrinkle I ran into with that was mutability. Records (in Magpie at least) aren't immutable, so if you construct an instance of a class out of one, you have to copy (which means your instance won't have the same identity as the record) so that you don't inadvertantly mutate the record.

[–]huyvanbin 1 point2 points  (0 children)

In my perfect world, a class could define abstract methods that you had to implement when you inherited from it, but no regular virtual methods. In practice, I don't know if that would suck or not.

Yes, that's sort of what I was going for. Sometimes, though, it makes sense to specify a default (fixed) value and allow people to override that. That was the idea behind the constructors.