Reputation: 6361
I've often read that Ruby is a pure object oriented language since commands are typically given as messages passed to the object.
For example:
In Ruby one writes: "A".ord
to get the ascii code for A
and 0x41.chr
to emit the character given its ascii code.
This is in contrast to Python's: ord("A")
and chr(0x41)
So far so good --- Ruby's syntax is message passing.
But the apparent inconsistency appears when considering the string output command:
Now one has: puts str
or puts(str)
instead of str.puts
Given the pure object orientation expectation for Ruby's syntax, I would have expected the output command to be a message passed to the string object, i.e. calling a method from the string class, hence str.puts
Any explanations? Am I missing something?
Thanks
Upvotes: 2
Views: 288
Reputation: 168199
Printing something to somewhere at least involves two things: what is written and where it is written to. Depending on what you focus on, there can be different implementations, even in OOP. Besides that, Ruby has a way to make a method look more like a function (i.e., not being particularly tied to a receiver as in OOP) for methods that are used all over the place. So there are at least three logical options that could be thought of for such methods like printing.
For the second option, IO#write
is one example; The receiver is the destination of writing.
The puts
without an explicit receiver is actually Kernel#puts
, and takes neither of the two as the arguments; it is an example of the third option; you are correct to point out that this is not so OOP, but Matz especially provided the Kernel
module to be able to do things like this: a function-style method.
The first option is what you are expecting; it is nothing wrong. It happens that there is no well known method of this type, but it was proposed in the Ruby core by one of the developers, but unfortunately, it did not make it. Actually, I felt the same thing as you, and have something similar in my personal library called Object#intercept
. A simplified version is this:
class Object
def intercept
tap{|x| p x}
end
end
:foo.intercept # => :foo
You can replace p
with puts
if you want.
Upvotes: 0
Reputation: 230461
I would have expected the output command to be a message passed to the string object, i.e. calling a method from the string class, hence str.puts
This is incorrect expectation, let's start with that. Why would you tell a string to puts
itself? What would it print itself to? It knows nothing (and should know nothing) of files, I/O streams, sockets and other places you can print things to.
When you say puts str
, it's actually seen as self.puts str
(implicit receiver). That is, the message is sent to the current object.
Now, all objects include Kernel
module. Therefore, all objects have Kernel#puts in their lists of methods. Any object can puts
(including current object, self
).
As the doc says,
puts str
is translated to
$stdout.puts str
That is, by default, the implementation is delegated to standard output (print to console). If you want to print to a file or a socket, you have to invoke puts
on an instance of file or socket classes. This is totally OO.
Upvotes: 8
Reputation: 327
puts is a method on an output streams e.g.
$stdout.puts("this", "is", "a", "test")
Upvotes: 2
Reputation: 62668
Ruby isn't entirely OO (for example, methods are not objects), but in this case, it is. puts
is Kernel#puts
, which is shorthand for $stdout.puts
. That is, you're calling the puts
method of the $stdout
stream and passing a string as the parameter to be output to the stream. So, when you call
puts "foo"
You're really calling:
$stdout.puts("foo")
Which is entirely consistent with OO.
Upvotes: 5