03:04 pm, 3 Apr 06
checking types in a dynamically-typed language sucks
/usr/lib/ruby/1.8/rexml/parsers/basepars er.rb:133:in `stream=': Tempfile is not a valid input stream. It must be either a String, IO, StringIO or Source. from /usr/lib/ruby/1.8/rexml/parsers/basepars er.rb:100:in `initialize'
No small part of my lack of enthusiasm for hacking this week was running into this sort of thing (well, and being fuzzy-headed from trying sleep aids). In fact, i wound up running into exactly this, in my attempts to work out StringIO and GSL::Matrix marshalling. A StringIO object io (or Tempfile, for that matter) has TYPE(io) != T_FILE, so hah-hah, you can't use it as a File from the C bindings.
Pragmatically, it all makes sense, but it makes the little idealist in me very sad.
(I'm not hating on the duck typing, for anybody else reading, but that C extensions don't have an abstracted I/O layer which does the duck-typing for them....)
But it still bothers me. If you look at it from a naive ruby programmer's perspective (let's say... mine), suddenly ruby's duck typing isn't universal. You've got a thing that write()s like an IO and read()s like an IO, but it's not an IO when used in this particular interface. And it's somewhat annoying and concerning.
From the C side: given the way the FILE* is being used externally, one could solve this. Just change from a streams-based interface to a wrapped write()/read() one. That is, replace OpenFile GetOpenFile(VALUE io); FILE* GetWriteFile(OpenFile fptr); with int CheckWriteFile(VALUE io); int WriteToFile(VALUE, char*, off_t, size_t). Then, run a quick switch() inside WriteToFile() to check what kind of value was passed in. If it's a "true" File object, the fastpath isn't much slower. If it's a duck, run things through ruby. Not terribly efficient, but it's what the user intended. It's also slightly more awkward for the C programmers, but not so much more so (in fact, it saves them having to unpack some local variables, replacing them with retval checks).
I've been trying to figure out what i'm missing here--it seems like this is how it'd be implemented. Then again, i'm still meditating on the absence of ++/--, so i have a long way to go before ruby enlightenment.
Seems to me that in a duck-typed language the library should be checking capability rather than type. It should be doing
if myStream.can("read")
(or whatever) and notif myStream isa Stream
.Of course, this gets hairy when you have special case types that aren't objects, which is why I was so keen on modelling everything as an object in my programming language.
if my_stream.respond_to? :read
instead of
if my_stream.is_a? Stream