function isDuck(thing)
return thing.isDuckLike and thing.hasDuckSmell and thing.quacks
end
local catInADuckCostume = {
isDuckLike = true,
hasDuckSmell = true,
quacks = true,
doQuack = function() print('meow?') end
}
This example is absurd, but the general idea is that with duck typing you check the needed qualities on an object and don't care about its type, because you're just operating on those qualities.
Sure, but what did kikito's post have to do with duck typing? He seems to be advocating for subtype polymorphism, not duck typing. I don't see any feature detection (as in your example) in his post or anything else I'd normally associate with duck typing. What's the connection?
Or are we just calling it duck typing because there is no real contract in the form of an interface or abstract class or whatever, so even if it, err, walks like subtype polymorphism, and talks like subtype polymorphism, it must be a duck?
If A and B had a common ancestor C which also implemented printclass (maybe with an error("override in subclass!")), then it would be polymorphism (all classes of C implement the method printClass). If they didn't, it would be duck typing (in this particular piece of code, I expect the object x to respond to the message printClass - I don't care how, it must just respond to it). Without knowing more about my example, "duck typing" is probably a better candidate.
The truth is that I left it open precisely because I didn't want to delve on the specifics of how printClass was resolved. What matters is that the mechanism which decides which method to call is inside each object - that's the essence of OOP in my opinion.
Using the second option, you just need to create C, implement the class-specific methods on it (like printClass)
When I read "implement the class-specific methods on it," it sounds an awful lot like you mean implement the methods that weren't implemented on the superclass. I don't see how that would make any sense if we were talking about duck typing. We wouldn't be implementing the "class-specific" methods, we would just be implementing whatever methods some other piece of code expected to be there. Maybe I'm not reading that bit the way you intended, though.
time thief wrote:When I read "implement the class-specific methods on it," it sounds an awful lot like you mean implement the methods that weren't implemented on the superclass.
By "class-specific" I meant "the method like A:printClass and B:printClass, but for C". Since I was talking about an hypothetical case where there were "dozens of ifs", the equivalent using OOP would involve probably more methods like that.
function MixinDuckCostume(thing)
thing.isDuckLike = true
thing.hasDuckSmell = true
thing.quacks = true
end
local cat = {}
MixinDuckCostume(cat)
If you were to check an objects type, then you wouldn't be able to extend the properties of an object in any meaningful way. To demonstrate with something more useful:
function Mixin(target, source)
for k, v in pairs(source) do
target[k] = v
end
end
local ShieldMixin = {
block = function(self, damage) return damage * 0.75 end
}
local SkeletonEnemy = {
new = function(class) return setmetatable({}, class) end,
__index = {
attacked = function(self, damage)
if self.block then damage = self:block(damage) end
return damage
end
}
}
Mixin(SkeletonEnemy.__index, ShieldMixin)
local first_enemy = SkeletonEnemy:new()
print(first_enemy:attacked(100))
There I demonstrated that a particular feature of the object, that it can block, is a detected feature, as opposed to something that its type has to be checked for.