On Friday, January 14, 2011 07:34:04 am Jonas P. (zimbatm) wrote:
this is a general census to get developer feedback. Please post the
issues you encounter when developing in ruby. This can range from
syntax issues, to library support, documentation, or anything that is
a roadblock when developing in ruby.
In no particular order…
I wrote a long post because I don’t have time to write a short one. To
summarize:
RubyGems:
- Play nice with other package managers
- Better installers for end-users
- gems/rvm/bundler confusion
Library management:
- Better namespacing
- Better handling of monkeypatching
Multithreading:
- Threads are too primitive
- Actors would be cool, but have issues
Wish list:
- Lisp-style Macros.
- Ruby-in-Javascript.
And now the rant:
Right now, Rubygems is both the biggest win and the biggest blocker for
newbies. I haven’t been keeping track, so I find I now have to learn RVM
and
Bundler just to keep up with the best practices for deploying a Rails
app. And
how do these interact with the system Ruby? If I install a system
package that
depends on a particular version of Ruby, will it get the version it
expects,
or will it get my default RVM version?
Unfortunately, I don’t have a good answer for this, and I don’t really
know
much about the solutions that do exist, but nearly every one I’ve tried
has
been very messy in many ways. Installing gems to my home directory is
just not
a good idea.
Another thing that I’d like to see more work on – though I think
alright
solutions exist, especially for JRuby – is the ability to distribute
Ruby
clients. It seems like there are two solutions, one for developers, and
one
for end-users. The developer solution is to build it as a gem, but it’s
not
reasonable to ask an end-user to install Ruby, Rubygems, and then your
gem.
The end-user solution is to build it as a giant monolithic file which
includes
all relevant gems. Surely there’s a middle ground – a nice, intuitive
installer, but one which actually sets up a system Ruby and Rubygems for
the
user, so that multiple programs don’t have to separately download and
install
the same libraries.
And Rubygems itself – while it seems it’s capable of understanding
reverse
dependencies, I want to be able to automatically clean stuff that I
don’t need
anymore. Gemfiles are kind of a decent start, but I’m thinking something
along
the lines of “manual” vs “automatic” in Debian, or even better, Gentoo’s
/var/lib/portage/world. In particular, I want to be able to install a
gem just
to see what it does, and if I don’t like it, uninstall that gem and then
clean
up every gem that was only installed because it was depended on.
RVM’s gemsets are kind of cool, but they seem to be a workaround for not
having that functionality. The only other reason to have them seems to
be when
developing a gem.
One more thing about gems: How do we know what a good gem is? I love how
decentralized it is now, at least in theory, but occasionally it means I
need
to go through two or three gems and try each of them before I find
either that
none of them does what I want, or some oddly named and maybe
semi-obscure gem
is perfect.
Moving away from rubygems and installation, and to semantics…
A bit more namespacing would be awesome. The current practice of just
dumping
something you hope is appropriate into the global namespace of constants
is
bad enough. It’s worse when we start monkey-patching other stuff.
I don’t know how big a problem this actually is. It doesn’t seem to
actually
cause problems in practice, so maybe I’m overreacting. Still, I like how
Perl
and Python handle this, where there’s a difference between loading a
library
and importing it into your namespace.
Namespacing constants aggressively shouldn’t be too much of a burden, as
Ruby
has the same shortcuts JavaScript does. Take JRuby. Sure, you can do
this:
import java.util.PriorityQueue
But you can also do this:
pq = java.util.PriorityQueue
That’s locally-scoped, which means you aren’t polluting any namespace
past the
end of the current block. Or you could do something like:
module MyMod
PQ = java.util.PriorityQueue
…
end
In both of these cases, you aren’t affecting anything else in the same
runtime. But if you ‘import’ it globally, you’ve declared it, well,
globally.
Point is, I don’t think we should be afraid to do stuff like this,
though we
could use just a bit of sugar to make it easier:
module FooCo
module LibFooVersion213
class Foo
…
end
end
end
Ok, yes, people will get sick of typing that, but again:
foo = FooCo::LibFooVersion213::Foo
Problem solved.
Monkey-patching is harder. Is there a way we can scope things like
Activesupport’s hacks? Stuff like:
‘bird’.pluralize
5.days.ago
foo.should be_valid
I love these things, but I wish there was a way I could declare them to
be
only available in a certain context or scope. This seems like it needs
some
language support to be really effective – we could fake it now by
mixing them
in and out of the core classes as needed, but that would affect any
other
threads, so it kind of defeats the purpose if you’re using
multithreading.
Essentially, the idea here is that when multiple libraries inevitably
end up
using the same name for something, we should be able to work around it.
When
they don’t, we don’t want it to be a burden. (I think JQuery is a
perfect
example of this done right.)
Let’s get some proper multithreading, to start with. It’s 2011. There is
really no excuse for a GIL. Either drop it or mainstream COW GC –
otherwise,
JRuby becomes the only real option for multicore on Ruby.
How about some higher-level threading constructs, too. There are a few
gems
which add interesting ideas… However, there are limits to what you can
do
with Ruby as it is, and these are partly due to the fact that it’s not
Erlang.
Again, I’m not sure of the best way to do this, but…
We have objects, and objects can, in principle, completely encapsulate
their
state. We’ve actually got that implemented properly – just look at
send,
method_missing, the fact that we need accessors, etc. This would be a
perfect fit for the actor model, and it’s something I tried to do once,
but
never really finished – but essentially, why not “just” make all Ruby
objects
actors?
Aside from performance issues, but I was going to do a proof-of-concept
and
ignore those…
Well, I didn’t quite finish it, but one problem I ran into in the design
phase
is the fact that there’s entirely too much Ruby code which wouldn’t work
at
all with this kind of design. For example:
foo.a += 1
Sure, you can override foo.a and foo.a= and either add a giant mutex, or
push
them off to a separate thread. Either way, you’re still going to have a
race
condition between getting the value of a and setting it again. Something
like
this might work:
foo.increment! :a
If you were to view each object as an actor, and each method call as a
message, stuff like that works very, very well. I still want to finish
my
idea, because I think you could get a lot of mileage out of new objects
which
were designed from the start to be actors. I think you could do that
without a
lot of language overhead. I had a proof-of-concept partly done.
But that still doesn’t change the fact that this will never work:
foo.some_hash[:a] += 1
Even after you make sure foo is an actor, and the some_hash it returns
is some
sort of proxy object that serializes all access, you still have the race
condition between the call to [] and the call to []=.
I think that this kind of problem is exactly the kind of thing Ruby
should be
concerned with. The typical Ruby battle cry has been “Hardware is
cheaper than
programmers!” Well, alright, here’s a machine with a few thousand cores.
How
is Ruby going to handle that? Can the language itself be fixed, or does
there
need to be a new one that combines the best of Ruby with the best of
(say)
Erlang? (In other words, is something like Reia the way forward?)
I don’t know, but while it’s sort of workable now, it’s probably the
single
language flaw that keeps me up at night.
There is one other that just annoys the purist in me…
Is there any way we can get anything like Lisp sexps? As I understand
it, we
have a few Ruby parsers, but nothing standardized to the point where I
can ask
the runtime itself to give me a parse tree for a given expression. The
closest
I could find was various implementation-specific things and ruby_parser,
which
is cool but buggy, missing a few of the 1.9 features.
But that I can live without. My rationale is, pretty much any syntactic
ugliness can be worked around with a preprocessor if it’s really
bugging me
(see lazibi), and Ruby is pretty anyway. It’s semantic ugliness that’s
tricky.
Ruby doesn’t have GOTO, it doesn’t have pointers or malloc, it’s done
away
with pretty much every bit of low-level nastiness associated with
single-
threaded programming – but the Thread system makes me feel like I’m in
C
again, where the slightest mental mistake could lead to my entire
program
screwing up in unimaginably arcane ways.
A final point: Browsers are getting fast enough that we should be able
to do
Ruby in Javascript. And not a server-side implementation, either – I
want the
equivalent of JRuby. But this isn’t really a limitation of Ruby, it’s a
limitation of browsers that we’d be working around. The above rants are
things
I actually feel are broken about Ruby as it is today.