loading...

re: Pros and Cons of Ruby's .tap Method VIEW POST

FULL DISCUSSION
 

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]

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?

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.

code of conduct - report abuse