2018年2月24日 星期六

Mutation in Ruby, it tells why more and more people like pure functional (immutable function)

Some mind blown examples:


```rb
[1] pry(main)> list = Array.new(5, "foo")
# => ["foo", "foo", "foo", "foo", "foo"]
[2] pry(main)> list[4].clear
# => ""
[3] pry(main)> list
# => ["", "", "", "", ""]
```

因為每個 default foo 都是同一個 object


以及

```rb
[1] pry(main)> master_list = Hash.new([])
# => {}
[2] pry(main)> master_list[:evens] << 2
# => [2]
[3] pry(main)> master_list[:odds] << 3
# => [2, 3]
[4] pry(main)> master_list
# => {}
[5] pry(main)> master_list[:evens] <<= 4
# => [2, 3, 4]
[6] pry(main)> master_list
# => {:evens=>[2, 3, 4]}
[7] pry(main)> master_list[:odds] <<= 5
# => [2, 3, 4, 5]
[8] pry(main)> master_list
# => {:evens=>[2, 3, 4, 5], :odds=>[2, 3, 4, 5]}
```

一樣的原理,每個 default array 都是同一個 object

一個避免這樣的做法是用 proc 確保每個 default 值是不同 object,例如:

```rb
Array.new(5) { "foo" }
Hash.new { [] }
```

ex:

```rb
[1] pry(main)> Array.new(5) { "foo" }
=> ["foo", "foo", "foo", "foo", "foo"]
[2] pry(main)> a = Array.new(5) { "foo" }
=> ["foo", "foo", "foo", "foo", "foo"]
[3] pry(main)> a[2].clear
=> ""
[4] pry(main)> a
=> ["foo", "foo", "", "foo", "foo"]
[5] pry(main)>
```


另外還有更有趣的「以你為 freeze 了是吧?Boom~!」

```rb
[1] pry(main)> CONSTANT = ["foo"]
# => ["foo"]
[2] pry(main)> CONSTANT << "bar"
# => ["foo", "bar"]
[3] pry(main)> CONSTANT.freeze
# => ["foo", "bar"]
[4] pry(main)> CONSTANT << "baz"
# RuntimeError: can't modify frozen Array
# from (pry):4:in `__pry__'
[5] pry(main)> CONSTANT[1] << "baz"
# => "barbaz"
[6] pry(main)> CONSTANT
# => ["foo", "barbaz"]
```

這是因為 freeze 是 freeze CONSTANT 這個 Array 的 object,沒有 freeze 裡面的 string,所以 string 還是 mutable

(Ice Nine gem for deep freezing)

為了拯救 developers 於水深火熱的加班 debug 地獄中,不需要 mutate 的時候就不要 mutate,寫每個 function 都想一下「這真的有必要 mutate 嗎?」

沒有留言:

張貼留言