Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Misunderstanding about Object#tap

Tags:

ruby

Here is my simple test code:

def test_function
  0.tap do |v|
    v += 10
  end
end

p test_function

Why I get 0 here ? I was waiting to get 10.

Update:

class TestClass
    def initialize
        @v = 0
    end

    def inc 
        @v = @v + 1
    end
end

def test_function
    0.tap do |v|
        v += 10
    end
end

def test_function_2
    TestClass.new.tap { |obj|
        obj.inc
    }
end

p test_function
p test_function_2

0

TestClass:0x29244f0 @v=1

like image 382
ceth Avatar asked Dec 15 '22 12:12

ceth


2 Answers

The reason is that the original object is not changed by the += operator. So what happens it that you create a reference to it in the block, change the reference to point to an other object, which is larger by 10 and then return. the value of 0 stays the same.

And thing about it -- this is what you want to happen. The += operator does not change the object it operates on. It returns another different object and assigns the reference to it. This is like:

  v = 0
  v = v + 10

You wouldn't expect 0 == 10 to be true after this, right?

like image 136
davidrac Avatar answered Jan 02 '23 01:01

davidrac


There is additional answer here: The method tap returns the original object. You can use it e.g. for side-effects, like reporting, collecting stats or logging. It doesn't give you extra under-the-hood access to the original object.

v = [0,1,2]
v.tap { |array| array.map { |x| x + 1 } }
=> [0,1,2]

v.tap { |array| array.map! { |x| x + 1 } }
=> [1,2,3]

A variant of your test_function_2 could be used, where an object that you control has an Integer property, even just an array with one element:

def test_function
  [0].tap do |v|
    v[0] += 10
  end
end

p test_function[0]
=> 10

. . . depends what you are trying to do?

like image 21
Neil Slater Avatar answered Jan 02 '23 01:01

Neil Slater