DEV Community

loading...

Discussion on: Pros and Cons of Ruby's .tap Method

Collapse
jfouse profile image
Joel Fouse

Ran into a quick "gotcha" with .tap that seems worth sharing in case it trips anyone up. As you mentioned above, the documentation says that .tap: "Yields self to the block, and then returns self." This is important to remember, because sometimes we think we're modifying an object when we actually aren't.

For example, let's say we want to add elements from a new array to an existing array:


# using .concat
ary = [1, 2, 3]
ary.concat([4, 5, 6])
#> [1, 2, 3, 4, 5, 6]

# using +=
ary = [1, 2, 3]
ary += [4, 5, 6]
#> [1, 2, 3, 4, 5, 6]

Enter fullscreen mode Exit fullscreen mode

By all appearances they look like they do the same thing; one might suppose += is simply a "syntactic sugar" shortcut for .concat. Besides, += is quicker to type and easy to conceptualize what you're doing, right? Let's try both approaches with .tap:


ary = [1, 2, 3]
ary.tap do |a|
  # do some things
  a.concat([4, 5, 6])
  # do more things
end
#> [1, 2, 3, 4, 5, 6]
# ...as expected

ary = [1, 2, 3]
ary.tap do |a|
  # do some things
  a += [4, 5, 6]
  # do more things
end
#> [1, 2, 3]
# ...wait, what?

Enter fullscreen mode Exit fullscreen mode

Why the difference? Concat does what we expect because it works by appending the new elements to the existing array. But + (and therefore +=) creates a new array with elements from both originals and returns the new one, which we're just throwing away here because we're not assigning it to anything. That means the original array object we tapped from remains unchanged, and that's what .tap then returns or passes on to the next method in the chain.

In short, if you're expecting the tapped object to be modified within the tap block, make sure you're using methods that will actually modify self instead of returning a modified copy.