Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
New Ruby 1.9 Features, Tips & Tricks (igvita.com)
76 points by LiveTheDream on Feb 21, 2011 | hide | past | favorite | 26 comments


Also there are lots of other lovely methods on Array and Enumerable:

  * Array
    * new method:
      * Array#keep_if
      * Array#repeated_combination
      * Array#repeated_permutation
      * Array#rotate
      * Array#rotate!
      * Array#select!
      * Array#sort_by!
    * extended methods:
      * Array#{uniq,uniq!,product} can take a block.

  * Enumerable
    * New methods:
      * Enumerable#chunk
      * Enumerable#collect_concat
      * Enumerable#each_entry
      * Enumerable#flat_map
      * Enumerable#slice_before
http://rbjl.net/27-new-array-and-enumerable-methods-in-ruby-...


keep_if is like select! but slightly different; each_entry is like each but slightly different; flat_map is like map but slightly different.

In no case are you saving more than a few keystrokes, yet any ruby coder who works with other ruby coders now has more language "surface area" to learn in order to read ruby.

This, guys, is how Perl became Perl. Oy.

keep_if is the truly maddening WTF eye-bleeding rage-inducing make-a-normally-calm-rubyist-very-stabby one.

First off, it's a mutator but the name doesn't end in a bang. It should be keep_if! but for some random reason isn't. Yet another example of how the pleasant idea of using question marks for bool-returning methods (file.exists?) and exclamations for mutators (file.delete!) is just that -- a neat idea (very!) inconsistently applied and thus pointless. Ruby should use method punctuations consistently or get rid of them, for heaven's sake.

keep_if is also egregious because it is the THIRD verb for doing basically the same thing. We already had find_all and select (synonyms) and now select! and, presumably, find_all!. So your workaday ruby coder gets to pick between find_all, select and keep_if to pick items out of an array based on truthiness of a callback. He gets to figure out which two are true synonymous and which is a near-synonym , and then wonder/research why you would ever want to use the near synonym.

Which brings us to the third reason keep_all is egregious, which is that brings very, very little to the table. It is just like select! except it always returns the object, whereas select! returns the object except when no changes are made, in which case it returns nil. In this regard select! behaves like your other ruby mutators (string.sub! and string.gsub!, for example). Now this behavior, which keep_if was invented to circumvent, can be annoying in a very narrow range of circumstances, for example when you stick a mutator on a var at the end of a method and take it for granted that it will always return the thing it's mutating, as your return value, until one day your method is randomly returning nil and you have to delete a single character from your method to fix this. But 99/100 times the whole reason you are using a mutator is that you don't care about the return value just the side effect (the mutation); if you cared about the rv you would just use the non mutator.

So keep_if is "fixing" a "problem" with an extremely minor scope and with an extremely easy programmer fix.

Also, keep_if is a one off.

In other words:

If it's problematic that mutators in ruby return nil on non mutation, then the Correct Solution, i.e. the non aneurism inducing one, is either to change how ALL mutators work, say in 2.0, or to invent some kind of new standard alternate syntax like say a double bang or something. Because there are a LOT of mutators that work the way select! works.

The way NOT to fix this (supposed) mutator problem is to invent a one off solution for one particular type of mutator, a solution that invents /a whole different name/ for the method it is altering, thus setting the precedent of sprawling the language. By the apparent reasoning behind keep_if we now need something called like, oh I don't know, "rewrite_all" to similarly be like-but-not-quite-like "gsub!". And "rewrite" to do same for "sub!", and so on and so on for the other mutators until ruby is getting nearly as bloated and unreadable as.... Perl. (Which I love and used to code in but, let's face it, has some mmmmmhowdoyousay -- issues? -- with readability.)

So keep_if is a one-off inelegant language bloating solution to a non problem. That's egregiousness three.

Egregiousness four is that keep_if was introduced at the same time as the thing it was invented to fix -- the (apparently sometimes) undesirable behavior of select!. select! was introduced.... at the same time as keep_if. Ugh. Facepalm.

So it's not like I'm going to stop using ruby or anything, and it's not like anyone would care if I did, but I am going to have disagree with you a little here about these new methods being "lovely," in the sense that methods that will eventually send ruby spiralling into, at best, a /near brush/ with morbid, Perl-esque language obesity are not "lovely" /in my humble opinion./

Anyway sorry for the rant! (And Ruby 1.9 does, in general, rock the Casba, previous nothwithstanding.)


Does feel a little like someone vandalised your car doesn't it. At this rate someones going to be writing "ruby, the good bits".


Most of it seems pretty nice (although I can hardly believe Ruby 1.8 does not have named groups), but...

    tip "Ruby 1.9 supports 4 ways to call a proc!"
      f =->n {[:hello, n]}
      p f[:ruby] # => [:hello, :ruby]
      p f.call(:ruby) # => [:hello, :ruby]
      p f.(:ruby) # => [:hello, :ruby]
      p f === :ruby # => [:hello, :ruby]
wtf? Why are they writing that like it's a good thing?


Ruby is a language that encourages developing internal DSL's for a lot of problems. Having different syntaxes for the same thing gives you more flexibility in defining DSL's. For instance, the "f[:ruby]" form lets you define DSL's that intermix procs and hashes. The "f.(:ruby)" form is probably the new standard shorthand, but the explicit "f.call(:ruby)" is clearly there for backwards compatibility.

If you want a language where there's only one way to do it, I think there's a language with that exact motto.


How do all of these DSLs interact in close proximity? Ruby seems to do DSLs via a lenient lexer, rather than an explicit "quoting" mechanism, which means there isn't a way to locally specify a context for its idioms.


It doesn't turn out to be a big problem since you don't generally mix idioms in the same file. In a Rails app, it's pretty easy to figure out which files are evaluated by which DSL. If you're using HAML, anything in app/views with a .haml extension is HAML. Anything under /spec is Rspec. Capfile, Gemfile, and Rakefile are self explanatory. routes.rb uses the Rails routing DSL.


All right. Thanks for the polite and detailed response.


"If you want a language where there's only one way to do it, I think there's a language with that exact motto."

Be that as it may, in Ruby you should also follow conventions, write readable code, use the most obvious choice when possible and avoid being unnecessarily clever. Most of the core principles that Python promotes should also be heeded when writing Ruby code. Ruby gives you the flexibility to choose different options, but that doesn't mean that the flexibility should be abused.


And it isn't abuse until it is used in a specific abusive way. Having the options available to you isn't abusing it.


Doesn't it "bloat" the language though?


That is subjective. You can feel it is bloated, but I would call it handy and expressive.


Conventions vary from DSL to DSL. The flexibility doesn't have to be used by the application programmer to be useful. Think of how gemfiles or Rspec would look without optional parentheses in method calls. Even though parens are usually the way to go, sometimes you want different conventions in your DSL.


Agreed. If there is anyone here who knows the justification for the latter two, please explain.

Edit: found the explanation for the last one: http://www.aimred.com/news/developers/2008/08/14/unlocking_t... So, basically, it's just using it as a normal case equality operator and you generally shouldn't use it in place of a regular #call.


Proc#=== allows you to do this:

  def multiple_of( n )
    lambda{ |x| x.modulo( n ).zero? }
  end

  case 10
  when multiple_of( 3 ):
    puts "3!"
  when multiple_of( 4 ):
    puts "4!"
  when multiple_of( 2 ):
    puts "2!"
  end
Basically you can now run code when checking case equality (===).


Since we're talking Ruby 1.9 here, that code won't even run :-( .. drop the colons and you're good though! :-) But if we're really talking Ruby 1.9, you could also go with:

    ->(x){ x % n == 0 }


Or, more simply:

    case $stdin.gets.chomp
    when lambda {|matchobj| matchobj.count("!") > 1 }
      puts "Quitcher yelling!"
    when "hi"
      puts "Hi yourself"
    end


Conspicuously missing is the brilliant new Proc.curry method!

For those that don't know currying is a method that returns another Proc with one fewer argument, so you can do: multiply = Proc.new { |x, y| x * y } multiply_by_five = multiply.curry(5) multiply_by_five.call(3) # => 15


Useful little thing: Object#tap. But Mostly, no mention of Fibers? :)


"stabby proc"

-- sweet name for the new -> proc syntax.


or perhaps better yet: "stabda"


Named captures in regular expressions looks really nice! I'm especially excited to play with Oniguruma.


Named captures are a great way to document your regular expressions. Don't use the "#" notation to comment on what the regex is doing, just name the group instead!

.Net has had them for a long, long time and they are AWESOME!

It'd be cool to see Ruby get something like a MatchEvaluator, too

http://msdn.microsoft.com/en-us/library/system.text.regulare...

A MatchEvaluator lets you much more complicated regex find replace. Currently, find/replace only lets you re-order or remove groups or insert static text. A MatchEvaluator lets you handle what to replace the match with code - meaning you can call a webservice that will replace MM/DD/YY with the equivalent Mayan/Persian/Lunar date.


Speaking of named captures.. Something I didn't mention in the blog post, but a rather handy feature:

ruby-1.9.2-p136 :011 > /My name is (?<first>\w+) (?<last>\w+)/ =~ "My name is Ilya Grigorik"

ruby-1.9.2-p136 :012 > first

"Ilya"

ruby-1.9.2-p136 :013 > last

"Grigorik"


> Currently, find/replace only lets you re-order or remove groups or insert static text.

I must be misunderstanding you, but that's obviously incorrect.

    "foo 1 bar 2".gsub(/\d+/) {|m| m.to_i * 2 }
1.8 also. Does MatchEvaluator bring more to the table?


that looks exactly like a match evaluator, so it looks like ruby does have that ability. Awesome.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: