Forum: Ruby-core StringIO is not IO?

Posted by Hongli Lai (Guest)
on 2007-10-12 20:06
(Received via mailing list)
According to irb,

 >> StringIO.is_a?(IO)
=> false

This seems illogical to me. Is this intentional? If so, why?
Posted by Eric Mahurin (Guest)
on 2007-10-12 20:16
(Received via mailing list)
On 10/12/07, Hongli Lai <h.lai@chello.nl> wrote:
> According to irb,
>
>  >> StringIO.is_a?(IO)
> => false
>
> This seems illogical to me. Is this intentional? If so, why?

Since StringIO doesn't use IO to base its implementation, it is not an 
IO.

Why does it matter?  If you use standard duck typing, it shouldn't.
Class hierarchy doesn't matter with duck typing.  Classes are just an
implementation detail with duck typing.  You are best off not looking
#is_a?, #respond_to?, etc.  Just assume the object can do the methods
you'll be using and use.

Eric
Posted by Eric Mahurin (Guest)
on 2007-10-12 20:19
(Received via mailing list)
On 10/12/07, Hongli Lai <h.lai@chello.nl> wrote:
> According to irb,
>
>  >> StringIO.is_a?(IO)
> => false
>
> This seems illogical to me. Is this intentional? If so, why?

BTW, I asked this same question when I found StringIO.  The answer
enlightened me to the beauty of duck-typing.
Posted by Hongli Lai (Guest)
on 2007-10-12 20:38
(Received via mailing list)
Eric Mahurin wrote:
> Since StringIO doesn't use IO to base its implementation, it is not an IO.
> 
> Why does it matter?  If you use standard duck typing, it shouldn't.
> Class hierarchy doesn't matter with duck typing.  Classes are just an
> implementation detail with duck typing.  You are best off not looking
> #is_a?, #respond_to?, etc.  Just assume the object can do the methods
> you'll be using and use.

Well, some of my code can write its output to a file (specified by a
filename string) or to an IO object. So my code first checks whether
output.is_a?(IO), which is not the case.

RMagick seems to do the same thing. I tried making RMagick write its
output to a StringIO, but that failed because it thought it isn't an IO
object. I spent quite some time on debugging these two problems, and I
guess that am not the first one.

Finally, when one thinks about it logically, StringIO *should* be an IO.
There's even the word 'IO' in its class name. So why not do an 'include
IO' in StringIO? That would solve a lot of problems and make the class
hierarchy more logical.
Posted by Berger, Daniel (Guest)
on 2007-10-12 21:02
(Received via mailing list)
> >  >> StringIO.is_a?(IO)
> You are best off not looking #is_a?, #respond_to?, etc.  Just 
> assume the object can do the methods you'll be using and use.

This is actually the one case where duck typing burned me. In the
spreadsheet library I was using IO#fileno, so that users could pass
their own IO objects if they wanted. Well, it turned out some people
were trying to stream the results to their web browsers using StringIO.
Does StringIO respond to fileno? Yes. Does it work? No. It returns nil.

Just thought I'd share that little anecdote with y'all. :)

Regards,

Dan


This communication is the property of Qwest and may contain confidential 
or
privileged information. Unauthorized use of this communication is 
strictly
prohibited and may be unlawful.  If you have received this communication
in error, please immediately notify the sender by reply e-mail and 
destroy
all copies of the communication and any attachments.
Posted by Evan Phoenix (Guest)
on 2007-10-12 21:10
(Received via mailing list)
On Oct 12, 2007, at 11:37 AM, Hongli Lai wrote:

> a filename string) or to an IO object. So my code first checks  
> make the class hierarchy more logical.
>

StringIO is not a subclass of IO because it doesn't share anything
with IO implementation wise. It simply provides the same interface as
IO does. It's a technical problem for having it be a subclass as all
IO objects have some kind of backing with the OS.

What would the backing object with the OS be for StringIO? With every
other IO object, on unices, it's a file descriptor. Lowlevel things
in ruby that take an IO object (select, etc) do so because the
require the ability to extract this backing object from the IO object
and hand it to the OS.

As for including IO, well you can't include a Class. If you want
something akin to this check, create a module, say called IOLike.
Include it in IO and StringIO, then do output.kind_of?(IOLike).
Better would be to test for the interface you want, rather than the
type though. So in your case, output.respond_to?(:write).

  - Evan Phoenix
Posted by Sam Roberts (Guest)
on 2007-10-12 21:14
(Received via mailing list)
On Sat, Oct 13, 2007 at 03:37:36AM +0900, Hongli Lai wrote:
> filename string) or to an IO object. So my code first checks whether 
> output.is_a?(IO), which is not the case.
> 
> RMagick seems to do the same thing. I tried making RMagick write its 

So does RexML. You see same symptoms when you use open-uri, and then try
to pass the returned "not quite IO" object to RexML. Its unfortunate the
std libs aren't a better example of duck-typing. Search around, I think
_why and a few other blogs have talked about how difficult it is to do
streaming decoding of XML coming over HTTP with RexML because of this.

I think the idiomatic ruby way would be for objects that are "like" an
IO object to implement #to_io(), so libs could check for #to_io(). This
would be analogous to #to_str(), etc.

Sam
Posted by Eric Mahurin (Guest)
on 2007-10-12 21:17
(Received via mailing list)
On 10/12/07, Hongli Lai <h.lai@chello.nl> wrote:
> filename string) or to an IO object. So my code first checks whether
> output.is_a?(IO), which is not the case.

I suggest that you either use another way to figure out how you should
treat an argument (like number of arguments or a flag) or don't use
"multi-methods" (split the single method into multiple so that you
don't have to look at the "type" to determine what to do).
Multi-methods really aren't a good thing in ruby and clash with the
concept of duck-typing.

> RMagick seems to do the same thing. I tried making RMagick write its
> output to a StringIO, but that failed because it thought it isn't an IO
> object. I spent quite some time on debugging these two problems, and I
> guess that am not the first one.

It sounds like it has the same problem that your code does.  If you
don't do duck-typing you'll restrict flexibility.

> Finally, when one thinks about it logically, StringIO *should* be an IO.
> There's even the word 'IO' in its class name. So why not do an 'include
> IO' in StringIO? That would solve a lot of problems and make the class
> hierarchy more logical.

IO is a class.  You can't include a class.  Even if you could, you
wouldn't want StringIO to have all of the data-structures needed to
implement IO.

To get what you want, you'd really IO to be based on an abstract class
that had no implementation (like Fixnum and Bignum are based on
Integer which has not implementation to be used directly).

Again, your best bet is to just steer clear of #is_a?, #respond_to?,
etc.  If you want these to make multi-methods, just say no - use
multiple methods instead.

Of course the ruby core itself doesn't follow the duck typing
philosophy all the time (actually most of the time it doesn't).  Take
Regexp for example.  Regexp is completely tied to String.  You can't
make something that looks like a String (like say the opposite of
StringIO - an IOString, or a rope - tree structure for a string) and
have it work with Regexp.
Posted by Michal Suchanek (Guest)
on 2007-10-12 23:11
(Received via mailing list)
On 12/10/2007, Eric Mahurin <eric.mahurin@gmail.com> wrote:
> > Well, some of my code can write its output to a file (specified by a
> > filename string) or to an IO object. So my code first checks whether
> > output.is_a?(IO), which is not the case.
>
> I suggest that you either use another way to figure out how you should
> treat an argument (like number of arguments or a flag) or don't use
> "multi-methods" (split the single method into multiple so that you
> don't have to look at the "type" to determine what to do).
> Multi-methods really aren't a good thing in ruby and clash with the
> concept of duck-typing.
>

I also hit this particular case. The question "Is it an IO or a
filename?" can be solved by detecting the read/write method that would
be used to get/store the data. If it's not there you can try and use
the object as filename.
But in general writing methods that do different things depending on
the kind of data they receive is quite hard in ruby.

Thanks

Michal
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.