The hash example seems to go entirely against duck typing without ever mentioning it. Does that mean I shouldn’t do duck typing? Or I should? Based on this I should probably just be using a static language…
I too think that it’s unreasonably strict. I’m not familiar with Ruby, but is_a is frowned upon in most class-based OOP: the usual alternative is to ask the object if it can do something (i.e. call one of its methods), rather than the language (via is_a).
A simplistic version might use a method like canHash, returning a boolean. This moves the responsibility away from the language and into the objects, where we can choose how it’s implemented. The problem with doing this is that we’ll probably have to change/wrap the objects that we’re using to give them this new method.
A nicer approach would use what’s already there. For example, in Python the foo[bar] notation is implemented by calling a __getitem__ method. Hence we could have the constructor check whether the given object has a __getitem__ method, rather than what class it is/inherits from. One problem with this is that the object might use a “magic method” to handle those calls, which may cause our check to fail despite giving a valid object. Whether we want to allow that or not depends on the language culture.
The equivalent with static typing is an interface, e.g. KeyValueMap. The nice thing about interfaces is that we don’t care what the actual type is, how it is structured, if it’s a subtype of something, etc. we only care about “can we use it like a hash map?”. This is like doing a canHash check, but doing it statically at compile time rather than at run time; like canHash, we would have to ensure that the types we care about provide an implementation of the interface. Some languages, like Haskell, allow us to provide such implementations ourselves in an ad-hoc way (although there can be conflicts, see “orphan instances”); in languages like Java we can only implement interfaces from within the type (actually class) definition, we can’t “plug in” an implementation later (we’d have to provide a wrapper).
The hash example seems to go entirely against duck typing without ever mentioning it. Does that mean I shouldn’t do duck typing? Or I should? Based on this I should probably just be using a static language…
I too think that it’s unreasonably strict. I’m not familiar with Ruby, but
is_ais frowned upon in most class-based OOP: the usual alternative is to ask the object if it can do something (i.e. call one of its methods), rather than the language (viais_a).A simplistic version might use a method like
canHash, returning a boolean. This moves the responsibility away from the language and into the objects, where we can choose how it’s implemented. The problem with doing this is that we’ll probably have to change/wrap the objects that we’re using to give them this new method.A nicer approach would use what’s already there. For example, in Python the
foo[bar]notation is implemented by calling a__getitem__method. Hence we could have the constructor check whether the given object has a__getitem__method, rather than what class it is/inherits from. One problem with this is that the object might use a “magic method” to handle those calls, which may cause our check to fail despite giving a valid object. Whether we want to allow that or not depends on the language culture.The equivalent with static typing is an interface, e.g.
KeyValueMap. The nice thing about interfaces is that we don’t care what the actual type is, how it is structured, if it’s a subtype of something, etc. we only care about “can we use it like a hash map?”. This is like doing acanHashcheck, but doing it statically at compile time rather than at run time; likecanHash, we would have to ensure that the types we care about provide an implementation of the interface. Some languages, like Haskell, allow us to provide such implementations ourselves in an ad-hoc way (although there can be conflicts, see “orphan instances”); in languages like Java we can only implement interfaces from within the type (actually class) definition, we can’t “plug in” an implementation later (we’d have to provide a wrapper).