3

Up until my attempt to answer this question I had always assumed arguments were evaluated from left to right the way we type them. For example we have some method:

def foo(a,b,c)

Is there a way to monitor variable definitions and values in a Ruby program as time passes? If you call foo 0,1,2 how can you prove that variables assigned in the following order?

time 0: a = 0

time 1: b = 1

time 2: c = 2

I realize my example is trivial because I have the same type of argument for a, b, c but it potentially gets muddied up when you introduce default arguments, keyword arguments, and array arguments. If no one knows the answer I would appreciate suggestions on how to determine the answer.

Basically the Ruby equivalent of this.

Community
  • 1
  • 1
Mike S
  • 11,329
  • 6
  • 41
  • 76
  • This is crazy... I googled "ruby call what order are arguments" and ended up here only to find that you are linking to my question from 4 years ago. Just FYI, I think you are talking about parameter assignment order and not argument assignment. https://stackoverflow.com/questions/156767/whats-the-difference-between-an-argument-and-a-parameter – Gerry Sep 20 '20 at 10:40

4 Answers4

5

The ISO Ruby Language Specification says that arguments are bound to parameters in the order in which they appear in the program text. However, the spec is vague about whether that also means that they are evaluated in that order.

The RubySpec, AFAICS doesn't say anything at all about the evaluation order of method arguments.

So, the answer seems to be: there is no guaranteed evaluation order for method arguments. It may be different between different implementations, it may be different between different versions of the same implementation, it may be different between two runs of the same version of the same implementation, it may even be different between two calls to the same method. They may be evaluated in parallel.

You just don't know.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • It's slightly frustrating to not know the definitive order but you're right. Even if you can show with some code that in one example it is/isn't left-to-right, it does not mean that it necessarily always will be. Without an official source I guess we can't know. – Mike S Mar 25 '16 at 15:54
  • i am able to say for certain that across all ruby versions(from ruby1.8.6 to present 2.7.0) the evaluation is the same through out. by running the following script below `rvm use [ruby version] && ruby -ve 'def m(x,y); {same: x==y, ltor: xy}; end; p m(Time.now, Time.now)` for all the ruby versions to date( ruby 2.7.0), you will get the following return value below `{:same=>false, :ltor=>true, :rtol=>false}` where `same` means x and y are evaluated at the same time, `ltor` means left to right, and `rtol` means right to left. – Paa Yaw Feb 27 '20 at 00:39
  • @PaaYaw sorry but it seems the question was using the word "argument" when from the examples Op clearly meant "parameter". – Gerry Sep 20 '20 at 10:55
  • @MikeS: It is completely normal for a spec to *not* specify something. That is actually where performance comes from: not specifying something allows the implementor to choose the most efficient option. For example, in a massive multi-core system with low overhead, it would be possible to evaluate the arguments in parallel. Specifying an evaluation order would deny this optimization opportunity. – Jörg W Mittag Sep 20 '20 at 10:57
3
def arg_test(a=Time.now, b=Time.now)
  puts "left to right" if a < b
end

arg_test #=> left to right
steenslag
  • 79,051
  • 16
  • 138
  • 171
1

Perhaps you can record the initialization of the objects, and see them later in a stack.

module Record
  @@stack = []
  def initialize *; super; @@stack.push(self) end
  def self.stack; @@stack end
end

class String
  prepend Record
end

def foo a, b, c; end

foo(String.new("x"), String.new("y"), String.new("z"))
Record.stack # => ["x", "y", "z"]

In this case, a, b, c are evaluated in this order.

With default value, you can see:

def foo a, b, c = String.new("c"); end

foo(String.new("x"), String.new("y"))
Record.stack # => ["x", "y", "c"]
sawa
  • 165,429
  • 45
  • 277
  • 381
0

For anybody coming here looking for argument assignment order (what the question asks at the time of my post) and not parameter assignment order. I always assumed they were evaluated in the order written until I tried writing some odd code and got a strange result. steenslag gave me an idea of how I could test it:

def arg_test(a, b)
  puts([a, b].inspect '- out of order!') if a > b
end

1000000.times {
  arg_test(Time.now, Time.now)
}

This results in no output. So it seems they are assigned in order and my other code probably just has a bug.

Gerry
  • 10,584
  • 4
  • 41
  • 49