What I would like in Ruby 3
Ruby development is hurtling along these days. In 2012 1.9 finally became the norm, and in 2013 we got both 2.0 and 2.1. All of these versions brought small but steady feature improvements.
Here are some thoughts on how I would like the language to change, possibly appropriate for Ruby 3 or Ruby 4.
Methods consistently as first-class objects
Ruby has fantastic support for anonymous functions and closures. But they have some problems that I think can be improved to make them more intuitive and consistent.
- procs — lambda cares about arity, proc doesn't. lambda doesn't take over the return value of the outer method, proc does. I guess this simply means that procs are a special-purpose construct (I don't think one is "higher-level" than the other, as neither can be used to implement the other, right?). What are procs useful for, and what is their history? I don't know. At the very least I think we should stop mentioning procs alongside lambdas in guides and documentation. As far as I'm concerned they could be removed from the language, or maybe moved into a gem with more description of what they are useful for, or maybe their functionality should be expanded (could they becomes Erlang processes?) .
- syntax for methods objects — if object
foo
has methodbar
, in order to get the method as a lambda that can be passed around or inspected, one must invokefoo.method(:bar)
. I would prefer that this instead be done javascript style:foo.method
returns the method object,foo.method(...)
invokes the method. - invocation syntax for lambdas — If we have the above change, now we can also simplify the invocation syntax for lambdas. If
foo
is a lambda, it is called withfoo.call(...)
. I would prefer it simply befoo(...)
. - return value of
def
— This used to benil
, in ruby 2.1 it became a symbol representing the method name. I think it should be the method object. - syntax for passing a lambda as a block — currently if you have method
mymethod
which take a block, and lambdamylambda
which has the same arity as the block whichmymethod
expects, you can passmylambda
as the block like so:mymethod &mylambda
. I think we should not need the&
. Methods which accept blocks should just be methods which accept lambdas, and the block syntax should just be syntactic sugar. However, the limitation to this idea is that if there is a method which both...- accepts optional lambdas as arguments (to compose them into other structures, invoke them conditionally, etc.)
- has an optional block
...then it would be possible to have an ambiguous invocation. But I bet there could be easy enough intelligent assumptions here, along the same lines as how ruby currently handles an options
hash as the final argument (I haven't thought this through completely thought, maybe it's impossible to support all cases).
Methods which accept multiple blocks
I wrote about this here: http://code.jjb.cc/passing-multiple-blocks-to-a-ruby-method
Better namespacing
problems:
- http://stackoverflow.com/questions/6208206/ruby-thinks-im-referencing-a-top-level-constant-even-when-i-specify-the-full-na
- http://stackoverflow.com/questions/6254496/is-it-possible-to-give-a-sub-module-the-same-name-as-a-top-level-class
I want two things.
::Foo
should mean "get top-levelFoo
, and if there isn't one, blow up. Do not go looking for it somewhere else."Foo::Bar
should mean "get the constantBar
which is inside the namespaceFoo
, and if there isn't one, blow up. Do not go looking for it somewhere else."
I haven't done a deep dive into the reasons for Ruby's current behavior so maybe what I propose will introduce new problems.
more explicit DSL support
lisp and scheme have syntactic macros. You have the ability to create DSLs with arbitrary syntax. Ruby has amazing metaprogramming and lambda/closure/block support, but the DSLs still have to be tied to the same level of abstraction as the rest of the language (methods and symbols). It would be great to be able to define DSLs which have completely different symbols and grammar. Consider a Gemfile:
before
source "https://rubygems.org"
gem "puma"
gem 'rails', '4.0.2'
group :development do
gem "growl"
gem "letter_opener"
end
after
source "https://rubygems.org"
puma
rails 4.0.2
group development
growl
letter_opener
end
In short, Gemfiles are all about gems and groups, so let those things be first-class syntactic constructs. This example isn't great because a Gemfile is already a standalone file, so it kind of just seems like I'm defining a better yml or something. Consider the state_machine DSL:
before
class Registration < ActiveRecord::Base
state_machine initial: nil do
event :activate do
transition [nil, :active] => :active
end
event :cancel do
transition [nil, :active] => nil
end
end
def mymethod
# ...
end
after
class Registration < ActiveRecord::Base
state_machine
activate
[nil, active]→active
end
cancel
[nil, active]→nil
end
end
def mymethod
# ...
end
Now events and states are first-class citizens in the state_machine DSL syntax. Also I'm using → to denote transition. Crazy!
More control flow / concurrency constructs?
For this one I'm not sure what should be in the core language, std lib, or stay in 3rd-party land. But it would be nice to see heavily-used constructs become first-class citizens in Ruby, in the same way that case
is syntactic sugar for a common use of if
/elsif
/else
. One clear candidate is state machines. Others might be concurrency constructs like those provided by the concurrent-ruby project.
Am I a crazy dreamer? Or crazy enough to dream?
Let me know what you think, either in the comments or on twitter!