Type Hinting & Duck Typing in Ruby vs PHP
PHP’s built in Type Hinting feature is completely optional. You can craft entire applications without ever type hinting once. However, if you don’t you’re missing out on a great way to ensure arguments to methods adhere to an interface.
By implementing an Interface on a Class, you’re guaranteeing that the Class implements the entirety of the Interface’s signature a.k.a. it’s public methods.
Let’s reuse our simple interface from the last section:
Now we’re going to guarantee this Cat
can eat
by implementing the Animal
interface on it:
Cat
implements Animal
, so that means we know that a Cat
can eat
.
While we’re at it, let’s just let Dog
’s in too:
So, that means we can open up a AnimalShelter
and feed our Animals without knowing exactly what kind of Animal
is at the shelter:
Alright, so our AnimalShelter
can take in any Animal
implementation by adding them to an internal array through the addAnimal
method.
Then we can feed
them with $food
. The Animal
contract guarantees that each Animal
has the eat
public method and we type hinted Animal
in the feed
method so no argument will be accepted unless it’s an animal.
Let’s see it in action:
This is a great way of making polymorphic and reusable code. The AnimalShelter
doesn’t really care what kind of Animal
comes through it’s doors. If one day we need to support Alligator
’s coming in, we don’t have to update the AnimalShelter
’s code at all.
Type Hinting in Ruby
Ok, this title is totally misleading. There is no type hinting in Ruby whatsoever. In PHP we have the ability to type hint both incoming parameters and returned parameters from functions.
Is polymorphic code possible in Ruby then? Absolutely.
Interfaces jobs are to ensure a public signature on a class. How can we do that in Ruby? One answer isn’t all that complicated, it’s Guard Statements:
class Cat
def eat(food)
puts "nom nom #{food}"
end
end
class Dog
def eat(food)
puts "bark bark #{food}"
end
end
Now Cat
and Dog
share the same signature. Now let’s create an AnimalShelter
that can house and feed them:
class AnimalShelter
def addAnimal(animal)
raise ArgumentError('Given animal doesn\'t implement .eat!') unless animal.method_defined?(:eat)
@animals << animal
end
def feed(food)
@animals.each do |animal|
animal.eat(food)
end
end
end
In our addAnimal
method, we raise an ArgumentError
unless the given animal can eat
.
This is the logical equivalent of implementing an interface, but it’s actually easier to read. It’s an example of defensive programming, where you’re checking for the incoming data before processing it.
Now I wish Ruby had type hinting & interfaces built into the language. There’s something about the comfort of knowing a particular is absolutely defined on an incoming argument. However, this is how the language is written.
I don’t see the Guard Statement being any less readable than an interface. Just like PHP, these types of Guard Statments are totally optional.