Defining “callback” through practical examples
A callback is a function that happens after we call another function, but the catch is, they’re coupled. However, being coupled in this way doesn’t mean we can’t reuse the callback function.
We can actually sort of “scoop out” the function that we passed into
Array.prototype.forEach in order to make it reusable:
How do we do this in Ruby? And what does “idiomatic” mean?
You’ll hear a lot of Rubyists use the word “idiomatic”. Read:
The idiomatic way to pass arguments to a method in Ruby is such and such…
The idiomatic way to use a “callback” in Ruby is to such and such…
In normal-people speak, you’d say:
The common way to go about doing this is such and such…
Make sense? In other words, there are certain patterns and best practices (solutions) for common problems in Ruby: we call these solutions “idioms”. So, the idiomatic way to use callbacks in Ruby is not to use callbacks at all. Instead, we use blocks.
If you run the above, you’ll see
Hello <name> printed for each name, and at the end it’ll actually return the entire array for you to use.
…is the same as this Ruby:
We can also write the above Ruby in another, shorter way:
So here’s a few things we’ve learned from the above.
1st thing we learned (string interpolation)
Both ways are correct, but the string interpolation notation is more idiomatic.
2nd thing we learned (block syntax)
We also learned two styles, or syntaxes, for writing blocks:
Before we continue, I want to point out a couple style points. Rubyists often use "soft tabs", or 2 spaces instead of just a 4-character tab, only because it different environments it remains readable (see this StackOVerflow question). Also, look at the spacing between the curly braces and pipes. I recommend something to you: Rubocop, which is sort of the de-facto command line tool that tells you if your code looks bad according to the community style guide on GitHub. It's good stuff.
3rd thing we learned (
Enumerable#each == Array.prototype.forEach)
Continuing, we learned that Ruby’s
Array.prototype.forEach (more or less). I have found only one online blog post so far that has made Ruby’s
#each make sense to me. It is by Erik Trautman.
“Scooping out” the block like a reusable callback
There are two common ways to make a reusable “callback” in Ruby. They are the
lambda and the
Proc. There are very subtle differences between the two ways.
By the way, that’s the new syntax for the single-line lambda as of Ruby 1.9 (I have Ruby 2.1 right now, if you’re curious). If you want to do a multi-line lambda, you do:
Now that’s some weird-ass looking syntax, huh? Let’s make it look even weirder:
Proc (short for “procedure”)
undefined, and in Ruby, the undefined arguments will be
- If you call
return <whatever>, the lambda stops, but any method that the lambda is in does not stop. You can use the lambda’s returned value in the rest of the containing method.
- You must pass in all the arguments, or you’ll get an error.
- If you call
return <whatever>, the Proc and any method that it is in will stop. The Proc will return its value to the containing method, and the containing method will also return that same value.
In Ruby, though, as a side note, we rarely use an explicit
return statement. You can just type a value.
…is equivalent to this Ruby:
The ampersand (
This confusing thing basically just makes sure the thing you’re passing into the method is a Proc, sort of. You can read about that in detail on a.blog.about.code.
The above error is basically saying, “why did you pass me an argument? I expected 0 arguments for
&sayHello is not an argument! It is a
Proc, and Ruby does not count
Procs as arguments!
It’s weird, huh? That’s because you can pass in a block at the end of pretty much any method in Ruby, and it won’t see it as an argument, in the typical sense.
Check it out:
We passed in a block at the end, but it didn’t run. But we didn’t get any error either. If we want the block to actually run, we can just say
#block_given? is a native method that returns a boolean (
false) that checks if a block was passed in to the method. We have to use it in the above example, because if we just say
yield instead of
yield if block_given?, then we’ll get an error that says
no block given (yield).
Also, the question mark
? is Ruby’s way of meaningfully letting the user know that the method is supposed to return a boolean. There’s nothing really special about it.
Now, going back to the original error (
error: wrong number of arguments (1 for 0)):
If the above code were Ruby, you’d get an error something like
wrong number of arguments (2 for 1). Once again, I want to emphasize this is because Ruby doesn’t see the
Proc as an argument.
- Use blocks as if they were callbacks.
- Use a
Proc. Remember the two subtle differences between them (number of arguments, and return values).
- Put a
&before the block that you pass into the method, if you pass it in to a native Ruby method that will run a block, like