Lambdas and '&' in Ruby
Tags: ruby, beginners
Published: 2020-12-24 18:41:58

`places.map(&:city)`

I've seen this pattern a lot in Rails and have wondered what exactly it's doing.  In researching it, I learned about lambda functions and procs in Ruby, and that in turn led to last week's post about [currying functions in Ruby](https://dev.to/dhintz89/functions-creating-functions-ruby-edition-1j1a), which relies on basic lambda functionality.  Going a bit further, I started looking into the beforementioned pattern, now knowing a bit more about lambdas.  At first, I thought I'd understood it, but then I realized that it was more complicated than it initially seemed.  The part that was most confusing to me was whether or not the lambda call was supposed to be a symbol, so this is a quick guide for when to use a symbol in this pattern and when not to, as well as an explanation of why.

####Here's the TL;DR version if you're not interested in the investigation and the why:

| Use Symbol | Don't Use Symbol |
|:--:|:--:|
| Method **belongs to the object** you are iterating on | Using a Lambda you've created |
| Iterating on array of objects and operating on a property | |

###Okay now let's look at how I got here.

First thing, let's create a lambda function for use throughout this guide.  If you read last week's post, this will look very familiar:
```
half = ->(numerator) {
    if numerator % 2 === 0
        return numerator / 2
    end
    return "#{numerator} can't be halved evenly"
}

puts half.call(10)
  # => 5
puts half.call(3)
  # => "3 can't be halved evenly"
```
This pattern is super common for iterators, so we'll want a couple of arrays too:
```
numarry = [0, 1, 2, 3, 4]
textarry = ["a", "b", "c"]
```
Alright, so what now?

Say we want to half each of the integers in numarry.  We should be able to use '&:' in an iterator to call our lambda like this, right?
```
puts numarry.map(&:half)
```
Wrong! You get `undefined method 'half'` error!

The reason is because `half` is a lambda, not a symbol, so you need to change it to:
```
puts numarry.map(&half)
  # => [0, "1 can't be halved evenly, 1, "3 can't be halved evenly", 2]
```

That makes sense (and seems obvious), so then what's with the symbol pattern at the top?  I can't just convert my method to a symbol, so it's not just a matter of preference. Something else must be going on.

I tried a different method, but this time I decided to use a built-in method instead of a local one that I created:
```
puts textarry.map(&upcase)
```
And I got an `undefined local variable or method 'upcase'` error!  So, just for fun, I tried it as a symbol:
```
puts textarry.map(&:upcase)
  # => ["A", "B", "C"]
```
And voila - it worked!  But the question is *why*?

We know that everything in Ruby is an object, ultimately inheriting from the global class `Object`.  When you define a method, it's defined with an object as its owner.  You can see what the owner of a given method is by using the #owner method:
```
def double(num)
  num * 2
end

puts method(:double).owner
  # => Object
```
but what about our lambda method at the top?
```
puts half.class
  # => Proc
```
So our lambda is not on the same object as the `Integer` that we are calling it on.  This gives me an idea!  If we think back to the original pattern: `places.map(&:city)` the symbol is a property of the `Place` class, similar to how `upcase` is technically a property of the `String` class.  Meanwhile, our poor lambda is out there on its own and not associated to anything.  There's the difference!  Time to test the theory.  If I'm correct, then by adding a method to an existing class, I should be able to pass it as a symbol to my iterator.

```
# modify Integer class to include double method
class Integer
  def double
    self * 2
  end
end

puts numarry.map(&:double)
  # => [0, 2, 4, 6, 8]
```

It worked!  After all this investigating, I finally came to the simple guidelines below.

| Use Symbol | Don't Use Symbol |
|:--:|:--:|
| Method **belongs to the object** you are iterating on | Using a Lambda you've created |
| Iterating on array of objects and operating on a property | |

As a bonus, I gained a stronger understanding of how Ruby works under the hood, which is always a good thing!