-=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- (c) WidthPadding Industries 1987 0|639|0 -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=- -=+=-
SoCoder -> Article Home -> Advanced Techniques


 
JL235
Created : 04 January 2007
Edited : 04 January 2007
Language : QBasic

Duck Typing

A short description with example

As many of you know I am a bit of a Ruby enphusiast. After reading sections of 'Programming Ruby' I also finally feel confident enough to write my first article for Socoder, on 'duck typing'.

For those who don't know, duck typing is a form of 'dynamic typing' where what a variable can do (as opposed to what it is) defines what I does. The name comes from the analogy used to describe this, where 'if it walks like a duck and talks like a duck, it's a duck.' The point here is that how the variable acts defines how it is to be used.

So... this means?

Effectively objects of different classes can be used in the same situation. For example:

The above code is a function with one parameter 'a'. This will push "a string" onto the end of 'a'. In terms of duck typing, as long as '<<' is a function of 'a' then "a string" will be pushed onto it's end. Some examples can help show this.


In the first example a string is passed in so the '<<' method of the String class is run, however in the second example an array is passed so instead the same method is run from the Array class. Both methods have the same signature, but it is the class of the variable it self which depicts which is used. A File, an IO (input/output), Array and String class' are just some of this with this method, so any of these could be used in this example, and many other classes also have this method. The real point here however is that instead of the variable's class being checked in the methods parameters, it is checked on the methods they use. This is the real difference that Duck typing makes.

But if the parameters aren't checked then couldn't I throw in something silly, like a Time object? Well yes, but you could do that in Java too. However the difference is that in Java the error will occur at the methods parameter, in that will not be able to find a method to take the Time object. In Ruby this error is just passed further so it occurs when it tries to use a method of the class ('<<' in this case) which it doesn't have. However yes you could pass in something completely unrelated, like a Fixnum where '<<' is for bitshifting and so is just wrong in this context. This is the main worry with duck typing, that things will be passed around into strange places making crazy things happen and so causing your program go completely out of control.

But when does this actually happen in programming? If you write your program properly then it won't. Duck Typing is somthing to help you, something to be taken advantage of. Such as what if you deliberately wished to pass in a different object, such as an Array instead of a String? Then in Java you will have to find the method and change it to accomodate the type of the collection you are passing or write a second method of the same name, overloading this, to achieve the same affect. In Ruby, as seen above, you just pass in an Array instead of a String.

A Practical Example

Next however I will move onto a more practical example. Let's say I am writing a real-time strategy game where I want to check for collisions between my units and all other units, resources, buildings and scenery. In Blitz the formula I will use is:


The formula finds the absolute value of the distance between (x1,y1) and (x2,y2) where these co-ordinates are centre of the two units. It then checks if the distance is less then half the width of the first unit plus half the width of the other unit, and does the same with the y distance and the heights of the two units. If the x and y distances is less then this they are overlapping, otherwise they are not.

So what I wish to achieve is to create methods for colliding against all other objects. In Java this could very easily be achieved through 'method overloading', where I simply have a method for each of my collision checks, as shown below:


This now works with my lists of different objects: enemyUnits, enemyBuildings, resources and sceneryList. With these lists I can now perform:


The problem I have with this example is that first it repeats alot of code as I have four very similar methods for collission detection. I could easily break the collision formula into it's own method, and have each of the 4 collision methods call that with the parameters being passed in. So I could compress the first section further by:


I could also have that one collision method being called directly from the for loops without the four other wrapping methods around it. But that would make the loops look less tidy and I'd rather be able to hide all these workings behind whole methods, even if they only contain single lines. Plus what if I wanted to change the formula? By having it seperated into it's own allows me to only change the formula once if I need to. But it's still rather long with the four loops calling four very similar methods.

The Ruby way

This is where duck typing can come in to it's own. The original 'collided' method used the methods: 'getX', 'getY', 'getHeight' and 'getWidth'. So as long as what I pass in has those methods, do I really need to check what the class the object is? This means that I wouldn't need to abstract the collision formula into it's own seperate method, because I can write all five into:


Suddenly over 30 lines in Java become 5 in Ruby (and that doesn't include the Java doc comments, which should never be left out). Before I explain the method, I need to explain certain conventions in Ruby which have also made some minor changes. I added a '?' to the method name because it is effectvely a question. Ruby's get methods are also typically the same name as the variables themselves, as I am not (and shouldn't) accessing those variables directly. The return also isn't needed, as the last object in the method is returned, which in this case will be a True or False object. But I keep it for those who aren't familiar.

As long as what is passed in to the method has the methods 'x', 'y', 'width' and 'height' (for getting these variables) then I can pass into the method any object I wish. It could be a Unit, Resource, Scenery or Building because they will all have these methods, meaning I can save alot of space. Another important advantage, is that lets say I wanted to add an Item class to my RTS. As long as it has those same methods (which it would need for the Java version too), the object can be passed into my 'collided?' method without any worries about their being any problems with the current method. In the previous Java example I would have to write another method to accept Item objects, which means more of the code would have to be changed. As long as the object will walk like a duck, it will be a duck.

Thanks to how easily Ruby works with arrays I can also compress all the arrays carrying the different objects into one creating a single loop inside the first.

That's the real advantage of duck typing, the ease to pass around and work with variables in such an easy context. Just like an object-oriented language, if used well and properly it can help to lower the amount of code repetition and make maintanability of code far easier.

However this code still isn't as short as it could be. First I don't like all those Arrays stuck togethor like that, it's untidy, so I'll be changing that to a single parameter passing them in. Secondly what I am doing will be inside the method, what if I could pass it away elsewhere to have it sorted there. From here on I will be adding a little extra ruby code to the article, as I will now be discussing some minor brief points about blocks, iteration and yielding objects. I discuss these mainly at this point in time because using them can enable me to compress this code further.

A little extra...

What I can do is two minor changes. First I can pass in all the objects I will be checking the collisions against as a parameter. This way my code at this point is fully abstracted from any real lists of objects in my RTS. Second I can yield a Unit if it collides with an entity from the array. Yield throws the object out of the method it is running in and outside, where it can then be run. As a result that code is not shown here because it is outside the method:


This will iterate through each entity within iterating within each unit. The next line within the loops is a command with an if statement on the end. It is essentially 'If true then doSomething()' in Blitz, only instead it is 'doSomething() If true'. In this case it will 'yield' the unit if it has collided with the entity. Yield will throw the unit out of the method and pause it's processing. The object thrown will then be passed to a block outside, which also requires parameters to catch the object. You can yield multiple objects (including none) and the blocks doesn't neccessarily require parameters. This will mean I can hide the whole set of iteration through the loops into one method where I can get my collided units from, rather then having to have the whole loop set up each time.



The 'do' 'end' is a block, with the 'collidedUnit' as it's parameter. In this case it will run the 'checkCollisions' method with those arrays passed in, and if any units collide they will be passed out as 'collidedUnit' where I can do something with them, like reposition them so their not on top of my other RTS objects.

Finally

Blocks can also appear as '{|collidedUnit| #do something }', which by convention are for single lines (although you can write them over multiple lines). The '{' is essentially the 'do' and the '}' is the 'end'. There are other aspects which can be improved, such as allowing the 'checkCollisions' method to take multiple parameters, and simpy adding the arrays togethor inside hiding that section of code. I was also planning a section of doing something to the Unit based on the entities class, as methods can be stored as objects. But these are a little too advanced for this article, maybe they can appear in the second.

 

Comments


Thursday, 04 January 2007, 09:47
Scherererer
I think that in the above example where you said that the Java fails in that you have to overload several methods, there is a very simple solution, and its one that is better from a design standpoint. Simply, you would create an interface called something like ICollisionObject that all collidable objects would implement, and then you would only need one method that would work like this:

And any object implementing that interface would be "collidable". The nice thing about this implementation is it creates a stricter policy so that if another person were to work on it they wouldn't try using an object that wasn't collidable, which could cause some puzzling run-time errors.
Thursday, 04 January 2007, 09:55
power mousey
squeak!!

very good and long read. this is truly an article of bloggish proportions, diablo.

um, some things I don't quite follow nor understand. as I told you that I might be checking out Ruby myself.

can you level it down with some articles such as

Beginning Ruby for Dummies. like me
Ruby Does Her Nails: Intermediate Ruby
The Simple Joys and Treasures of the Ruby

I will read up on this more too. But I like to get gradually into it and check it out...as you said

cheers,
Thursday, 04 January 2007, 13:39
JL235
The mouse is moving like crazy (it is a Microsoft optical mouse) so I haven't much ability. Instict, yes you could improve upon my examples with your idea, but the point still stands. My original article had used an example of all objects being inherited by a single object, an 'rtsObject' (in my respect), but this is still more work then the Ruby solution.

Power Mousey, I might try the articles suggested, but the problem is to convey several aspects of Ruby at once. The fortinate aspect of this topic is how I only have to work with one idea, 'duck typing', and so only explain the aspects related. Many of the early chapters of 'Programming Ruby' reffer me to later ones for a full explination of what they discuss, so how can I summerise the same in a few pages?

In short, my article will not teach Ruby. It is not meant to, but instead teach the idea of one aspect; 'duck typing'.
Thursday, 04 January 2007, 14:25
power mousey
okay

ha! when I first saw the blog topic I said what the heck!! Duck typing!?

What a quacky thing? I thought it was going to be about quacky and squeaky ducks typing.

anyway, we I start to learn about this duck typing in Ruby I will refer to your article too. true.

ahhh!!!,
power mousey