Home  Forums  Reviews  Guides  Newsgroups  Register  Search 
Thread Tools 
Kristof Bastiaensen 


 
JeanHugues ROBERT
Guest
Posts: n/a

Multiple Values, Assignments and *Unifications*
This RCR is really nice. The multiple assignment reminds me of Prolog's "unification" mechanism. What about some further generalization ? Why not a generalized "assign" operator ? Syntax: assign term1, term2 [, term3 [, ...]] And of course an "unify" operator, Syntax: unify term1, term2 [, term3 [, ...]] The difference between "unify" and "assign" is only when some lvalue exists already. In that case, unify checks that the previous value is equal to the new value. If not, no assignment is done at all. Whereas "assign" always assigns. Another difference is that "assign" is a short match (vs greedy for unify), i.e. assign [a,b], [1,2,3] # 3 ignored. i.e. assign [a,b,c], [1,2] # c ignored, not assigned anything, not even nil. The operator = is kept as it is today, leftterms = right_term would be equivalent to assign [leftterms], rightterm Result def mymethod(); return 1, [2], 3 end # Returns an Array a, b, c = mymethod # => 3, a == 1, b == [2], c == 3 assign [a,[b]], mymethod # => 2, a == 1, b == 2, c unchanged assign [a,[],c], mymethod # => false, [] instead of [2] p unify [a,[2],3], mymethod # => true p unify r, mymethod # => [1,[2],3] p unify [a,_,_], mymethod # => 1, _ means "ignore me" Add tail recursion optimization and you become functional. I guess that with some callcc(), backtracking could come too. A key benefit of this proposal is that it does not change the current semantic of returned values. As a result it is fully backward compatible with existing source code. Would this solution solve the issue that your RCR solves ? JeanHugues BTW: It is often said that operator = is unusal because it does not apply to an object. It could, we would need a BoundVariable class, close to a Bindind, but referencing a specific variable. At 21:24 26/04/2004 +0900, you wrote: >Hi liszt, > >I read on Matz' Ruby2.0 slides that the semantics >of Multiple Values will change. Are these already >decided? Here are my ideas about the subject. >I want to post it as a RCR, but I would like to have >your opinion, or criticism first. Also I am not >very familiar with the RCR mechanism. >It is already gotten to a quite large and complicated document. > >Thanks, >Kristof > >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 seperate datatype. It is >based on the observation that returning (multiple) values >is similar to passing arguments. This fact is even more clear >when using continuations. > >for example: > x, y, z = mymethod(a, b, c) > > def mymethod > return r1, r2, r3 > end > >would mean this > > def mymethod > callcc { cc cc.call(2, 3, 4) } > end > > #(the following isn't legal syntax, but just to show the meaning) > mymethod(a, b, c) x, y, z > >basicly the expression: > x, y, z = <expression returning multiple arguments> >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 construct and method >========================== > > new construct: > >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 usefull inside blocks that pass arrays > > 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) > > variable asignment > >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] > >* when passing nested multiple variables just pass the first > element > > def func3(a, b) [a, b] end > > 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 that could be passed from arguments >passing to multiple assignment, for example hash paramaters? >Other may not be appropriate (i.e. blocks).  Web: http://hdl.handle.net/1030.37/1.1 Phone: +33 (0) 4 92 27 74 17 




JeanHugues ROBERT 


 
Kristof Bastiaensen
Guest
Posts: n/a

Hi,
this looks very interesting. I particulary like the hash passing mechanism. I will add it to my RCR. It would then work also inside method definitions: def params({:this => a, :that => b}) [a, b] end params( {:this => 1, :that => 2, :dummy => 3} ) => [1, 2] Maybe it would be too hard to implement, or decrease performance, but that is not for me to decide! > Examples of possible rules: > > {:a=>a, :b=>b} = f(x) > # provided f(x) returns hash, h > # h[:a] and h[:b] assigned to a, b > > y.a = f(x) > # provided f(x) returns object x with accessor a > # assign x.a to y.a > This wouldn't work, because y.a = f(x) is already valid syntax, meaning call y.a= with the value of f(x) > y.a, y.b = f(x) > # provided f(x) returns object x with accessors a, b > # assign x.a, x,b to y.a, y.b > Neither does this, because it would mean pass the multiple values from f(x) to the methods y.a= and y.b= 




Kristof Bastiaensen 
Kristof Bastiaensen
Guest
Posts: n/a

Hi,
On Tue, 27 Apr 2004 03:00:23 +0900, JeanHugues ROBERT wrote: > Multiple Values, Assignments and *Unifications* > > This RCR is really nice. > The multiple assignment reminds me of Prolog's "unification" mechanism. > What about some further generalization ? > > Why not a generalized "assign" operator ? Syntax: > assign term1, term2 [, term3 [, ...]] > And of course an "unify" operator, Syntax: > unify term1, term2 [, term3 [, ...]] > > Result > def mymethod(); return 1, [2], 3 end # Returns an Array > a, b, c = mymethod # => 3, a == 1, b == [2], c == 3 > assign [a,[b]], mymethod # => 2, a == 1, b == 2, c unchanged > assign [a,[],c], mymethod # => false, [] instead of [2] > p unify [a,[2],3], mymethod # => true > p unify r, mymethod # => [1,[2],3] > p unify [a,_,_], mymethod # => 1, _ means "ignore me" > This looks very interesting, I hadn't thought so far. (I also don't know so much about Prolog). From the point of view of syntax, it would be hard to implement, because assign [a, [b]], mymethod is already valid syntax, and would have to make assign and unify a special construct or operator. This may be not so clear. I cannot say much more about it since I am not into prolog. > A key benefit of this proposal is that it does not change > the current semantic of returned values. As a result it is > fully backward compatible with existing source code. The current semantic will change anyway (see also Matz slides about Ruby 2.0), because it has some ambiguities. for example: arr = [1, 2, 3] a, b, c = arr #currently a == 1; b == 2; c == 3 #should be a == [1, 2, 3]; b == nil; c == nil > > Would this solution solve the issue that your RCR solves ? > I think a unify operator would be a different addition than what I described in the RCR. > JeanHugues > > BTW: It is often said that operator = is unusal because it does > not apply to an object. It could, we would need a BoundVariable > class, close to a Binding, but referencing a specific variable. Yes, I also think that would be nice. Yet another RCR? > > >  > Web: http://hdl.handle.net/1030.37/1.1 > Phone: +33 (0) 4 92 27 74 17 




Kristof Bastiaensen 
Dan Doel
Guest
Posts: n/a

Hi.
I thought since you said you don't know about Prolog, I'd say something about it and unification, since it's interesting. Feel free to ignore this, though. Prolog is a logic programming language, and as such, doesn't have functions perse. Instead, it has predicates, which are either true or false. So, for example: plus(0, 0, 0). plus(0, 1, 1). plus(1, 0, 1). Are three predicates that are true. The first one means 0 + 0 = 0, the second means 0 + 1 = 1, and the third means 1 + 0 = 1 (at least, it could mean that. It could mean many things, really). Now, with Prolog programs, you can define what's true for constants, like above, but that doesn't get very interesting. More interesting is defining what's true for variables. So for example, you can define Pythagorean triples like so: ptriple(X, Y, Z) : X2 is X*X, Y2 is Y*Y Z2 is Z*Z Z2 is X2 + Y2. Now, when you write ptriple(3, 4, 5)? Prolog unifies X2 with X*X and so on, and finally it checks that X*X + Y*Y = Z*Z, and finds it's true, so it prints "yes" or something like that. However, unification is more powerful than that. With unification, you can specify variables in your ptriple "call" and Prolog will search for values that make it true (assuming ptriple is written correctly, which it is). So, you can do: ptriple(3, 4, Z). And it will determine values of Z for which ptriple is true. Similarly, you can do ptriple(3, Y, 5). And it will determine values of Y for which ptriple is true. This is quite a bit different from all the procedural and functional languages that I personally know, because in all of those, you have to specify which arguments are inparameters and which are outparameters beforehand, but with Prolog, if you write your predicates properly, any variable can be an in or out parameter. Having similar functionality in Ruby would be interesting. I don't know if it's feasible, since unification algorithms aren't particularly easy to write, and I don't know if it would fit with the other aspects of the language. The proposed unification seems like a subset of the full functionality of unification anyway, though, so that might be feasible. The other proposition here is pattern matching, which is also in existing programming languages. Haskell has pattern matching. For example, lists can be built like so: a : list Where a is an element, list is a list, and ':' is the cons operator. However when writing list processing routines, you don't need to take a list and call head and such. What you can do is: f (xs) = ... And because of Haskell's pattern matching, if you pass something that is a cons of an item and a list, the item gets assigned to x, and the rest of the list gets assigned to xs. You can also do more complicated things where you assign the whole list to a variable, but still assign heads and tails to other variables and so on. I don't know if Matz would want to take the time to implement such functionality, but it can be done. Anyhow, that's it for my language theoretic tangent of the day. If any of this was interesting, I might suggest learning a little about Prolog or Haskell. The Art of Prolog is purportedly good (I have it, but haven't gotten around to reading it yet), and there are several pretty good Haskell tutorials on the internet. Have a nice day.  Dan 




Dan Doel 
Simon Strandgaard
Guest
Posts: n/a

Dan Doel <(EMail Removed)> wrote:
[snip] > The other proposition here is pattern matching, which is also in existing > programming languages. Haskell has pattern matching. For example, > lists can be built like so: > > a : list > > Where a is an element, list is a list, and ':' is the cons operator. However > when writing list processing routines, you don't need to take a list and > call head and such. What you can do is: > > f (xs) = ... > > And because of Haskell's pattern matching, if you pass something that is a > cons of an item and a list, the item gets assigned to x, and the rest of the > list gets assigned to xs. You can also do more complicated things where you > assign the whole list to a variable, but still assign heads and tails to other > variables and so on. I don't know if Matz would want to take the time to > implement such functionality, but it can be done. > Ruby's case statement can do pattern matching.. relatively close to SML, Haskell. server> ruby a.rb result=61 server> ruby a.rb result=61 server> expand t2 a.rb def ackerman(m, n) case when m==0: n+1 when n==0: ackerman(m1, 1) else ackerman(m1, ackerman(m, n1)) end end result = ackerman(3, 3) puts "result=#{result}" server>  Simon Strandgaard 




Simon Strandgaard 
Kristof Bastiaensen
Guest
Posts: n/a

Hi, thanks for the explanations!
> Having similar functionality in Ruby would be interesting. I don't know > if it's feasible, since unification algorithms aren't particularly easy > to write, and I don't know if it would fit with the other aspects of the > language. The proposed unification seems like a subset of the full > functionality of unification anyway, though, so that might be feasible. I don't think it would be feasible. Ruby would have to conclude from X2 = Z * Z with X2 == 25 that Z == 5. It cannot have this information, because * as a method could mean anything. > > The other proposition here is pattern matching, which is also in existing > programming languages. Haskell has pattern matching. For example, > lists can be built like so: > > a : list > > Where a is an element, list is a list, and ':' is the cons operator. However > when writing list processing routines, you don't need to take a list and > call head and such. What you can do is: > > f (xs) = ... > I would propose something like: def mymethod(*[a, *b]) [a, b] end mymethod [1, 2, 3, 4] => [1, [2, 3, 4]] It would only be a subset of pattern matching, but interesting on its own. We could use := as a unify operator, it would be a combination of comparison and assignment. I have added these to my RCR. 




Kristof Bastiaensen 
Kristof Bastiaensen
Guest
Posts: n/a

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 datatype. 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 = <expression returning multiple arguments> 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. 




Kristof Bastiaensen 
Yukihiro Matsumoto
Guest
Posts: n/a

Hi,
In message "Re: Semantics of Multiple Values (updated RCR)" on 04/04/27, Kristof Bastiaensen <(EMail Removed)> writes: TITLE  Semantics of Multiple Values Interesting, but I'm afraid this one is too big (and too complex) change. I'd like to see a language with this semantics though. *[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}  func3((1, 2), 4)  => [1, 4]  #or maybe signal error? not sure about this one Making (1,2) as values is not a good idea. Perhaps we should prepare "array to values" converter (opposite of your "values" method).  func2(*values(multi), 4)  => [1, 2, 3, 4] I'm still against argument splat in the middle of actual argument list. It's not consistent with formal argument list, where splat is only allowed at the end. 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} I just don't feel right about this. Maybe arguments and return values are different beasts in my brain, even though continuation tells us their similarity. * Comparing multiple values * Unify operator I have to confess I couldn't understand those two proposals, how they behave, and how they are useful. matz. 




Yukihiro Matsumoto 
Kristof Bastiaensen
Guest
Posts: n/a

Hi,
On Tue, 27 Apr 2004 23:39:24 +0900, Yukihiro Matsumoto wrote: >  func3((1, 2), 4) >  => [1, 4] >  #or maybe signal error? not sure about this one > > Making (1,2) as values is not a good idea. Yes, I agree that nesting values would be overcomplicating things, and is probably too hard to implement. > Perhaps we should prepare > "array to values" converter (opposite of your "values" method). > Isn't * an array to values converter? (like in method(1, *[2, 3, 4]))? >  func2(*values(multi), 4) >  => [1, 2, 3, 4] > > I'm still against argument splat in the middle of actual argument > list. It's not consistent with formal argument list, where splat is > only allowed at the end. > Sorry, I overlooked this one. > 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} > > I just don't feel right about this. Maybe arguments and return values > are different beasts in my brain, even though continuation tells us > their similarity. > Yes, it is perhaps more proof of concept than really practical. (Being obliged to choose your variable naming wouldn't surely be a good idea). Some other constructs may be more practical. When Arrays are not the same as multiple values, the following will not work: a = [[1, 2], [3, 4]] a.each { a, b #something useful here ... } that's why my idea was to have to following construct: a.each { *[a, b] #something useful ... } and similarly: *[a, b] = a[1] > * Comparing multiple values > * Unify operator > > I have to confess I couldn't understand those two proposals, how they > behave, and how they are useful. > > matz. The idea of comparing multiple values would be a kind of syntactic sugar. Instead of writing x == 1 && y == 2 you could write x, y == 1, 2 (which looks much clearer in my eyes). In concrete it would evaluate all the expressions, and then compare them one by one. The first on the left will be compared with the first on the right, the second with the second, and so on. When all return true, evaluate to true. When comparing with ===, one could compare with multiple values: if (1, 3, 4..5) === x <...> end that would work like it does in case statements: case x when 1, 3, 4..5 <...> end About the unify operator, I am not so sure myself it is really usefull. I just put it here for discussion. Thanks, Kristof 




Kristof Bastiaensen 


 
Thread Tools  


Similar Threads  
Thread  Thread Starter  Forum  Replies  Last Post 
assigning multiple hash values to multiple variables  Patrick  Perl Misc  5  05032006 05:06 PM 
showing multiple values from multiple db rows in a DropDownList  Schultz  ASP .Net  1  02142005 12:38 AM 
it's all about semantics  =?Utf8?B?RGltaXRyaXMgUGFudGF6b3BvdWxvcw==?=  ASP .Net  2  07142004 08:19 AM 
Semantics extractor  Luigi Donatello Asero  HTML  5  02142004 07:10 PM 
destructor / semantics of delete this  Alexander Stippler  C++  6  08202003 03:27 PM 
Powered by vBulletin®. Copyright ©2000  2014, vBulletin Solutions, Inc..
SEO by vBSEO ©2010, Crawlability, Inc. 