Semantics of Multiple Values

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 data-type. 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 =
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).

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,
left-terms = right_term would be equivalent to
assign [left-terms], right-term

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 ?

Jean-Hugues

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 data-type. 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 =
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: @jhr is virteal, virtually real
Phone: +33 (0) 4 92 27 74 17

“Jean-Hugues ROBERT” jean_hugues_robert@yahoo.com wrote in message

The multiple assignment reminds me of Prolog’s “unification” mechanism.

It may be closer to ‘deconstruction’ or ‘pattern matching’ in some
functional languages. It is still entirely directional, assigning from rhs
to lhs, so it is a step short of unification.

Multiple value assignment and regexs can both be seen as special cases of
‘if pattern matches then assign values’

I suppose if done fully, could understand the deconstruction pattern for a,
b in all Ruby structures, requiring a clear definition of successful pattern
match, and of run-time behavior when pattern match fails:
- multiple values: a, b
- lists [a, b]
- hashes { :k => a, :l=>b}
- objects: x.a, x.b
- regexps: ??
- ?others?

Examples of possible rules:

a, b, c = f(x)

provided f(x) returns multiple values

1st 3 values assigned to a, b, c

[a, b, c] = f(x)

provided f(x) returns array

1st 3 elements assigned to a,b, c

{: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

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

It could be a very nice facility if done well and consistently e.g.
exploited in “case…when…” statements. It could (not necessarily
‘should’) potentially be many of the places variables get assigned to today
e.g. assignment statements, formal parameters, case statements, …

Hi,

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.

Jean-Hugues

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?

···

On Tue, 27 Apr 2004 03:00:23 +0900, Jean-Hugues ROBERT wrote:


Web: @jhr is virteal, virtually real
Phone: +33 (0) 4 92 27 74 17

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=

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
per-se. 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 XX,
Y2 is Y
Y
Z2 is Z*Z
Z2 is X2 + Y2.

Now, when you write

ptriple(3, 4, 5)?

Prolog unifies X2 with XX and so on, and finally it checks that
X
X + YY = ZZ, 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 in-parameters and which are out-parameters 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:

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 (x: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
···

a : list

[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 (x: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.

ruby a.rb
result=61
ruby a.rb
result=61
expand -t2 a.rb
def ackerman(m, n)
case
when m==0: n+1
when n==0: ackerman(m-1, 1)
else ackerman(m-1, ackerman(m, n-1))
end
end
result = ackerman(3, 3)
puts “result=#{result}”

···

Dan Doel djd15@po.cwru.edu wrote:


Simon Strandgaard

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 (x: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.

You are quite right.

What I wrote was incorrect and also did not capture my intent. Please see
“object pattern” in my other post replying to Matz.

Cheers.

···

“Kristof Bastiaensen” kristof@vleeuwen.org wrote

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)

Hi, here is my update RCR.
It adds a unify operator := , multiple variables
comparison, and some more goodies (:slight_smile:

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.

Hi,

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.
···

In message “Re: Semantics of Multiple Values (updated RCR)” on 04/04/27, Kristof Bastiaensen kristof@vleeuwen.org writes:

Hi,

   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

···

On Tue, 27 Apr 2004 23:39:24 +0900, Yukihiro Matsumoto wrote:

Yukihiro Matsumoto wrote:

Hi,

Moin!

   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.

I think both should be allowed. Both func2(*(values + [4])) manual
argument handling in methods are a stark contrast in ugliness to their
respective end-of-argument-list counterparts.

I also think that all this looks even weirder in Array construction. I
think this case would be a good one to support:

ary = [1, 2, 3]
[:foo, *ary, :bar, *ary, :qux]

However I don’t think that there should be support for multiple
splat-arguments in method argument lists. That would feel too unnatural.
This would be a small asymmetry.

Regards,
Florian Gross

Unification as 2-way pattern matching seems belong outside this proposal.

But perhaps Multiple Values could fit consistently into a more general
framework of one-way pattern matching for variable binding/assignment. It
seems duck typing also fits into such a pattern matching framework.

Here are a couple of usages examples of pattern match:

call with multiple values

def f (x, y, z=nil); end
def g; return 1, 2; end
x,y = g() #x=1, y=2
f(g()) # same as f(1,2,nil)
f(g(),3) # same as f(1,2,3)

conditional multiple assignment

{p: x, q: y} ||= h
# match x if x is not nil, then x = h[:p]
# match y if y is not nil, then y = h[:q]
# overall match never fails

method call with pattern-matched arg list

class C
def f ( a1, [a2,a3], {p: a4}, a5.(q: a6, r: a7, s:) ) ; end
end
C.new.f (w, x, y, z)

object match for method ‘f’

# multiple values match formal args with w,x,y,z
    # assuming w,x,y,z are not 'multiple values'
    # variable match: a1 = w
    # array match: a2, a3 = x[0],x[1]
    # hash match: a4 = y[:p]
    # object match: respond_to? :q,:r,:s
    #   a5 = a; a6 = z.q;  a7 = z.r

Why?

  • Makes intent and usage clear
  • Duck typing: patterns are duck types .(q:, r:, s:)

So here is my attempt at defining Pattern:

Pattern

···

“Yukihiro Matsumoto” matz@ruby-lang.org wrote

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 Pattern is a syntactic form containing 0 or more variables. It can be used
in place of a variable, anywhere that variable could be initialized or
assigned (lvalue?). The Pattern is matched at run-time (to the extent
necessary), resulting in either

  • successful match + variable bindings + success action
  • failed pattern match + failure action

A pattern, to match and bind, walks the target structure:

  • by position (array, positional args, multiple values, regex)
  • by name (hash, keyword args, object accessors/methods)
  • by magic (&block args, others?)

Kinds of patterns + pattern match:

  • variable + variable assigment
    x x = a

    always matches

  • multiple values + multiple assigment
    x,y,z
    x,y,z = a, b, c
    x,y,z = a, b # a, b, nil
    x,y,z = a # a, nil, nil
    def f(p); return 1, 2; end
    x,y,z = f(a) #1,2,nil
    x,y,z = f(a), b #1,2,b
    *v = 1,2
    x,y,z = *v, 3 #1, 2, 3

    multiple values is a separate type

    “,” extends multiple values, like Array#concat

    rhs treated as multiple values (extended by nil’s)

    always matches on assigment

    can fail on method invocation (below)

  • *arg vary-ary variable
    *x *x = 1, 2, 3

    always matches, assigns an array

    *x also converts back to multiple values

  • method definition + method invocation
    X#f(p,q) x.f(a,b)
    X#f(p: 10) x.f(p: a)
    X#f(p, *q) x.f(a, b, c)

    fail with exception if parameter mismatch

    else invoke method

  • hash pattern + pattern assigment
    {p: x} {p: x} = a

    fail with exception if !a.is_a? Hash

    else x = a[:p]

  • array pattern + pattern assigment
    [x, y, z] [x, y, z] = [a, b, c]

    fail if !rhs.is_a? Array

    else assign rhs[0], rhs[1], …

    like hash pattern: [1: x, 2: y, 3: z]

  • object pattern + pattern assigment
    .(p: x, q: y)
    .(p: x, q: y) = a

    match if a.respond_to? :p, :q

    assign x = a.p; y = a.q

    o.(p:x, q:y) = a

    assign o = a; x=a.p; y=a.q

    o.(p,q)

    match if a.respond_to? :p, :q

    perhaps extend to match specified classes?

  • conditional assigment
    x ||= value

    match if x was nil; no-op if fail

    Think of pattern: x||

  • regex pattern + regex match
    /abc*/ /abc*/ =~ string

    perhaps ‘=’ would work for ‘=~’

Any checking required to bind variables must take place at (by) run-time.
Some checks that define pattern match, but do not bind variables, may be
optionally turned off.

The behavior of success and failure depend on the context in which the
pattern appears. e.g. method invocation x.f(a) will pattern match x’s
pattern of methods x. with “x.f(a)”. Success means the method is invoked.
Failure means either undefined method, or wrong number of arguments, or
missing 2.0 keyword argument, or missing block, etc. depending on what
failed.

Thoughts? Full of holes?

(btw: Beta pushed this whole pattern/match about as far as it can go).

Sorry if this post appears more than once; it vanished off my news server.

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.

Unification as 2-way pattern matching seems to belong outside this proposal.

But perhaps Multiple Values could fit consistently into a more general
framework of one-way pattern matching for variable binding/assignment. It
seems duck typing also fits into such a pattern matching framework.

Here are a couple of usages examples of pattern match:

call with multiple values

def f (x, y, z=nil); end
def g; return 1, 2; end
x,y = g() #x=1, y=2
f(g()) # same as f(1,2,nil)
f(g(),3) # same as f(1,2,3)

conditional multiple assignment

{p: x, q: y} ||= h
# match x if x is not nil, then x = h[:p]
# match y if y is not nil, then y = h[:q]
# overall match never fails

method call with pattern-matched arg list

class C
def f ( a1, [a2,a3], {p: a4}, a5.(q: a6, r: a7, s:) ) ; end
end
C.new.f (w, x, y, z)

object match for method ‘f’

# multiple values match formal args with w,x,y,z
    # assuming w,x,y,z are not 'multiple values'
    # variable match: a1 = w
    # array match: a2, a3 = x[0],x[1]
    # hash match: a4 = y[:p]
    # object match: respond_to? :q,:r,:s
    #   a5 = a; a6 = z.q;  a7 = z.r

Why?

  • Makes intent and usage clear
  • Duck typing: patterns are duck types .(q:, r:, s:)

So here is my attempt at defining Pattern:

Pattern

···

“Yukihiro Matsumoto” matz@ruby-lang.org wrote

A Pattern is a syntactic form containing 0 or more variables. It can be used
in place of a variable, anywhere that variable could be initialized or
assigned (lvalue?). The Pattern is matched at run-time (to the extent
necessary), resulting in either

  • successful match + variable bindings + success action
  • failed pattern match + failure action

A pattern, to match and bind, walks the target structure:

  • by position (array, positional args, multiple values, regex)
  • by name (hash, keyword args, object accessors/methods)
  • by magic (&block args, others?)

Kinds of patterns + pattern match:

  • variable + variable assigment
    x x = a

    always matches

  • multiple values + multiple assigment
    x,y,z
    x,y,z = a, b, c
    x,y,z = a, b # a, b, nil
    x,y,z = a # a, nil, nil
    def f(p); return 1, 2; end
    x,y,z = f(a) #1,2,nil
    x,y,z = f(a), b #1,2,b
    *v = 1,2
    x,y,z = *v, 3 #1, 2, 3

    multiple values is a separate type

    “,” extends multiple values, like Array#concat

    rhs treated as multiple values (extended by nil’s)

    always matches on assigment

    can fail on method invocation (below)

  • *arg vary-ary variable
    *x *x = 1, 2, 3

    always matches, assigns an array

    *x also converts back to multiple values

  • method definition + method invocation
    X#f(p,q) x.f(a,b)
    X#f(p: 10) x.f(p: a)
    X#f(p, *q) x.f(a, b, c)

    fail with exception if parameter mismatch

    else invoke method

  • hash pattern + pattern assigment
    {p: x} {p: x} = a

    fail with exception if !a.is_a? Hash

    else x = a[:p]

  • array pattern + pattern assigment
    [x, y, z] [x, y, z] = [a, b, c]

    fail if !rhs.is_a? Array

    else assign rhs[0], rhs[1], …

    like hash pattern: [1: x, 2: y, 3: z]

  • object pattern + pattern assigment
    .(p: x, q: y)
    .(p: x, q: y) = a

    match if a.respond_to? :p, :q

    assign x = a.p; y = a.q

    o.(p:x, q:y) = a

    assign o = a; x=a.p; y=a.q

    o.(p,q)

    match if a.respond_to? :p, :q

    perhaps extend to match specified classes?

  • conditional assigment
    x ||= value

    match if x was nil; no-op if fail

    Think of pattern: x||

  • regex pattern + regex match
    /abc*/ /abc*/ =~ string

    perhaps ‘=’ would work for ‘=~’

Any checking required to bind variables must take place at (by) run-time.
Some checks that define pattern match, but do not bind variables, may be
optionally turned off.

The behavior of success and failure depend on the context in which the
pattern appears. e.g. method invocation x.f(a) will pattern match x’s
pattern of methods x. with “x.f(a)”. Success means the method is invoked.
Failure means either undefined method, or wrong number of arguments, or
missing 2.0 keyword argument, or missing block, etc. depending on what
failed.

Thoughts? Full of holes?

(btw: Beta pushed this whole pattern/match about as far as it can go).

I have some Prolog background and I believe Prolog is the
language where unification is central.

First, I have to say that what I proposed is a “poor man’s unification”.
Not that its value is low, only it does not turn Ruby into Prolog.

Q. So, what is a “poor man’s unify”.
A. Well… it is Unification. But without a key aspect of it: Backtracking.

Q. Backtracking ?
A. Backtracking is what happen when a function can return many values,
once a time, like a generator if you wish. So let’s forget about that.

What remains is a mechanism that does two things at once:

  • comparison
  • assignment

Q. You mean, I can have my cake and eat it too ?
A. Cool, isn’t it ?

Comparison: ==
We know what it is already. It depends either on the identity of objects
or on their value. Let’s skip the easy one.

Comparison by value:
For scalar type, it’s easy, “a” is “a” and 1 is 1.
For collection, it’s slightly more complex. You compare (note the
recursivity introduced here) each element of the collections.

Q. That’s not exactly true, .==() is called.
A. True. Let’s assume that the unification algorithm walk the collections
itself, calling some .unify() instead.

So far, so good, known stuff.

Now, Assignment: =
Assignment is about setting the value of a Variable.
Before the assignment, there exists 2 cases:
- The variable exists already. It is “Bound” to something
- The variable does not exit. It is “Free”
After the assignment the variable is bound to some value.

Finally, Comparison + Assignment:
Let’s assume that, in addition to a scalar and collection, a
value can be a reference to a variable (not the case in Ruby today).

Q. Then, what happens with comparison ?
A. That’s easy:
- For a variable that exists already, you pick it’s value and
you use it to do the comparison. “Bound” case.
- For a variable that does not already exists, it is even easier,
you assume that the comparison succeed and, as a side effect,
you “create” the variable with the value it was compared against.

Q. That’s it !
A. Yes !

Q. Now, what about the consequences/impacts on Ruby ?

A. First and paramount is that when building a value, in the context
of a “Comparison + Assignment”, you cannot proceed as usual. i.e.
unify [a,b], [c,d] is not like [a,b] == [c,d].

Q. OK. How ?
A. When you evaluate the later (==), you build values getting rid of the
variables, i.e. you use the value of the variable in place of the
variable itself.

Whereas with unify, you don’t. Instead, whenever there is a variable,
you keep it.

Q. How is that possible ?
A. Well, it is… you need something that does not exists in Ruby so far,
an object that describes a variable. In such a way that, instead
of using the variable’s value, you reference the variable itself.

Q. In C, this is &var, the “address of var”, similar ?
A. Yes, pretty much the same.

So, if a is 1, b is 2… then
[a,b] == [c,d] is about comparing [1,2] with [3,4] and…
unify [a,b], [c,d] is about [&a,&b] and [&c,&d] (using C’s syntax)

That’s an additional level of indirection.

Q. OK. Nice. But where is it useful ?
A. Very often ! It is already there in Ruby actually !

Q. No kidding ?
A. No, look.

def f( a, b, c ); … end
When f is invoked, some unification occur:
unify f’s args, [a,b,c]

a, b, c = f()
When assignment with multiple values occur:
unify [a,b,c], f() # Q. Almost… what if f() returns [1,2] ?
# Q. What if a, b or c already exists ?

p = proc( { |a,b,c| … }
p.call( 1, 2, 3)
When Proc get their parameters:
unify [a,b,c], proc’s actual parameters

A. “unify” is there but not exactly with the same semantic:

  • Assignment always occur, even if variable is already bound.
  • nil is assigned to the variable when there is no better value.
    That behaviour is what I call “assign”, versus “unify”.

“unify” and “assign” work even better if a new type of
value is introduced (in addition to the one introduced already,
the one that is a & reference to a variable).

The new type is “FreeClass”, much like TrueClass. That is
a Singleton too.

Q. What is it for ?
A. a = FreeClass.instance() is the way to “free” the “bound”
variable named “a”. Shorthand: a = free

Now, some examples, explained.

def test()
unify a, 1
p a # => 1, a was free, now it is bound
a = free
p a # free, a is free again
unify a, 2
p a # => 2, a was free
unify a, 3
p a # => 2, a was bound, unify failed.
a = free
unify [a,b], # Fails (evaluate to false)
unify [a,b], [1,2]
p a, b # => 1 2, unify succeeded.
unify [free,b], [1,2] # Succeeds
unify [free,b], [1,3] # Fails
unify [free,b], [1,c] # Succeeds
p c # => 2… it works both ways
a = b = c = free
unify [a,b], [1,2,3]
p a, b # nil nil, unify failed
assign [a,b], [1,2,3]
p a, b # 1, 2, assign is more tolerant
assign [2,b], [1,3]
p b # 2, assign failed, no assignments done
end

I hope that this explanation, way too long, gives you
some idea about how powerful an “assign” and “unify”
operators could be and how close they are to what
already exists.

Nota: *, like in f( *args), makes them even more powerful !

Yours,

Jean-Hugues

···

At 23:39 27/04/2004 +0900, you wrote:

  • 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.

Jean-Hugues ROBERT wrote:

Q. So, what is a “poor man’s unify”.
A. Well… it is Unification. But without a key aspect of it: Backtracking.

On the other hand, it would probably be reasonably easy to implement
using continuations if one really wanted to.

Hi,

“unify” and “assign” work even better if a new type of
value is introduced (in addition to the one introduced already,
the one that is a & reference to a variable).

The new type is “FreeClass”, much like TrueClass. That is
a Singleton too.

Q. What is it for ?
A. a = FreeClass.instance() is the way to “free” the “bound”
variable named “a”. Shorthand: a = free

This type already exists: NilClass, with as only
instance nil. In fact nil is always used for unbound variables.
Whenever a variable isn’t bound, it evaluates to nil, for all
variables (global, object) except local. Local variables are
the only ones that can raise an error, the reason for this is
that local variables and methods or interchangeable. Whenever
Ruby sees a local variable that didn’t have an assignment before,
it treats it as a method call. When at runtime it executes the
method, and cannot find it, it will give an error.

I would suggest using := as a unification operator. Ruby could
then always see that what is before the operator are variables.
It would treat nil as the unbound.
I am still not really convinced of the practical use of this
operator. I can see one usage, as initialization:

perhaps the following

def search_data
some_operation
return succeed, value1, value2
end

a, b = nil
if true, a, b := search_data
<use a, b>
end

#but you may as well use
succes, a, b = search_data
if(succes)
<use a, b>
end

There may be other real world practical uses, but I cannot think
of any.
Any suggestions?

···

On Wed, 28 Apr 2004 04:51:59 +0900, Jean-Hugues ROBERT wrote:
a := 0 #meaning a = 0 if (a == nil).

Hi,

Isn’t * an array to values converter?
(like in method(1, *[2, 3, 4]))?

I don’t think so, even with your proposal. If * (splat operator) is
an array to values converter,

def func1(a,b)
[a,b]
end
func1(1, *[2, 3, 4])

should give [1,2], unless values work like LIST in Perl.

  • Comparing multiple values
  • Unify operator

I have to confess I couldn’t understand those two proposals, how they
behave, and how they are useful.

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).

Not for my eyes. YMMV.
I don’t feel right to mix comparison operands x, 1, and y, 2.

						matz.
···

In message “Re: Semantics of Multiple Values (updated RCR)” on 04/04/28, Kristof Bastiaensen kristof@vleeuwen.org writes:

Hi,

“unify” and “assign” work even better if a new type of
value is introduced (in addition to the one introduced already,
the one that is a & reference to a variable).

The new type is “FreeClass”, much like TrueClass. That is
a Singleton too.

Q. What is it for ?
A. a = FreeClass.instance() is the way to “free” the “bound”
variable named “a”. Shorthand: a = free

This type already exists: NilClass, with as only
instance nil. In fact nil is always used for unbound variables.
Whenever a variable isn’t bound, it evaluates to nil, for all
variables (global, object) except local. Local variables are
the only ones that can raise an error, the reason for this is
that local variables and methods or interchangeable. Whenever
Ruby sees a local variable that didn’t have an assignment before,
it treats it as a method call. When at runtime it executes the
method, and cannot find it, it will give an error.

nil ? Not quite. nil is a valid value for a bound variable.
unify nil, 1 # fails

I would suggest using := as a unification operator. Ruby could
then always see that what is before the operator are variables.
It would treat nil as the unbound.

Makes sense (but a binary unify operator is more like =~= than
:=, because it works both ways, a := b eqv b := a).

syntax unify x, z makes sense too because one can also unify x, y, z

BTW: what is after the operator is also variables (sometimes).
Having the variables on the left side is just a trick to put the
emphasis on the fact that statement is more like assignment than
comparison.

I am still not really convinced of the practical use of this
operator. I can see one usage, as initialization:

inits ? that’s right, specially considering that Ruby current
initialization semantic is already a subset of unify.

a := 0 #meaning a = 0 if (a == nil).

No. Should be:
I would prefer a =~= 0 however, or unify a, 0

perhaps the following

def search_data
some_operation
return succeed, value1, value2
end

a, b = nil
if true, a, b := search_data
<use a, b>
end

another example:
if true, “a”, b =~= seach_data then

end

#but you may as well use
succes, a, b = search_data

That is an example where one can see that something very
similar to a more generalized unify already exists in Ruby.
i.e. success, a, b = seach_data eqv unify [success, a, b], seach_data
I am proposing to enhance slightly what already exists.

if(succes)
<use a, b>
end

Yes, but:
success, a, b = seach_data
if success and a == “a”

end
is becoming verbose compared to:
if true, “a”, b =~= seach_data

end

There may be other real world practical uses, but I cannot think
of any.
Any suggestions?

Plenty. Where ever there is a combination of comparison and
assignment, unify (and/or =~=) will be less verbose.

Back to nil & free:
If you want to avoid a FreeClass, at least you need a way to free
bound variables. I suppose that a there is already enough in Ruby
today to:

  1. Check if a variable already exists
  2. “delete” it

I also suppose it is possible to prototype unify/assign today in
Ruby. I may do it. However, a user level implementation is not
going to look very nice (not mentioning performances). That is
why some support at the syntax level would help. Additionnaly,
a user level unify will not integrate well with the existing
stuff about multiple assignments and parameters passing.

def test()

unify [a,b], [1,2]

unify [ ref{:a}, ref{:b} ], [1,2]
a = Free
unify ref{:a}, ref{:b}
end

def mymethod( a, (b, c) ) # but using user level unify

def mymethod( *args )
unify [ref{:a},[ref{:b},ref{:c}]], args

end

Have a look at http://onestepback.org/index.cgi/Tech/Ruby/RubyBindings.rdoc
for a definition of a Reference class and a ref{var_name} factory method.

Yours,

Jean-Hugues

···

At 06:54 28/04/2004 +0900, you wrote:

On Wed, 28 Apr 2004 04:51:59 +0900, Jean-Hugues ROBERT wrote:
a := 0 # meaning a = 0 if a does not exists already.


Web: http://hdl.handle.net/1030.37/1.1
Phone: +33 (0) 4 92 27 74 17