Duck Typing, this is fun. I keep saying, right, Ruby emphasizes
what do you do, what methods do you respond to instead of
emphasizing what class are you? The question is why should you
care about this, right? How does this encourage productivity?
What kind of reuse does it buy you? An informal and humorous
name some people have used to refer to Ruby's implementation of
this is Duck Typing . You may have heard the colloquial
expression that if it looks like a duck , it walks like a duck ,
quacks like a duck , well , it might as well treat it like a
duck. What we are saying is if you have an object that responds
to certain kinds of methods, you just treat it as the thing that
looks like it is . If it responds to things like give me each of
yourself or reverse yourself, well , what is it meaningful to
talk about those operations on? Well , it must be some kind of a
collection. You say something quacks like a collection in
Ruby it means, " Ah, you can expect that thing to respond to
most of the same collection -oriented methods that a normal
collection like a built -in collection arrays would respond to.
" If you use interfaces in Java , you will notice this is kind
of similar , but I think this is actually much easier to use. As
an example, if I call Sort on my list, what does that actually
mean ? Well, suppose my list is something like this array of
integers, you can imagine what it would mean for Sort to operate
on that . What if it was a list of Strings , you can imagine for
what it means for Sort to operate on that or even a list of
symbols or even what you get back from IO readlines , which is
all of the lines in a file one line at a time, right ? This last
one is basically we are saying, "Sort this file ." Why does this
work? To get the answer to the question, we will introduce one
new Ruby construct that doesn't really appear in other languages
. In languages where it does appear , the term module doesn't
mean the same thing . In Ruby, a module is a collection of
methods that aren't a Class . You can't instantiate it. There
are no instances of a module . It's really just a group of
methods that collect a bunch of related behaviors and they go
together . Now in some simple cases, the use of a module is just
to collect methods like methods related to a common set of
operations like mathematical operations or constants that are
mathematical operations . That's pretty trivial . The much more
interesting use of modules, which we focus on is when you use a
module to mix methods into a class . What does that mean ?
Suppose, during my declaration for Class A, I say include the
name of some module , that means that when I try to call a
method on an instance of A , if I call for example, A. foo,
first it will search whether Class A has that method, then it
will search whether any modules included into the class have
that method, and then it will do the other stuff we have already
talked about . It will call method missing . It will point up to
the ancestors, but modules essentially give you a way to say,
"Wait a minute , add a few things to this class , I am not done
with it yet. " The reason you'd want to do that is that it's
often possible , because Ruby cares more about responding
than it does about classes and types . It's possible to define
modules, collections of behaviors in a way that's reusable
around a lot of different classes. In fact it might surprise you
to learn . As one example, that the sort method we used on the
previous slide as an example , you would think that it's defined
on arrays , but it's actually not to defined on arrays, it's
defined a module called Enumerable . The Enumerable module can
be mixed into any class that knows how to enumerate the elements
of itself . Let me say that again, because this is a little bit
non -obvious , okay? We saw these different examples of using
each and I kept saying we are not intrusively reaching into a
collection and getting stuff, we are asking the collection
nicely , "Please give me back each element of yourself in turn.
I don't care how you get them, I just want each elements so that
I can do stuff to it ." Now, what we are saying is , "Well , if
you can do that, then there is a bunch of things that I know
how to do for free . For example, if you can return
each element of yourself, I can sort you ," great , provided
that your methods .. . the things inside your collection are in
fact sortable . We will come to that in a moment . What else
could you do? If you can return every element of yourself , I
can test every element against some condition that was Select
and Collect and those methods that we saw. What does this mean
? As a concrete example , it means that a mix-in is like a
contract . The module Enumerable, we are going to use it as an
example, because it's used all over the place in Ruby , it's
very common. It assumes that the target object in the class you
are mixing it into know how to respond to each, right ? Like I
said, if you define a new class , for example, a binary tree
data structure and you define each as a way to get at the
elements of that data structure, you can now mix -in Enumerable
and you will get these methods for free . It will define for you
predicates like all and any, collect , find, include , map,
partition ; a whole bunch of really neat stuff just because you
agreed to provide each . This is a hugely powerful behavior .
Enumerable is also where Sort happens to be defined in Ruby ,
but Sort also has another requirement, which is that your model
. .. sorry . Sort also has another requirement, which is that
the class .. . the elements in the collection be able to respond
to the Spaceship operator. Remember the Spaceship operator ?
It's kind of rolling all the equality operators into one. A
Spaceship B is negative one if A is less than B. Positive one if
B is greater than A. Negative one if A is less than B, positive
one if A is greater than B and zero if they are equal , right?
You can imagine for whatever class you want you can define the
Spaceship operator. You can define it to be sorting your colors
in your favorite order if you wanted. Now , obviously in classes
like Int and String, Spaceship is already defined with the
semantics that you would expect . The important thing about this
is if you have a collection of things and the collection
responds to each and if the elements in the collection respond
to Spaceship, then you have to do zero work to get that thing to
be sorted, right ? Because what is sorting? It's taking para
-wise comparisons and seeing which one is greater of the two,
and in some algorithmic form put in those things back together
. In fact there is another module that ships with Ruby called
Comparable, which says , if you provide the Spaceship operator,
which takes one argument , right? Spaceship takes one argument
because there is the receiver of Spaceship and there is the
thing you are comparing the receiver against. If you provide
Spaceship , then I will define these other ones for you and also
give you happy ones like between which tells you if one element
is between two others according to sort order . What does this
all mean? It means the class of objects really doesn't matter
almost at all only the methods to which they respond. Let's look
at a couple of concrete examples of this . Sorting a file, well
, we have File. open, that's a method that returns an IO object
representing an open file . IO objects actually define each.
They define it as, " I am going to return each line of the file
in turn. " That's a pretty sensible semantics for reading a text
file . What does that mean? I can say open a file and sort it .
That's it. I don't have to provide anything because the thing
that I am calling sort on is a collection that responds to each
. It knows how to yield every element of itself . Inside that
collection what is each element? Well , it's a String, right ?
It's a line of a file. Strings know how to compare themselves ,
that's part of the built in String behavior. With no extra work
I can reuse behaviors despite the fact that the classes are
different. I can also say which lines of this file begin with a
vowel , well, that's easy enough . We already know how to use
Select to pass an anonymous method to something ,
anonymous function . For every line of a file we are going to
we are going to pick out the ones where this regular expression returns
true. Again, zero additional work. We can do our own example
with accounts, but before we do that, let's ask how it would be done?