Basically, sometimes you do want to reuse the mutable between function calls, and in those cases it can save a fair bit of code passing it in repeatedly.
def calculate(a, b, c, memo={}):
try:
value = memo[a, b, c] # return already calculated value
except KeyError:
value = heavy_calculation(a, b, c)
memo[a, b, c] = value # update the memo dictionary
return valueYou can use a mutable default argument as an ersatz static variable, e.g. for memoization.
Maybe I misunderstood you, but they are perfectly mutable:
class A
attr_accessor :a
def initialize
@a = []
end
def b x = a
x << 1
end
end
obj = A.new
# => #<A:0x007fd92b2e9850 @a=[]>
obj.b
# => [1]
obj.b
# => [1, 1]
obj.a
# => [1, 1]
If you mean "inline default arguments are not mutable", that's not true either. What is true is that the default argument is evaluated when the function is called, not when it is defined: a = 0
# => 0
x = lambda {|y = (a + 1)| y }
# => #<Proc:0x007fd92b1dae50@(irb):33 (lambda)>
x[]
# => 1
a = 5
# => 5
x[]
# => 6 ruby> class A; end
=> nil
ruby> def foo(bar = A.new); return bar; end
=> nil
ruby> foo
=> #<A:0x00000101985060>
ruby> foo
=> #<A:0x0000010197a6b0>
and get back the same object each time `foo` is called in Ruby.I've even seen a major Python library with this bug (I'm sorry, I don't recall which off-hand). It's really surprising behavior for new Python devs.
>>> def f(x):
... return x + 5
>>> type(f)
<type 'function'>
>>> dis.dis(f.func_code)
2 0 LOAD_FAST 0 (x)
3 LOAD_CONST 1 (5)
6 BINARY_ADD
7 RETURN_VALUE
>>> g = f
>>> g(10)
15
>>> g is f
True
They're just variables in the current scope. If you're quite clever, your brain is already figuring out that has some interesting implications that some libraries use: >>> import socket
>>> socket.gethostbyname('www.google.com')
'74.125.71.103'
>>> socket.gethostbyname = lambda i: '10.0.0.1'
>>> socket.gethostbyname('www.google.com')
'10.0.0.1'
I will not pass judgement on monkey patching like this, just pointing out it's doable. I know for a fact Ruby can as well.Functions just being variables has useful properties when you're doing something like fancy switch/case type things (the readability of this is questionable, but it's cool to look at it, like a Duff's device):
>>> i = 1
>>> { str: func1, unicode: func2, int: func3 }.get(type(i), func1)(i)
in func3
Also consider something like this, which is how decorators work (and they're incredibly useful), sort of like a closure: >>> def maker(i):
... def ret(x):
... return x + i
... return ret
>>> f, g = maker(10), maker(100)
>>> f(5), g(10)
(15, 110)
I'd be surprised if Ruby couldn't do everything I just did.By useful he means mucking up your program in totally unexpected ways.
He's being nice about it being a silly decision to have it behave that way. The entire post reads more like a list of unexpected things that will bite you in the ass.