From: Kristof Bastiaensen Date: 2004-04-27T22:44:11+09:00 Subject: Re: Semantics of Multiple Values (updated RCR) Hi, here is my update RCR. It adds a unify operator := , multiple variables comparison, and some more goodies (:-) TITLE Semantics of Multiple Values ABSTRACT This RCR describes a possible change in semantics of multiple values, by making them equivalent to argument passing. PROBLEM Currently Array's are used as multiple variables, which creates some ambiguous or unclear situations. i.e.: a, b, c = x #where x = [1, 2, 3] PROPOSAL * model ======== This proposal favors the use of multiple values as inherent to the language, rather than a separate data-type. It is based on the observation that returning (multiple) values is similar to passing arguments. This is even more apparent when using continuations. for example: def mymethod(a, b) return a + 1, b + 2, a end x, y, z = mymethod(1, 2) would mean this def mymethod(a, b) callcc { |cc| cc.call(a + 1, b + 2, a) } end #(the following isn't legal syntax, but just to show the meaning) mymethod(1, 2) |x, y, z| basicly the expression: x, y, z = will pass the multiple arguments to the given variables like in function argument passing. The difference with argument passing in functions are the following: * no new scope is created, bound variable are just replaced * there is no strict checking if the number of arguments is correct * new constructs and method ============================ - new constructs: ------------------ because Array's are now treated different from multiple arguments I would like to suggest the following construct: *[a, b] = [1, 2] meaning the same as a, b = *[1, 2] but useful inside blocks that pass arrays. Optionally allow: *[a, *b] = [1, 2, 3] => a == 1, b == [2, 3] or perhaps with hashes: *{:a => c, :b => d} = {a: 1, b: 2} => c == 1; b == 2 *{:a => c, *b} = {a: 1, b: 2, c: 3} => c = 1, b = {:b => 2, :c => 3} these constructs would work both in argument passing and variable assignment. - new method: -------------- (for now called values) method returning multiple values as an array def values(*params) params end values(1, 2, 4) => [1, 2, 4] * variable assigment examples: =============================== def multi return 1, 2, 3 end #multi may be replaced everywhere with (1, 2, 3) x = multi => x == 1 x, y = multi => x == 1, y == 2 x, y = multi, 3 => x == 1, y == 3 (x, y), z = multi, 4 => x == 1, y == 2, z == 4 (*[x, y], z), p = ([1, 2, 3], 4, 5), 6 => x == 1, y == 2, z == 4, p == 6 * calling to functions ======================= I would recommend the following behavior: * when passing multiple values from one function to another, spread the values in a relaxed way (don't check the number of parameters). When used with an explicit list, use strict checking. def func1(a) a end func1(multi) => 1 func1(1, 2, 3) => error def func2(*a) a end func2(multi) => [1, 2, 3] def func3(a, b) [a, b] end func3(multi) => [1, 2] * when passing nested multiple variables just pass the first element func3(multi, 4) => [1, 4] func3((1, 2), 4) => [1, 4] #or maybe signal error? not sure about this one func2(multi, 4) => [1, 4] func2(*values(multi), 4) => [1, 2, 3, 4] * allow nested multiple values in method definitions def func4(a, (b, c), d) [a, b, c, d] end func4(1, 2, 3) => [1, 2, nil, 3] #maybe raise an error? func4(1, (2, 3), 4) => [1, 2, 3, 4] func4(1, multi, 4) => [1, 1, 2, 4] def func5(a, *[b, c], d) func5(1, 2, 3) => [1, 2, nil, 3] func5(1, [2, 3], 4) => [1, 2, 3, 4] func5(1, (2, 3), 4) => [1, 2, nil, 4] # (what will happen here is only the 2 from (2, 3) will be passed # to func5, converted into an Array, and passed to b * Making argument passing and multiple assignment more similar =============================================================== There may be other features of multiple assignment that could be taken from argument passing, for example hash parameters(?). Other may not be appropriate (i.e. blocks). example: def func6 return 4, b: 5, c:6 end a, b:, **keys = func6 => a == 4; b == 5; keys == {:c=>6} * Comparing multiple values ============================ Multiple values may be compared using comparison operators. For example: a, b, (c, d), e == 1, 2, (3, 4), 5 would mean a == 1 && b == 2 && c == 3 && d == 4 && e == 5 with the exception that in the first, all the expressions will be evaluated. It would return false if the number of arguments to the left aren't equal to the ones to the right. It would be also useful to be able to compare with the operator ===, so multiple values can be compared inside case statements. When comparing one value with multiple values, return true if at least on matches. 1 === (1, 2, 3) => true #same as 1 === 1 or 1 === 2 or 1 === 3 case a, b when 1, 2 puts "is 1 and 2" when (2, 3), 4 puts "is (2 or 3) and 4" end * Unify operator ================= This section descibes a unify operator, that works like a combination of comparison and assignment (similar to unify in Prolog). The expression a, b, c := multi would assign the lefthand values, if unbound (or nil?) to the corresponding righthand value, and if bound (or an immediate value) compare if they are equal. If one of the comparisons would return false, no assignment will be done. examples: a, b, c := 1, 2 =>true (a == 1, b == 2, c == unbound or nil) a, b := 1, 3 =>false (a == 1, b == 2, c == unbound or nil) a, c := 1, 2 =>true (a == 1, b == 2, c == 2) def amethod return true, 1, 2, end true, x, y := amethod =>true (x == 1, y == 2) ANALYSIS The changes I proposed here provide a consistent way of using multiple values, by using (mostly) the same semantic model for assignment as for argument passing, without losing any power to the language. The major drawback is that programs written using the old semantics may not work correctly, (but the current behavior will change anyway?). It also descibes some other extensions to the language that may or may not be useful. IMPLEMENTATION These changes will have to be made to the core of the language. Some features I described above may be to hard to implement or decrease performance (nested multiple values in method definitions, assigning variables from hash elements), and therefor disallowed.