Comments on: Managing Decoupling Part 3 – C++ Duck Typing I've used systems like this in the past, moved to structure of arrays, then moved to storing the arrays by type so that no extra type tags needed to be stored. If you encode the type into the field id (very easy with integer ids) you can still get all the benefits of duck typing without having to break type safety (and you can make your search space smaller for ids). I’ve used systems like this in the past, moved to structure of arrays, then moved to storing the arrays by type so that no extra type tags needed to be stored. If you encode the type into the field id (very easy with integer ids) you can still get all the benefits of duck typing without having to break type safety (and you can make your search space smaller for ids).

]]>
By: Steven Shaw/2011/02/25/managing-decoupling-part-3-c-duck-typing/#comment-2046 Steven Shaw Mon, 28 Mar 2011 03:09:38 +0000

]]>
By: Dan/2011/02/25/managing-decoupling-part-3-c-duck-typing/#comment-1144 Dan Tue, 01 Mar 2011 23:50:20 +0000 Link to the pdf.

]]>
By: andrew fray/2011/02/25/managing-decoupling-part-3-c-duck-typing/#comment-1095 andrew fray Mon, 28 Feb 2011 11:33:48 +0000 There's another kind of duck-typing in C++ that you don't mention: templates. <pre lang="cpp" escaped="true"> template <class T> void quackADuck(const T& duck) { duck.quack(); } class Mallard { void quack() const { /* */ } }; class Muscavoy { void quad() const { /* */ } }; quackADuck(Mallard()); quackADuck(Muscavoy()); </pre> Templates obviously have their disadvantages but they are still practical in many cases, and avoid vtables and dynamic allocation. There’s another kind of duck-typing in C++ that you don’t mention: templates.

template <class T> void quackADuck(const T& duck) { duck.quack(); }
 
   
 
  class Mallard { void quack() const { /* */ } };
 
  class Muscavoy { void quad() const { /* */ } };
 
   
 
  quackADuck(Mallard());
 
  quackADuck(Muscavoy());

Templates obviously have their disadvantages but they are still practical in many cases, and avoid vtables and dynamic allocation.

]]>
By: Peter/2011/02/25/managing-decoupling-part-3-c-duck-typing/#comment-1081 Peter Sun, 27 Feb 2011 01:56:37 +0000 Maybe part of my dislike of duck typing comes from the fact that I avoid working with C++, and so have tools available to me that relieve a lot of the problems you describe: a decent Intellisense, automatic refactoring, etc. (C# 4.0 plus Resharper: code reviews have never been so much fun). Fair point that there's a tradeoff between multiplicity and complexity of interfaces. I suppose that in practice I tend to err on the side of more complexity - after all, if I want to implement the interface, I just right-click it and choose 'implement,' and then all the stubs are generated for me as throwing NotImplementedException()s. Very little typing involved. What it <i>does</i> harm, if you take this too far, is readability - having to scroll past loads of stubs to get to the code you actually want to look at can start to impact things. Maybe this comes down to a certain amount of personal preference, but I'd take things like compile-time type checking over 'more typing' any day - especially because 'more typing' is much easier to automate. And yes, this is a side-track. I <i>am</i> a graphics coder, so, as you said, it's all pretty familiar already ;-) The place that I most frequently used this was with vertex layouts, indeed. Though I note that, in D3D at least, when describing a vertex layout you'd include not just the type information, but also some semantic bindings - have you not found any need to do that in your system? How can you tell the difference between a vector3 member that represents a position, and a vector3 member that represents a normal, or a velocity? Maybe part of my dislike of duck typing comes from the fact that I avoid working with C++, and so have tools available to me that relieve a lot of the problems you describe: a decent Intellisense, automatic refactoring, etc. (C# 4.0 plus Resharper: code reviews have never been so much fun).

Fair point that there’s a tradeoff between multiplicity and complexity of interfaces. I suppose that in practice I tend to err on the side of more complexity – after all, if I want to implement the interface, I just right-click it and choose ‘implement,’ and then all the stubs are generated for me as throwing NotImplementedException()s. Very little typing involved. What it does harm, if you take this too far, is readability – having to scroll past loads of stubs to get to the code you actually want to look at can start to impact things.

Maybe this comes down to a certain amount of personal preference, but I’d take things like compile-time type checking over ‘more typing’ any day – especially because ‘more typing’ is much easier to automate.

And yes, this is a side-track. I am a graphics coder, so, as you said, it’s all pretty familiar already ;-) The place that I most frequently used this was with vertex layouts, indeed. Though I note that, in D3D at least, when describing a vertex layout you’d include not just the type information, but also some semantic bindings – have you not found any need to do that in your system? How can you tell the difference between a vector3 member that represents a position, and a vector3 member that represents a normal, or a velocity?

]]>
By: Niklas Frykholm/2011/02/25/managing-decoupling-part-3-c-duck-typing/#comment-1053 Niklas Frykholm Sat, 26 Feb 2011 06:32:15 +0000

Nice.

]]>
By: Niklas Frykholm/2011/02/25/managing-decoupling-part-3-c-duck-typing/#comment-1052 Niklas Frykholm Sat, 26 Feb 2011 06:24:33 +0000 I kind of both agree and don't agree with this. As you say, the method's expectations of the object already constitute an "informal interface". Formalizing that interface into a (set of) interface class(es) makes the expectations clearer and enables compile time type checking. From that perspective, the only advantage of "duck typing" is that it saves us some typing (the other kind of typing, well come to think of it, actually both kinds of typing). On the other hand it can save us A LOT of typing. Properly using an "orthonormal" set of interface classes as you propose is quite a lot of work. For example, instead of taking a String, your method would take an ICharIndexable, IFindCharable, IDebugPrintable and IComparable. Or maybe that shouldn't be IComparable, maybe it should be IGreaterThanAble and IEqualAble? Or maybe a less silly example. In my article the integrate_position function would need to take an object that is IHasPosition and IHasVelocity. You end up juggling a ton of interface definitions and in practice, nobody is going to write their code that way. They will just specify the type explicitly. I guess my point with this rather long-winded example is that once you save enough typing you get a qualitative change in what the language feels like. E.g., a language with proper closures <i>feels</i> different, even though you can do the same thing by creating a custom functor class and assign the "upvalues" that you need to member variables. In the same way, using "duck typing" <i>feels</i> different/easier than specifying everything with explicit interfaces. So I do think that "duck typing" is "better" in a sense. Of course, explicit interfaces is better in another sense, since it allows compile time type checking. I guess what it boils down to is dynamic vs static typing. If you are already using dynamic typing, duck typing doesn't cost you anything, it just means that you <i>abstain</i> from checking the object's type. So it is actually easier to duck type than not. But in a language with static typing, using duck typing means giving up compile time type checking and that might not be something you are prepared to do. Anyway, this duck typing vs interfaces discussion is kind of a side track to the ideas in the article, since for the kind of duck typing presented there you wouldn't want to use interfaces anyway. I.e., representing a vertex as List<IVertexComponent> would be way too expensive. I kind of both agree and don’t agree with this. As you say, the method’s expectations of the object already constitute an “informal interface”. Formalizing that interface into a (set of) interface class(es) makes the expectations clearer and enables compile time type checking. From that perspective, the only advantage of “duck typing” is that it saves us some typing (the other kind of typing, well come to think of it, actually both kinds of typing).

On the other hand it can save us A LOT of typing. Properly using an “orthonormal” set of interface classes as you propose is quite a lot of work. For example, instead of taking a String, your method would take an ICharIndexable, IFindCharable, IDebugPrintable and IComparable. Or maybe that shouldn’t be IComparable, maybe it should be IGreaterThanAble and IEqualAble?

Or maybe a less silly example. In my article the integrate_position function would need to take an object that is IHasPosition and IHasVelocity. You end up juggling a ton of interface definitions and in practice, nobody is going to write their code that way. They will just specify the type explicitly.

I guess my point with this rather long-winded example is that once you save enough typing you get a qualitative change in what the language feels like. E.g., a language with proper closures feels different, even though you can do the same thing by creating a custom functor class and assign the “upvalues” that you need to member variables. In the same way, using “duck typing” feels different/easier than specifying everything with explicit interfaces.

So I do think that “duck typing” is “better” in a sense. Of course, explicit interfaces is better in another sense, since it allows compile time type checking.

I guess what it boils down to is dynamic vs static typing. If you are already using dynamic typing, duck typing doesn’t cost you anything, it just means that you abstain from checking the object’s type. So it is actually easier to duck type than not. But in a language with static typing, using duck typing means giving up compile time type checking and that might not be something you are prepared to do.

Anyway, this duck typing vs interfaces discussion is kind of a side track to the ideas in the article, since for the kind of duck typing presented there you wouldn’t want to use interfaces anyway. I.e., representing a vertex as List<IVertexComponent> would be way too expensive.

]]>
By: Peter/2011/02/25/managing-decoupling-part-3-c-duck-typing/#comment-1046 Peter Sat, 26 Feb 2011 01:55:52 +0000
unsigned type = *(unsigned *)o;
 
  if (type == FLOAT_TYPE)
 
      float f = *(float *)(o + 4);

is hard to debug, as Tomp noted. And in 64-bit systems, sizeof(unsigned) may be not equal to 4.

]]>
By: Rob Ashton/2011/02/25/managing-decoupling-part-3-c-duck-typing/#comment-1045 Rob Ashton Sat, 26 Feb 2011 01:37:08 +0000 I implemented just this sort of system in the past. It was pretty useful, but it's tedious to debug since the debugger doesn't know what the format of the memory is (I took the "separate definition from data" route for all data; it was geared toward arrays of homogeneous data not unlike vertex data as mentioned in the article), so writing good data debugging tools to go along with it is very important; the big one being a "dump this data to a text file please" function. Even more slick today would be to write an addin for your favorite debugger to just handle the data in place. Anyway, it works well in practice but it can be a trial to have the rest of the team embrace the majesty of something like this coming from your own hand. Beyond that, the compiler's not going to help you out at all so you do need to know the data that's going to be available and you need to figure out what to do when requests are unmatched. Both returning default values (e.g. zero) and reporting errors are valid options when a requested field doesn't exist, but you'll probably need to choose one or the other. At a high level something like this is "obviously useful," but there are a thousand little implementation details that you won't even know to solve until you get it in place and start using it. I implemented just this sort of system in the past. It was pretty useful, but it’s tedious to debug since the debugger doesn’t know what the format of the memory is (I took the “separate definition from data” route for all data; it was geared toward arrays of homogeneous data not unlike vertex data as mentioned in the article), so writing good data debugging tools to go along with it is very important; the big one being a “dump this data to a text file please” function. Even more slick today would be to write an addin for your favorite debugger to just handle the data in place.

Anyway, it works well in practice but it can be a trial to have the rest of the team embrace the majesty of something like this coming from your own hand. Beyond that, the compiler’s not going to help you out at all so you do need to know the data that’s going to be available and you need to figure out what to do when requests are unmatched. Both returning default values (e.g. zero) and reporting errors are valid options when a requested field doesn’t exist, but you’ll probably need to choose one or the other. At a high level something like this is “obviously useful,” but there are a thousand little implementation details that you won’t even know to solve until you get it in place and start using it.

]]>
By: Colin Riley/2011/02/25/managing-decoupling-part-3-c-duck-typing/#comment-1043 Colin Riley Fri, 25 Feb 2011 23:44:03 +0000 This is a really great post and implementation. It's similar to what I had in mind for my changing C++ post - having the compiler and ABI be changed in ways that could implement things like inheritance in this way out of the box. This is a really great post and implementation. It’s similar to what I had in mind for my changing C++ post – having the compiler and ABI be changed in ways that could implement things like inheritance in this way out of the box.

]]>
By: martin/2011/02/25/managing-decoupling-part-3-c-duck-typing/#comment-1041 martin Fri, 25 Feb 2011 23:17:55 +0000 The last reflection system I worked on had some of the duck-typing principles you've described at its core. The last reflection system I worked on had some of the duck-typing principles you’ve described at its core.

]]>
By: Richard Fine/2011/02/25/managing-decoupling-part-3-c-duck-typing/#comment-1038 Richard Fine Fri, 25 Feb 2011 22:49:59 +0000 The biggest problem I've always had with duck typing is summed up by the contrast you draw between inheritance typing and duck typing: Inheritance typing "still introduces a coupling, because it forces the objects we manage to inherit a type defined by us." While when we're duck typing, "we don’t care about the type of the object at all, as long as it has the fields and methods that we need." How, exactly, are these different? Even when we're duck typing, we need a particular set of fields and methods to be present, and we need them to conform to particular signatures, too. The only real difference is that the contract isn't stated explicitly, and there are many drawbacks to that: the compiler's completely hamstrung in what it can check for you, and it's much less clear which methods and fields an object <i>does</i> support, which is crap for someone who isn't familiar with the code (and really, all code should be written so it's good for people who aren't familiar with it). Yet where's the benefit? The only time I can see that duck typing really <i>is</i> going to be useful is when the interface types are crappy, either through poor human design, or through odd platform-interoperation scenarios. If you're provided with an interface IDuck: <pre> interface IDuck { void Quack(); void Waddle(); void Eat(Bread morsel); bool DoesEcho { get; } float Weight { get; } /* ... etc ... */ } </pre> and you're only using a part of the system that deals with quacking, then sure, it's very convenient to be able to just implement "void Quack()" and get on with things. The real problem here, though, is that IDuck is too big an interface; it's an interface modelling what the object <i>is</i>, rather than what you can do with it. What it should really be is a set of interfaces - IQuacker, IWaddler, IBreadConsumer, etc - at which point, duck typing really doesn't gain you very much. The biggest problem I’ve always had with duck typing is summed up by the contrast you draw between inheritance typing and duck typing:

Inheritance typing “still introduces a coupling, because it forces the objects we manage to inherit a type defined by us.” While when we’re duck typing, “we don’t care about the type of the object at all, as long as it has the fields and methods that we need.”

How, exactly, are these different? Even when we’re duck typing, we need a particular set of fields and methods to be present, and we need them to conform to particular signatures, too. The only real difference is that the contract isn’t stated explicitly, and there are many drawbacks to that: the compiler’s completely hamstrung in what it can check for you, and it’s much less clear which methods and fields an object does support, which is crap for someone who isn’t familiar with the code (and really, all code should be written so it’s good for people who aren’t familiar with it). Yet where’s the benefit?

The only time I can see that duck typing really is going to be useful is when the interface types are crappy, either through poor human design, or through odd platform-interoperation scenarios. If you’re provided with an interface IDuck:

 
  interface IDuck {
 
      void Quack();
 
      void Waddle();
 
      void Eat(Bread morsel);
 
      bool DoesEcho { get; }
 
      float Weight { get; }
 
      /* ... etc ... */
 
  }
 
  

and you’re only using a part of the system that deals with quacking, then sure, it’s very convenient to be able to just implement “void Quack()” and get on with things. The real problem here, though, is that IDuck is too big an interface; it’s an interface modelling what the object is, rather than what you can do with it. What it should really be is a set of interfaces – IQuacker, IWaddler, IBreadConsumer, etc – at which point, duck typing really doesn’t gain you very much.

]]>