Forum: Ruby Print method

Posted by Fred McArthur (bicycle_brain)
on 2012-08-15 18:17
Hello everyone.  I'm a newbie with a question whose answer is probably
obvious, but I'm too thickheaded to figure it out, so here goes...

We can say "cba".reverse or x.reverse and get "cba". Also x.upcase or
"abc".upcase and get "ABC".

So it's pretty clear that x and "abc" are objects, because we're able to
use  methods to send messages to them.

But when I do x.print or "abc".print it throws an error.  I have to use
print x or print "abc", which seems to violate the "least surprise"
principle and causes sleep deprivation in clueless newbies like me.
Also,
things like print x.upcase.reverse just drive me up a wall.  Why not
x.upcase.reverse.print?

Can anyone help?
Posted by Thiago Massa (Guest)
on 2012-08-15 18:29
(Received via mailing list)
Because print is not a object from the String class. So I'm going to do
what you want(save the following code as string_print.rb):

# Ruby has open classes so I can open a already existing class, like of
String, and declare methods to it
class String
  def print
    puts self
  eld
end

"abcd".reverse.print

# I hope you enjoyed.
Posted by Thiago Massa (Guest)
on 2012-08-15 18:29
(Received via mailing list)
Woops, typo.

# Ruby has open classes so I can open a already existing class, like of
String, and declare methods to it
class String
  def print
    puts self
  end
end

"abcd".reverse.print

# I hope you enjoyed.
Posted by Eric Christopherson (echristopherson)
on 2012-08-15 18:39
(Received via mailing list)
On Wed, Aug 15, 2012 at 11:28 AM, Thiago Massa <thiagown@gmail.com> 
wrote:
> Because print is not a object from the String class. So I'm going to do what
> you want(save the following code as string_print.rb):

No; it's a method from Object (mixed in from Kernel, I believe). It's
a private method, so you can't use dot notation on it; the actual
receiver is the "main"/"toplevel" object, which happens to be an
Object.

I'm not sure what languages actually have a print-type method defined
on instances like that, but there must be some. Languages like Java
and Smalltalk define their text output methods on instances of IO
streams; you can actually do that in Ruby too, using something like
`$stdout.print foo`. But I guess printing values is so common that
Matz decided to basically follow the example of Perl and other
languages for which output is done with something appearing more like
a command or statement than a method.
Posted by Brian Candler (candlerb)
on 2012-08-15 19:46
Eric Christopherson wrote in post #1072456:
> Languages like Java
> and Smalltalk define their text output methods on instances of IO
> streams; you can actually do that in Ruby too, using something like
> `$stdout.print foo`.

The OO conundrum again - function acting on two objects, which object 
class does the function belong in? It could be either.

If it were string.print and you wanted to send to a different stream, 
you would need to do something like

"hello".print($stdout)

which would work fine - Reia works this way <http://reia-lang.org/>

However I can think of one good reason for doing it the Ruby way round. 
print/puts expects a string, and it will call to_s automatically on the 
argument if it is not.

"foo.print" would fail on any object which doesn't have a print method - 
or there would have to be a print method in the top-level Object which 
does to_s.print. The latter would mix a public 'print' method into every 
object - and also risks infinite recursion if to_s doesn't actually 
return a string.

Having a public to_s method is more generally useful than a public print 
method, I think.

Regards,

Brian.
Posted by Henry Maddocks (Guest)
on 2012-08-16 01:49
(Received via mailing list)
On 16/08/2012, at 4:17 AM, Fred McArthur wrote:

> But when I do x.print or "abc".print it throws an error.  I have to use
> print x or print "abc", which seems to violate the "least surprise"
> principle and causes sleep deprivation in clueless newbies like me.
> Also,
> things like print x.upcase.reverse just drive me up a wall.  Why not
> x.upcase.reverse.print?

I know it's a common example in OOD text books, that an object has a 
print method (or draw or display), but that design is wrong.
 "abc".print makes the assumption that String has knowledge of where and 
how it will be 'printed'. String's print implementation will have be 
able to print to all known output devices. If another output device is 
invented String will have to be updated or it won't be able to print to 
it.

Sure you could do "abc".print( device) but that will put unnecessary 
restrictions on the devices interface.

The correct way to do this is have each device know which types it can 
print and how to print them.

$stdout.puts "abc"

The MVC pattern works this way. Ruby has done the correct thing.

Henry
Posted by Tony Arcieri (Guest)
on 2012-08-16 02:02
(Received via mailing list)
On Wed, Aug 15, 2012 at 4:48 PM, Henry Maddocks <hmaddocks@me.com> 
wrote:

> I know it's a common example in OOD text books, that an object has a print
> method (or draw or display), but that design is wrong. [...] Ruby has done
> the correct thing.
>

Oh has it now?

1.8.7 :001 > "Hello world!".display
Hello world!
=> nil
Posted by Fred McArthur (bicycle_brain)
on 2012-08-17 05:29
Thanks for responding. I'll have to think it over before really grasping
why it's done the way it's done.  The example for extending class String
works well enough.

But why it's not done that way in the first place still escapes me.  For
instance, I understand the statement:

### "foo.print" would fail on any object which doesn't have a print
method

But so would reverse, upcase, downcase and all the other methods that
ARE done with dotted notation.  Also if print is a private method, then
why aren't the others? I gather it has a lot to do with selection of
output devices, but isn't the idea of OO based in part on polymorphism,
therefore able to handle that (I'm taking a wild shot here)?

(No doubt I'll get it when I reread the responses again.)

Lots to think about here.  Thanks for everyone's input!
Posted by Fred McArthur (bicycle_brain)
on 2012-08-17 06:23
So initially I said that Thiago Massa's class String extension worked,
and it does to a point.  But not for numbers, at least not without
further mods.

class String
  def print
    puts self
  end
end

"abcd".print  # This works

1.print  # This causes a Fixnum error

because "print/puts expects a string,and it will call to_s automatically
on the argument if it is not."

What's nice about the standard "print" is that conversion is automatic. 
No need to convert a non display number to display type in order to 
print it. Saves a lot of work.

and then there's this:
"Hello world!".display  # This works fine!
Posted by unknown (Guest)
on 2012-08-17 10:51
(Received via mailing list)
Am 17.08.2012 06:23, schrieb Fred McArthur:
> "abcd".print  # This works
>
> 1.print  # This causes a Fixnum error

Of course, since there is no print method in the Fixnum class.
You would have to monkey patch that, too.

> because "print/puts expects a string,and it will call to_s automatically
> on the argument if it is not."

Only the print method from the kernel will do that,
but 1.print tries to invoke the print method from Fixnum.

You would have to define a print method for *all* the classes
that you want to work with.

> What's nice about this is that it's automatic.  No need to convert a non
> display number to display type in order to print it. Saves a lot of
> work.

Please keep in mind that too much monkey patching might be
counterproductive or even dangerous. In your case it does
*not* save you any work, rather the contrary.
And locating errors might get more difficult.

I would *highly* recommend to simply stick with:

print 'abc'
print 1
puts 'result: ' << (1+2).to_s
puts "result: #{1+2}"

(There is no need to reinvent the language.)
Posted by Jan E. (jacques1)
on 2012-08-17 11:07
Fred McArthur wrote in post #1072622:
> But why it's not done that way in the first place still escapes me.  For
> instance, I understand the statement:
>
> ### "foo.print" would fail on any object which doesn't have a print
> method
>
> But so would reverse, upcase, downcase and all the other methods that
> ARE done with dotted notation.  Also if print is a private method, then
> why aren't the others? I gather it has a lot to do with selection of
> output devices, but isn't the idea of OO based in part on polymorphism,
> therefore able to handle that (I'm taking a wild shot here)?

With polymorphism you probably mean that the method acts differently 
depending on the output device. The problem with this is that you 
certainly don't want to hardcode the output logic for some devices into 
your method. This wouldn't make any sense (as Henry already said).

So the method would have to delegate the call to the output object. In 
other words, you need two different methods:

IO#print(String) --> the actual output logic
String#show(IO)  --> for printing a string-like object on a device

This actually makes a lot of sense. And as you've already seen, that's 
pretty much what Ruby does with "print" and "display".

The only criticism I can think of is that the top level "print" should 
be removed. This would force you to either write $stdout.print("Hallo") 
or "Hallo".display instead of using this pseudo-imperative style.
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.