Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Example of Javascript Duck Typing?

Tags:

javascript

Some programmers advise against using pseudo classical inheritance in Javascript, but advise using duck-typing and giving each object a set of capabilities.

Is there a good example of how that is done? I have an example down below, but it only assign function one at a time. Can we assign a whole group of methods to an object, such as can we set a prototype of OceanAnimal which can "swim", "dive", and "rise", a prototype of LandAnimal for "run", "walk", and "jump", and let an object inherit from one or both of them? (so a fish object can inherit or get the capabilities of OceanAnimal, and a turtle can get the capabilities of both OceanAnimal and LandAnimal?)

var yoyo = {
    name: "Yoyo",
    type: "turtle"
}

var simba = {
    name: "Simba",
    type: "lion"
}

var dolphy = {
    name: "Dolphy",
    type: "dolphin"
}

function swim(n) {
    console.log("My name is", this.name, ", I am a", this.type, "and I just swam", n, "feet")
}

function run(n) {
    console.log("My name is", this.name,  ", I am a", this.type, "and I just ran", n, "feet")
}

Object.prototype.respondTo = function(method) {
    return !!(this[method] && (typeof this[method] === "function"));
}

yoyo.swim = swim;
yoyo.swim(10);

dolphy.swim = swim;
dolphy.swim(80);

simba.run = run;
simba.run(200);

yoyo.run = run;
yoyo.run(2);

yoyo.walk = run;
yoyo.walk(1);

console.log(simba.respondTo("swim"));
console.log(simba.respondTo("run"));
console.log(simba.respondTo("walk"));

console.log(yoyo.respondTo("run"));
console.log(yoyo.respondTo("walk"));
console.log(yoyo.respondTo("fly"));

if(dolphy.respondTo("run")) {
    dolphy.run(10);
}

if(dolphy.respondTo("swim")) {
    dolphy.swim(10);
}

output:

My name is Yoyo , I am a turtle and I just swam 10 feet 
My name is Dolphy , I am a dolphin and I just swam 80 feet 
My name is Simba , I am a lion and I just ran 200 feet 
My name is Yoyo , I am a turtle and I just ran 2 feet 
My name is Yoyo , I am a turtle and I just ran 1 feet 
false 
true 
false 
true 
true 
false 
My name is Dolphy , I am a dolphin and I just swam 10 feet
like image 541
nonopolarity Avatar asked Oct 06 '12 18:10

nonopolarity


People also ask

What is duck typing in JavaScript?

Duck Typing is a way of programming in which an object passed into a function or method supports all method signatures and attributes expected of that object at run time. The object's type itself is not important. Rather, the object should support all methods/attributes called on it.

What is duck typing in TypeScript?

The duck-typing technique in TypeScript is used to compare two objects by determining if they have the same type matching properties and objects members or not. For example, if we assign an object with two properties and a method and the second object is only assigned with two properties.

Is Python duck typed?

Python is a duck typing language. It means the data types of variables can change as long as the syntax is compatible. Python is also a dynamic programming language.

What is the difference between duck typing and inheritance?

With nominative typing, an object is of a given type if it is declared to be (or if a type's association with the object is inferred through mechanisms such as object inheritance). In duck typing, an object is of a given type if it has all methods and properties required by that type.


2 Answers

Functions in JavaScript are versatile. They can be used as subroutines, methods, constructors, namespaces, modules, and much more.

The reason people advise against using pseudo classical inheritance in JavaScript is because it hides the true power of JavaScript. Functions are just as expressive if not more expressive than objects. This has been proven by Alonzo Church who's work, Lambda Calculus, is Turing Complete.

To answer your question directly I'll use functions to create a Turtle, a Lion and a Dolphin. Then I'll demonstrate how a turtle is an OceanAnimal and a LandAnimal, how a lion is only a LandAnimal, and how a dolphin is only an OceanAnimal. I'll conclude by explaining what is duck typing.

First let's create the constructor for an OceanAnimal:

function OceanAnimal() {
    this.swim = function (n) {
        return "I am " + this.name + ", the " + this.type +
               ", and I just swam " + n + " meters.";
    };
}

Next we'll create the constructor for a LandAnimal:

function LandAnimal() {
    this.walk = function (n) {
        return "I am " + this.name + ", the " + this.type +
               ", and I just walked " + n + " meters.";
    };
}

Alright. So now let's create the constructor for a Turtle:

Turtle.prototype.type = "turtle";

function Turtle(name) {
    this.name = name;
    LandAnimal.call(this);
    OceanAnimal.call(this);
}

What's happening here? Okay, we want Turtle to inherit from both OceanAnimal and LandAnimal. So we're calling LandAnimal.call(this) and OceanAnimal.call(this). In this way we're using the OceanAnimal and LandAnimal constructors as mixins. Thus Turtle inherits from both OceanAnimal and LandAnimal without actually becoming of type OceanAnimal or LandAnimal.

Another thing to notice is that we're setting the type property on the prototype of Turtle instead of inside it. This is because type is the same for all turtles. Thus it's shared. The name of each turtle on the other hand may vary and hence it's set inside the constructor.

Now let's similarly create the constructor for a Lion:

Lion.prototype.type = "lion";

function Lion(name) {
    this.name = name;
    LandAnimal.call(this);
}

Since Lion is a LandAnimal we only mix in the LandAnimal constructor.

Similarly for a Dolphin:

Dolphin.prototype.type = "dolphin";

function Dolphin(name) {
    this.name = name;
    OceanAnimal.call(this);
}

Now that we have created all the constructors let's create a turtle, a lion and a dolphin:

var yoyo = new Turtle("Yoyo");
var simba = new Lion("Simba");
var dolphy = new Dolphin("Dolphy");

Awww, now let's set them free:

alert(yoyo.walk(10));
alert(yoyo.swim(30));   // turtles are faster in the water
alert(simba.walk(20));
alert(dolphy.swim(20));

Haha. That was fun. Personally I love yoyo the most.

Okay, so what's duck typing? We know that yoyo is an OceanAnimal and a LandAnimal. However if we do yoyo instanceof OceanAnimal or yoyo instanceof LandAnimal then it returns false. What?

  • You: Stupid JavaScript. A Turtle is an OceanAnimal and a LandAnimal!
  • JavaScript: Not from where I'm standing. All I know is that it's a Turtle.
  • You: But if it swims then it's an OceanAnimal, and if it walks then it's a LandAnimal.

So since JavaScript is such a party pooper we'll have to create our own test to check if an object is an OceanAnimal and if it's a LandAnimal.

Let's start with an OceanAnimal:

function isOceanAnimal(object) {
    if (typeof object !== "object") return false;
    if (typeof object.swim !== "function") return false;
    return true;
}

Similarly, for a LandAnimal:

function isLandAnimal(object) {
    if (typeof object !== "object") return false;
    if (typeof object.walk !== "function") return false;
    return true;
}

So now we can use isOceanAnimal(yoyo) instead of yoyo instanceof OceanAnimal, and isLandAnimal(yoyo) instead of yoyo instanceof LandAnimal; and both these functions will return true for our beloved yoyo. Yay!

This is a simple example of duck typing in JavaScript. To conclude:

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.

Similarly:

When I see an animal which swims like an ocean animal, I call that animal an ocean animal; and when I see an animal which walks like a land animal, I call that animal a land animal.

Edit: You can see the above code in action here: http://jsfiddle.net/aaditmshah/X9M4G/

like image 163
Aadit M Shah Avatar answered Oct 02 '22 13:10

Aadit M Shah


Instead of trying to subclass, why not use the duck-typing initiative to extend with components and dependency-injection?

var Duck = function (flyable, movable, speakable) {

    this.speak = speakable.speak;
    this.fly = flyable.fly;
    this.position = movable.position;

    flyable.subscribe("takeoff", movable.leaveGround);
    flyable.subscribe("land",    movable.hitGround);

}


var duck = new Duck(new BirdFlier(), new BirdWalker(), new Quacker());

var plane = new Duck(new VehicleFlier(), new Drivable(), new Radio());


duck.speak(); // "quack"
plane.speak(); // "Roger"

Notice that plane is happily using the same constructor as duck.

They don't even require a constructor in the first place.
A factory would work just as well.

The point of DuckTyping is that object-construction isn't the concern of the program using the object.

As long as it has the same method/property names, the program will use them, regardless of sub/super/static inheritance.

EDIT: added example components

There are a couple of different ideas here that I'm tying together. So one dot at a time:

First, the basic premise of duck-typing:

// the basic nature of duck-typing
var sheriff = {
    gun : {
        aim : function () { /* point gun somewhere */ },
        bullets : 6,
        fire : function () {
            if (this.bullets === 0) { return; }
            this.bullets -= 1;
            /* ... et cetera */
        }
    },
    draw : function () {
        this.gun.aim();
        this.gun.fire();
    }
},

cartoonist = {
    pen : { scribble : function () { /* ... */ } },
    draw : function () { this.pen.scribble(); }
},

graphicsCard = {
    pixels : [ /* ... */ ],
    screen : { /* ... */ },
    draw : function () {
        pixels.forEach(function (pixel) { screen.draw(pixel); });
    }
};


// duck-typing at its finest:
sheriff.draw();
cartoonist.draw();
graphicsCard.draw();

The goal being to write functions which don't bother to check what kind of object it is:

function duckDraw (array) { array.forEach(function (obj) { obj.draw(); }); }
duckDraw([ sheriff, cartoonist, graphicsCard ]);

So if you've got a sea_turtle, a dolphin, a whale, a submarine and a minnow, your program doesn't have to care about the differences in how they swim. It just cares that they can all .swim();. Each item can worry about itself, and the special way it does what it needs to do.

Duck-Typing

Next is dependency-injection. In a way, dependency-injection also uses duck-typing, but on the inside of your class, instead of the outside (or if you do it like I did up top, it starts on the inside, and then allows for duck-typing on the outside, as well).

Think of it like this: Instead of a person inheriting something, just hand it to them.

If you have a soldier, a sniper a plane and a tank, each one needs a gun. Instead of trying to subclass so that they can all fire... ...why not make different kinds of guns, to meet your needs, and hand them all what they need?

var Rifle = function () {
    this.reload = function () {};
    this.fire = function () { /* ... */ };
},

SniperRifle = function () {
    this.reload = function () {};
    this.fire = function () {};
},

MachineGun = function () {
    this.reload = function () {};
    this.fire = function () {};
},

Cannon = function () {
    this.reload = function () {};
    this.fire = function () {};
};

So now we've got different kinds of guns... You might think that because they've got the same function-names, and they all deal with bullets, so you could try to subclass... ...but you don't need to - none of these guns do the same thing when they fire or reload... ...so you'd end up writing overrides to an abstract virtual method/property in some other language, which would be useless.

So now that we've got them, we can see how we can "inject" them, and what that does for us:

var Soldier = function (gun) {
    this.currentGun = gun;
    this.inventory = {
        guns : [ gun ]
    };
    this.attack = function () { this.currentGun.fire(); };
};

var Sniper = function (gun) {
    this.currentGun = gun;
    this.inventory = {
        guns : [ gun ]
    };
    this.attack = function () { this.currentGun.fire(); };
};

var Plane = function (gun) {
    this.currentGun = gun;
    this.inventory = {
        guns : [ gun ]
    };
    this.attack = function () { this.currentGun.fire(); };
};

var Tank = function (gun) {
    this.currentGun = gun;
    this.inventory = {
        guns : [ gun ]
    };
    this.attack = function () { this.currentGun.fire(); };
};


var soldier = new Soldier( new Rifle() ),
    sniper  = new Sniper( new SniperRifle() ),
    plane   = new Plane( new MachineGun() ),
    tank    = new Tank( new Cannon() );

So now we've got these classes where they call their guns -- they don't care what kind of gun, and it just works, because the gun knows how the gun works and the combatant knows how to fire a gun, and the program knows how to tell a combatant to fire.

But if you look a little closer, the inner code for each combatant is 100% the same for now.

So why not just have a 'Combatant' that you can give specialized components?

var Combatant = function (gun) {
    this.currentGun = gun;
    this.inventory = {
        guns : [ gun ]
    };
    this.attack = function () { this.currentGun.fire(); };
};

var soldier = new Combatant( new Rifle() );

So the insides of the constructor are duck-typing gun, and if you had different classes for combatants, and each class had a fire method, then you can duck-type your units in the game-logic as well.

Ultimately, the constructor would just hold modules: one module to handle shooting, one to handle ground movement, one for drawing, one for player controls, et cetera... The constructor wouldn't have to do anything except put the pieces in touch with one another, and you could make units special by giving them different kinds of guns, or different kinds of movement, or different kinds of health, which operate differently on the inside, but have the same properties and method names for public access.

like image 26
Norguard Avatar answered Oct 02 '22 12:10

Norguard