Fighting Ruby's bad fame

P

Phil Tomson

do a lot of things in volume and I know with ruby this would bite me.

2) Localized modification of built-in classes.
While arguably a feature of ruby, the truth is that at a large scale
this leaves the door open to chaos. VFX is a dynamic industry. Oa
normal production, you have 10 to 50 technical directors, many of them
writing and updating scripts (and all of them with different amount of
programming knowledge), and usually doing so without a true
coordination. This is not chaos, but it is a true benefit for the
production and the facility. Lacking namespaces or so is an issue in
ruby and I know such an issue would not even arise for python, so this
is also a minus for the language.

can you elaborate? Ruby has namespaces defined by modules.

As far as modifying builtin classes goes, we're hearing a lot in
this thread about how this is a problem. Personally, I don't see it as
long as you're not redefining predefined methods.

However, if you really don't want anyone modifying built-in classes then
you can use freeze:

irb(main):001:0> Array.freeze
=> Array
irb(main):002:0> class Array
irb(main):003:1> def foo
irb(main):004:2> "foo"
irb(main):005:2> end
irb(main):006:1> end
TypeError: can't modify frozen class
from (irb):3

I suppose you could even define some sort of make_safe method that
iterates
through all of the built-in classes/modules (and even ones that you've
added) and calls freeze on them, thus
saving you from having to individually freeze each one - it wouldn't be
that hard to do.

In fact, here's the code for doing it:

def make_safe
ObjectSpace.each_object(){|o|
if(o.class == Class || o.class == Module)
o.freeze
end
}
end

call make_safe and all of the current Class and Method objects in
ObjectSpace are frozen. Maybe there should be an RCR for a commandline
argument that actually does this when Ruby is invoked.

However, I consider being able to (judiciously) add methods to built-in
classes one of the great features of the language and I really doubt that
when people are considering Ruby for a project that very many of them come
to the fact that classes are always open and decide that it's a deal breaker.
The kinds of 'dangerous' things you can do with Perl (for example) haven't
seemed to hinder it too much over the years.
3) Better english documentation.
There's still the need for good english docs of all the libraries that
are shipped with ruby. There's also the need for documenting what
constructs make ruby a tad faster or slower. There's also the need
for a solid "cookbook" a la perl or python, albeit some web docs are a
step in the right direction.

4) Better multiplatform support and libraries.
Ruby is still somewhat inmature in dealing with scripts that would be
run across multiple platforms. Determining the platform, OS and OS
version at runtime is still a pain in ruby compared to python or perl.
A library for this is neeed asap.

Perhaps, but I've written lots of cross-platform scripts. How hard is it
to do:

case PLATFORM
when /win/
#do Windows things
when /nix/
#do Unix things
else
#whatever
end

?

How does Perl/Python handle this any easier?
6) Better emacs support. Tools to do automatic refactoring.
The use of "end" as a delimiter makes it still hard in emacs to
determine to which block it corresponds, unlike a bracket.

This works in vim. I'm not an emacs user, but if they could get it
working with a vim script, I'm sure it can be done in emacs lisp.
Some easy
macro is needed for that. In some way, ruby seems to suffer from this
a tad like python, which makes refactoring important to keep functions
within a page long. Thus, automatic refactoring tools would also be a
godsend (specially if they work with emacs and vi like the python
library refactoring does).

7) Multiple inheritance.
While this is something imo a scripting language can indeed do without
(and I kind of like that), it does present a big headache if you want
to integrate a C/C++ library that does depend on multiple inheritance.

It's not so bad at this point. Swig 1.3.20 (for automatically
wrapping C++ code) handles this by considering each of the base classes as
modules that get mixed-in to the derived class.
For more details, see:

http://www.swig.org/Doc1.3/Ruby.html#n19

If you prefer not to use Swig, you could still borrow this idea. Swig
works quite well, however and it's a lot faster than 'hand' wrapping.

Phil
 
M

Mauricio Fernández

: It's simply the danger of a namespace clash. At least if short and
: simple names are used as i have seen as a suggestion in a ruby book.
:
: At least library developer should avoid this as hell.

Smart developers know when this kind of thing is appropriate and
when it isn't. Ruby is a language for smart people, and one of
the facets of its design is letting the Ruby programmer make that
judgement call.

"It's a language for smart wannabe (i.e people trying to be
smart)." - matz

;-)

--
_ _
| |__ __ _| |_ ___ _ __ ___ __ _ _ __
| '_ \ / _` | __/ __| '_ ` _ \ / _` | '_ \
| |_) | (_| | |_\__ \ | | | | | (_| | | | |
|_.__/ \__,_|\__|___/_| |_| |_|\__,_|_| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

Q: How does a Unix guru have sex?
A: unzip;strip;touch;finger;mount;fsck;more;yes;umount;sleep
-- unknown source
 
P

Phil Tomson

Yes, but not good enough.
Compared to Perl or Python, include is missing the ability of Perl's use or
Python's import command to just bring only certain parts of the module into the
current namespace.
In perl, you can do:
use Module :)func1 :func2);

You can do this. Remembering back to my Perl days (a distant, fading
memory), I never did this. It was always just 'use Module'. (but that's
not to say that being able to limit which functions are brought in isn't
useful and often warrented).
and in python:
from Module import :func1, :func2

And only those two functions/classes/variables get imported into the current
namespace. Ruby's include seems to be an all or nothing proposition,
unfortunately.

Perhaps this warrants an RCR. Right now, Matz is very open to RCR's
and he's asking for a lot of them to be submitted for Ruby 2.
http://rcrchive.net/

Perhaps there could be optional arguments to require and include so that
methods can be listed (as you show above):

require "foo", :func1, :func2

include Foo, :func1, :func2

It's probably possible to do something like this now, I'd have to play
with it a bit to see what I can come up with.
Correct. I do see this as a benefit of ruby, too, but not without some form of
leash in the language to have the ability to keep the addition/modification
local or under the programmer's control.
I've been using ruby for about 1 week & 1/2 and already run into that. I
created String#expand_tabs() function in a module of mine only to find a
similar (but less efficient implementation) of that function in another popular
ruby library. Freezing the class is no use as I WANT to be able to do that for
my own class.
This to me already points out the huge issue of name clashes. In this case, it
is not biggie as both functions do the same, but... it already sent shivers
down my spine.
While you can do such things in perl or python, too; those things are usually
discouraged while in the ruby way, they are not. And as I mentioned before,
the include's of the other languages are better than ruby's as the coder can
load the modifications selectively, too.
Personally I like this phisolophy of ruby, but I NEED to have the ability to
easily keep changes to base classes local to my class or in an easy way to
revert them.

This is kind of what David Black's Ruby behaviors does.
http://uweb.superlink.net/~dblack/ruby/behaviors/
Well, that's the issue isn't it? What is #whatever?

Whatever is whatever else you think your script might run on. Practically
speaking, you mainly need to cover: Windows, MacOSX, Linux and *BSD.
Now, better yet, can you tell me how can I distinguish the OS name and version
in ruby, since PLATFORM does not tell me that?
Those are the things missing from libraries and having at least ONE library
where all possible alternatives are listed would be nice.
In the case of perl, this is handled more or less as bad as ruby, but there are
a bunch of libraries that have been tested along the years that have a huge
list of these possibilities for you to learn from.
In the case of python, PLATFORM is much more simplified, as os.name can be only
one of 'posix', 'nt', 'dos', 'os2', 'mac', 'ce' or 'riscos'. No surprises
there and no need for any regex'es. Nothing weird like "i386-win32".

But what if new OS's are developed? And how do I distinguish between OSX
and OS9 if all I get is 'mac'? Do they have an os.version as well?
Given that OS9 and OSX are such different beasts, I don't want them lumped
together.

PLATFORM is set at compile time in Ruby (as in compile time of the Ruby
sources). I suppose it would be nice if there were some way to actually
query the OS in such a way as to determine the OS name and version.
(perhaps another RCR is in order). How does Python setup the os
datastructure?
Yes, I'll take a look at writting an emacs macro for this. But still,
refactoring tools would be much more useful.

Sure, but as you say Python and Perl don't have those sorts of tools
either. I guess this is one place where static typing make it easier for
the editor: it provides some idea about types of variables for
refactoring. But consider: Refactoring is already a lot easier in
dynamically typed languages like Ruby (or Python or Perl). The FreeRIDE
folks are planning some support for refactoring ASAIK.
Yes, been playing with swig1.3. Like it a lot, but it is missing some needed
stuff for ruby.
For example, ALL #define constants are translated to ruby. Even those that are
invalid ruby constants like those starting with underscores (and there does not
seem to be a way to ignore those, as with methods).

I wonder how hard this would be to fix.
There seems no way to have swig automatically expose protected methods, which
is quite needed.
There is no %rubycode available like the %pythoncode directive in python mode
to create additional .rb code files.
And I'm not sure mixin's really can deal with multiple inheritance 100%
properly. Sure, methods are easy. I am concerned more about dealing with
variables (both class and instance), thou, as from my quick glance at it,
modules seem to be somewhat limited in this aspect.


Definately some limitations to this method of emulating MI. I'm not sure
these differences can ever be completely bridged. Ruby supports single
inheritance with mixins, whereas C++ supports multiple inheritance with
all of the associated problems that come with it. Swig makes a good
attempt to bridge the gap, but of course there will never be a perfect
bridge between the two.

Phil
 
G

Gavin Sinclair

I've been using ruby for about 1 week & 1/2 and already run into that. I
created String#expand_tabs() function in a module of mine only to find a
similar (but less efficient implementation) of that function in another popular
ruby library.

Could you send me the efficient implementation? :)
This to me already points out the huge issue of name clashes. In this case, it
is not biggie as both functions do the same, but... it already sent shivers
down my spine.

You DO test code before deploying it into a critical productiohn
system, don't you? What's so bad about name clashes? You find them,
fix them, and move on. Same as any other kind of coding error. And,
short of clashing with a library whose explicit purpose is to extend
the built-in classes, I bet you don't even find any name clashes. :)
While you can do such things in perl or python, too; those things are usually
discouraged while in the ruby way, they are not.

Not true. Some people like them, some don't. Good libraries take
care not to impinge on public namespaces unreasonably. Experimenting
with modified classes is quite encouraged in Ruby. Overdoing it is
not.
And as I mentioned before, the include's of the other languages are
better than ruby's as the coder can load the modifications
selectively, too.

I don't like the sound of that "feature". A well-designed library
should be loaded in full. In OO languages especially, you either load
a class or you don't; you don't pick and choose methods.

Larger libraries may have several classes in a module, which can be
selectively loaded by loading different files (require "foo/a",
require "foo/b").

Selective importing of pure module functions *may* have some benefit,
but still sounds pretty average.
Personally I like this phisolophy of ruby, but I NEED to have the ability to
easily keep changes to base classes local to my class or in an easy way to
revert them.

At the risk of re-igniting an old flamewar, no you don't. Not until
you prove that you need it anyway. At the moment it's mere paranoia.
No offense intended; we've all been there I'm sure.

I don't rule out the possibility of you proving that you need the
feature. And I think that namespaces/behaviours would be nice. You
have certainly hit on a controversial Ruby feature, and interesting
discussion on it is always ... well, interesting.
Otherwise, it IS a deal breaker.

Take responsibility for your own code, and scrutinise third party code
you use. Run RDoc on them and see if any built-in classes appear.
Use Ruby to prototype a solution and then ask what's wrong with it.

Cheers,
Gavin
 
G

GGarramuno

Gavin Sinclair said:
Could you send me the efficient implementation? :)

Sure, here it is. This implementation will perform a tiny bit slower
on short documents that are mainly separated by a single tab, but it
should perform dramatically faster in longer documents, specially
those with multiple contiguous tabs (last test, being the most
pathologial one). Of course, the best thing would be to move these
methods from ruby onto C, if you ask me:


require "benchmark"

class String

# Return new string with all tabs set to spaces
def new_expand_tabs(n=8)
n = n.to_int
raise ArgumentError, "n must be >= 0" if n < 0
return gsub(/\t/, '') if n == 0
return gsub(/\t/, ' ') if n == 1
h = self.dup
while h.gsub!(/^([^\t\n]*)(\t+)/) { |f|
val = ( n * $2.size - ($1.size % n) )
$1 << (' ' * val)
}
end
h
end



def old_expand_tabs(n=8)
n = n.to_int
raise ArgumentError, "n must be >= 0" if n < 0
return gsub(/\t/, "") if n == 0
return gsub(/\t/, " ") if n == 1
str = self.dup
while str =~ /\t/
str.gsub!(/(^.*?)\t/) { |s|
# Our match starts at the beginning of a line, so we count the
# characters before the tab, and expand the tab to the next multiple
# of N spaces.
width = $1.size
gap = n - (width % n)
# Replace the tab with 'gap' spaces.
$1 + (" " * gap)
}
end
str
end


end


### Dummy tests
n = 100000
s1 = "\t\t\t\t\t\tadssaa\t\t\t\t\t" * 1000
s2 = "\t\t\t\t\t\tadssaa\t\t\t\t\t\n\n\t\t\n" * n
s3 = "adssaa\t\t\t\t\t\nxxxxxxx\t\t\nyy\n" * n
s4 = "adssaa\t\t\t\t\txxxxxxx\n" * n
s5 = "\na\ta\t\a\ta\t\a\n" * n
s6 = "\na\tthis\t\is\ta\t\test\tof\thow\tgood\ttabulation\tis.\n" * n

t1 = ''
t3 = ''

Benchmark.bm { |x|
x.report("n ") { t3 = s2.new_expand_tabs(); }
x.report("o ") { t1 = s2.old_expand_tabs(); }
}

if ( t3 != t1 )
t3.gsub!(/\t/,'-')
t1.gsub!(/\t/,'-')
puts "new error!",t3,".",t1
end

Benchmark.bm { |x|
x.report("n ") { t3 = s3.new_expand_tabs(); }
x.report("o ") { t1 = s3.old_expand_tabs(); }
}

puts "new error!" if ( t3 != t1 )

Benchmark.bm { |x|
x.report("n ") { t3 = s4.new_expand_tabs(); }
x.report("o ") { t1 = s4.old_expand_tabs(); }
}

puts "new error!" if ( t3 != t1 )

Benchmark.bm { |x|
x.report("n ") { t3 = s5.new_expand_tabs(); }
x.report("o ") { t1 = s5.old_expand_tabs(); }
}

puts "new error!" if ( t3 != t1 )

Benchmark.bm { |x|
x.report("n ") { t3 = s6.new_expand_tabs(); }
x.report("o ") { t1 = s6.old_expand_tabs(); }
}

puts "new error!" if ( t3 != t1 )

Benchmark.bm { |x|
x.report("n ") { t3 = s1.new_expand_tabs(); }
x.report("o ") { t1 = s1.old_expand_tabs(); }
}

puts "new error!" if ( t3 != t1 )

On my system:


user system total real
n 8.422000 0.080000 8.502000 ( 8.602000)
o 53.096000 0.420000 53.516000 ( 53.858000)
user system total real
n 4.807000 0.050000 4.857000 ( 4.887000)
o 20.560000 0.151000 20.711000 ( 20.830000)
user system total real
n 2.423000 0.050000 2.473000 ( 2.483000)
o 13.049000 0.080000 13.129000 ( 13.270000)
user system total real
n 9.293000 0.060000 9.353000 ( 9.403000)
o 9.324000 0.080000 9.404000 ( 9.434000)
user system total real
n 26.097000 0.250000 26.347000 ( 26.498000)
o 40.969000 0.391000 41.360000 ( 41.529000)
user system total real
n 14.721000 5.678000 20.399000 ( 20.570000)
o 228.529000 3.365000 231.894000 (235.588000)


What's so bad about name clashes?

Nothing much. Just that it is something I wouldn't be giving any
thought with python or perl, but that you tell me I may have to watch
out it for in ruby.
And,
short of clashing with a library whose explicit purpose is to extend
the built-in classes, I bet you don't even find any name clashes. :)

Not sure I agree. Do you know how many vector, matrix and so forth
libraries are out there? All of them more or less incompatible with
each other?
I don't like the sound of that "feature". A well-designed library
should be loaded in full. In OO languages especially, you either load
a class or you don't; you don't pick and choose methods.

Personally, I'm not too interested in choosing methods, but I am in
choosing classes.
Also, think of it negatively, as I do. What if the library you have
to use was not well designed? What if this is found out only in the
middle of the project? What if the person maintaining the library has
no time on their hands to fix it (or worse, refuses to change it for
some other reason)? Do I just hack into the library and start
maintaining my own incompatible version which I would then have to
keep updating every time a new update to the master library is
released?
Between being tied and just going on my own tangent, I'd rather take
the comprise of just picking and choosing what I want to use...
without hacking into the original code.
Will I'll be using that feature often? Heck, I hope never! But I'll
sleep well at nights knowing that if I ever HAVE to, that feature is
already there.
At the risk of re-igniting an old flamewar, no you don't. Not until
you prove that you need it anyway. At the moment it's mere paranoia.

Paranoia is good in programming. Keeps you sane later on.
And this fear must be a plague, as searching for solutions in the ruby
world, I've seen at least 3 or 4 proposals trying to address this same
issue, including:
- behaviors
- matz's namespaces
- profile method visibility

That tells me that: a) it is a problem already or b) it is enough of a
potential future problem to merit a solution.

Personally, I prefer the namespaces approach, but any of them standard
in the language would likely be better than having none if you ask me.
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: Fighting Ruby's bad fame"

|And this fear must be a plague, as searching for solutions in the ruby
|world, I've seen at least 3 or 4 proposals trying to address this same
|issue, including:
|- behaviors
|- matz's namespaces
|- profile method visibility
|
|That tells me that: a) it is a problem already or b) it is enough of a
|potential future problem to merit a solution.

Or c) it is a good puzzle for your brains. ;-) At least, my namespace
idea is originally not for the problem you've described.

matz.
 
G

Gavin Sinclair

Sure, here it is. [...]

Thanks. That'll make 0.3.

Nothing much. Just that it is something I wouldn't be giving any
thought with python or perl, but that you tell me I may have to watch
out it for in ruby.

That's right: nothing much. And many of the things you have to watch
out for in P* are not an issue in R*. There's no perfect language,
just different sets of compromises.
Not sure I agree. Do you know how many vector, matrix and so forth
libraries are out there? All of them more or less incompatible with
each other?

Even if that's true, how many are you going to use at once?
Personally, I'm not too interested in choosing methods, but I am in
choosing classes.
Also, think of it negatively, as I do.

I prefer to think positively when programming, and Ruby rewards that!
What if the library you have to use was not well designed? What if
this is found out only in the middle of the project? What if the
person maintaining the library has no time on their hands to fix it
(or worse, refuses to change it for some other reason)? Do I just
hack into the library and start maintaining my own incompatible
version which I would then have to keep updating every time a new
update to the master library is released?

An ugly situation, but that's a LOT of what-ifs.
Between being tied and just going on my own tangent, I'd rather take
the comprise of just picking and choosing what I want to use...
without hacking into the original code.
Will I'll be using that feature often? Heck, I hope never! But I'll
sleep well at nights knowing that if I ever HAVE to, that feature is
already there.

Like I said before, since Ruby is missing a few features you look for
in an ultimate solution (especially in a particular domain), try it as
a prototyping solution. I'd be interested to see what problems you
hit and how you deal with them.
Paranoia is good in programming. Keeps you sane later on.
And this fear must be a plague, as searching for solutions in the ruby
world, I've seen at least 3 or 4 proposals trying to address this same
issue, including:
- behaviors
- matz's namespaces
- profile method visibility
That tells me that: a) it is a problem already or b) it is enough of a
potential future problem to merit a solution.

It tells you what you want to hear. Likewise for me :)

Cheers,
Gavin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,787
Messages
2,569,629
Members
45,332
Latest member
LeesaButts

Latest Threads

Top