RCR 296: Destructive methods return self

D

Daniel Amelang

And you'll have to agree that that won't work.

Yes, with the current state of Ruby, you are right. But let's travel
back and take another look at matz's original idea: introducing _true_
multiple return values. With this change, the following methods will
no longer be equivalent:

def get_array
return [1,2]
end

def get_mult_values
return 1, 2
end

If we had true multiple return values (as part of the language), we
could then use the methods like this:

a = get_array # [1,2]
a = get_mult_values # 1
a, b = get_mult_values

This was what he meant in the first place by 'true multiple return
values'. I, personally, find it cleaner than having 3 methods for each
bang method (strip, strip!, strip!?) and it has potential for other
uses.

Although I _really_ want to write a 'what!?' method someday :)

Dan
 
C

Csaba Henk

If we had true multiple return values (as part of the language), we
could then use the methods like this:

a = get_array # [1,2]
a = get_mult_values # 1
a, b = get_mult_values

What would get_mult_values return if it's used elsewhere, not as a right
hand side of an assignment?

Could you come up with a consistent rule for defining in which
context how many values are returned?

Csaba
 
D

Daniel Amelang

Sure, multiple return values are only accessable in an assignment
(rvalue). Otherwise, you only get the first value.

Or, consider this: you could tack a * on the front if you wanted to
trap them in an array. That should cover your bases:

(*get_mult_values).each {|value| ...}

Seems pretty simple. What do you think? I may have missed some use case...

Dan
 
C

Csaba Henk

So the point is that we should not create a situation in which it easily
happens that the designer of a class forces an interface on the user.

Sure, multiple return values are only accessable in an assignment
(rvalue). Otherwise, you only get the first value.

This is bad in the above respect: either the class designer forces using
assignment on its users when they don't want to lose information, or
he/she has to take extra care to define alternative interfaces (eg., a
method which does the same except that it returns an array).
Or, consider this: you could tack a * on the front if you wanted to
trap them in an array. That should cover your bases:

(*get_mult_values).each {|value| ...}

Seems pretty simple. What do you think? I may have missed some use case...

This second one sounds better, but not perfect. "*" is used now in the
opposite sense: not for returning some stuff bundled in an array, rather
for "inlining" the content of the array. So with this proposal you
should write:

arr.values_at **get_mult_values

which is ugly.

Of course, if we introduced multiple values, as an accompanying
enhancement, we should also introduce that automatically all values are
retrieved if you use the method as a tail of a parameter list of a
method call which takes undefinite number of arguments. So then you
could write just

arr.values_at get_mult_values

However, this doesn't hide the twist that "*" is used in the opposite
sense. But I could live with it, and rather overload "*" in this way
than to introduce one more punctuation symbol for uniqueness.

Or, maybe "*" could be explicitly turned to such an operator which
toggles bundling/inlining. So the following would be valid:

**[1,2] == [1,2]

Not as if it were useful as such, but then "*" would still have a
consistent meaning.

Up till now (and now) it wasn't an issue as "bundling" was not present
in ruby. But with multiple return values, it would appear on the scene
and should be handled properly.

Csaba
 
F

Florian Gross

Daniel said:
Yes, with the current state of Ruby, you are right. But let's travel
back and take another look at matz's original idea: introducing _true_
multiple return values.

Yup, I tried to hint at that solution as an alternative in the email.
Sorry if I was not being explicit enough.
 
F

Florian Gross

Csaba said:
This second one sounds better, but not perfect. "*" is used now in the
opposite sense: not for returning some stuff bundled in an array, rather
for "inlining" the content of the array.

Actually it's already used for both senses.

a, *b = 1, 2, 3
b # => [2, 3]

Same for the & modifier which is used in opposing senses as well:

def x(&block)
y(&block)
end
 
F

Florian Gross

Yukihiro said:
I want some syntactical support if cascading method call have to be
introduced, not by a method (except for instance_eval, of course).

What do you think about Perl 6's given statement?

It would look somewhat like this:

given "string" (do?)
.reverse!
.chop!
end

Or are you thinking of a more symbolic syntax? I think that would be harder.
 
M

Mathieu Bouchard

I want some syntactical support if cascading method call have to be
introduced, not by a method (except for instance_eval, of course).

Unlike Perl, Ruby requires a space between a $-prefix and a variable
name. Therefore a standalone $ could be an operator in Ruby. There seems
to be a tacit tradition of treating $ as a slashed S letter, and using it
as an abbreviation for a word starting with S. For example:

Basic: it's $ as in String
Sh,Tcl: it's $ as in Substitute (interpolate)
Perl: it's $ as in Scalar
Ruby(a): it's $ as in Special (think-of *special-variables*)

and then

Ruby(b): it's $ as in Sequence (of method calls)

it wouldn't be that confusing because $-vars are rarely used in Ruby.

I often name ";" (and "\n") the sequence operator already, but this would
be a second flavour:

A && B run A and if true then run B
A || B run A and if false then run B
A ; B run A and then B no matter what

... are three operators on expressions, but in A $ B, the B part obviously
wouldn't be an expression, and even A would be restricted to being a
method-call expression. I assume the binding will be rather low-precedence
(mimicking Smalltalk's precedence could be a good idea). B is restricted
to being a receiver-less method-call in which the receiver will be A's
receiver instead of being the default self.

Any other ideas for the meaning of $ here? Any other characters? (I'd
rather *not* use backslash)

If people have violent allergic reactions to the use of "$" for *that*,
then I'd suggest "&." instead, even though it's longer.

Another idea I've had is that, because prefix "." is not allowed in Ruby
1.8, it could later serve so that in "a.foo;.bar" the dot-prefix operator
would make the method-call on the "syntactically previous" receiver, that
is, a. How "previous" is defined may depend on precedence rules...

What do you think of this?

_____________________________________________________________________
Mathieu Bouchard -=- Montréal QC Canada -=- http://artengine.ca/matju
 
T

Trans

3) is an interesting solution to the detection problem. Perhaps
having real multiple return values will solve some other issues in
Rubyland also (anyone?). Using a modified version of the 'strip'
method, let me illustrate a minor modification in the language that
allows for easier use of multiple return values:

I do not think there's much hope for multiple return values of this
sort --not unless Ruby were to change significantly. A differntiation
would have to be made between the returning of an array vs. the
returning of multiple values. Now I like the idea of returning multiple
values. I always wanted a way to feed return values into another method
as arguments _directly_ (without special * notation). But I suspect
multiple return values will simply cause too many undesired backward
compatability issues.

But what is worse about multiple return values is also what can be
frustrating about parameters: recalling the order of information. At
some point I can't help but feel like things would be a whole lot
easier if it were only a hash that could go into a method and only hash
that could come out.

On another note, something else to consider is that object's have the
ability to be "singlized", and this gives us the flexibility to flag
them as modified or not. So instead of returning nil when a destructive
method has no effect, just return self but flag the object as such.

a = "123"
if a.strip!.unmodified?

Then perhaps its time to just go all the way and support an in place
construction (umm... destruction ;-) on any method. Put the bang where
the dot is.

if a!strip.unmodified?

T.
 
T

Trans

Another idea I've had is that, because prefix "." is not allowed in
Ruby
1.8, it could later serve so that in "a.foo;.bar" the dot-prefix operator
would make the method-call on the "syntactically previous" receiver, that
is, a. How "previous" is defined may depend on precedence rules...

It has also been prposed that ".x" simply imply "self.x". An easier
notion would simply be:

a.(strip!; upcase!)

This was discussed breifly on ruby-muse (suby-ruby mialing list). But
the conclusion was basically why bother when

a = a.strip.upcase

works when speed is not an issue. And

a.strip!; a.upcase!

works just as well when speed does matter.

T.
 
C

Cameron McBride

Then perhaps its time to just go all the way and support an in place
construction (umm... destruction ;-) on any method. Put the bang where
the dot is.

if a!strip.unmodified?

I'm not a CS guy, nor a language designer - but this looks intriguing.
It'd be a way for the ruby programmer to signal that the upcoming
chain should be optimized and destructive and leave the how (or if) up
to the interpreter.

Course, I also like the Kernel#wtf!? if one needs a boolean value and
have any destructive method return self. It makes sense, and seems to
go with a lot of the ruby way as I see it. (and it's not half as ugly
as @@vars).

Cameron
 
P

Pit Capitain

Daniel said:
Although I _really_ want to write a 'what!?' method someday :)

Not as pretty as normal methods:

class C
define_method "what!?" do
"the hell..."
end
end

puts C.new.send("what!?") # => the hell...

Regards,
Pit
 
M

Malte Milatz

Trans:
But what is worse about multiple return values is also what can be
frustrating about parameters: recalling the order of information. At
some point I can't help but feel like things would be a whole lot
easier if it were only a hash that could go into a method and only hash
that could come out.

That's something I encounter often, too. It' especially tiresome when
trying to figure out how undocumented methods work - or when I've
forgotten in which order arguments have to be given (example:
alias_method). Though extensive use of hashes as parameters would be
terribly verbose, it would be very easy to read, because it's easier to
know what is happening - provided that the keys used have got sensible
names.

Malte
 
M

Mathieu Bouchard

Not as pretty as normal methods:
class C
define_method "what!?" do
"the hell..."
end
end
puts C.new.send("what!?") # => the hell...

My only regret with that, is that I can't define a method whose "name" is
actually an array.

I'd do that with 2-element arrays being pairs of one Fixnum and one
Symbol. (this is because some languages, like PureData (which I *do*
connect to Ruby), select on both a method name and an "inlet number", that
is, an "entry-point" through which the message is entering the object.
Currently I am converting the Fixnum to a number and inserting it inside
the Symbol but that's ugly.

Do you like the idea of non-Symbol selectors?

_____________________________________________________________________
Mathieu Bouchard -=- Montréal QC Canada -=- http://artengine.ca/matju
 
P

Pit Capitain

Mathieu said:
My only regret with that, is that I can't define a method whose "name" is
actually an array.
...
Do you like the idea of non-Symbol selectors?

At least it's an interesting idea, but I imagine this would be hard to
read. Couldn't you fill a hash with associations from your non-Symbol
selectors to actual methods? You'd need a special method calling syntax
anyway.

Regards,
Pit
 
F

Florian Gross

Trans said:
I do not think there's much hope for multiple return values of this
sort --not unless Ruby were to change significantly. A differntiation
would have to be made between the returning of an array vs. the
returning of multiple values. Now I like the idea of returning multiple
values. I always wanted a way to feed return values into another method
as arguments _directly_ (without special * notation). But I suspect
multiple return values will simply cause too many undesired backward
compatability issues.

But what is worse about multiple return values is also what can be
frustrating about parameters: recalling the order of information. At
some point I can't help but feel like things would be a whole lot
easier if it were only a hash that could go into a method and only hash
that could come out.

Let's keep talk about this for a moment.

We'll finally get keyword arguments in Rite and they're already in Ruby
1.9 as well.

But what about keyword return values? How would those look?

I'm not sure how this would work out, but I guess we would need to build
it on the (a, b) = [1, 2] syntax.

Perhaps (result: a, changed: b) = obj.strip! would work.

I think the case of (result: result, changed: changed) would be quite
common so perhaps it would be a good idea to introduce a short-cut.
Maybe :)result, :changed) = rhs.

What do you think about this? I guess I'm getting obscure here, but this
might be an alternative to more complex solutions...
 
F

Florian Gross

Trans said:
It has also been prposed that ".x" simply imply "self.x". An easier
notion would simply be:

a.(strip!; upcase!)

And this is a very interesting alternative to the a.apply { strip!;
upcase! } syntax.

I think I like this one as it is paired and thus easier to make the
connection visually. E.g:

form.button.tabgroup.last.(
caption = "Foo"
enabled = true
top -= 5
left += 10
onclick = {
puts "Hello World"
}
)

Does this sound sensible?
 
F

Florian Gross

Mathieu said:
I often name ";" (and "\n") the sequence operator already, but this would
be a second flavour:

A && B run A and if true then run B
A || B run A and if false then run B
A ; B run A and then B no matter what

That is an interesting approach to viewing this.
.. are three operators on expressions, but in A $ B, the B part obviously
wouldn't be an expression, and even A would be restricted to being a
method-call expression. I assume the binding will be rather low-precedence
(mimicking Smalltalk's precedence could be a good idea). B is restricted
to being a receiver-less method-call in which the receiver will be A's
receiver instead of being the default self.

Though I'm still wondering how all this would look in an actual code sample.

Would this come close?

obj $ first_call $ second_call

I think that would be quite hard to space out over multiple lines:

obj $ first_call \
$ second_call \
$ third_call \
$ fourth_call

And I'm wondering if calling setters and methods with complex arguments
would also be possible with this syntax.

You know -- somehow I can't stop thinking of this as a parallelism
operator...
Any other ideas for the meaning of $ here? Any other characters? (I'd
rather *not* use backslash)

@? Though then the order would somehow seem to be reversed...
If people have violent allergic reactions to the use of "$" for *that*,
then I'd suggest "&." instead, even though it's longer.

Another idea I've had is that, because prefix "." is not allowed in Ruby
1.8, it could later serve so that in "a.foo;.bar" the dot-prefix operator
would make the method-call on the "syntactically previous" receiver, that
is, a. How "previous" is defined may depend on precedence rules...

Somehow this looks a lot like an implicit call to $_. Perhaps we could
rename $_ to a variable-like-construct 'it' (similar to self, false,
true and nil).

But then again $_ is not only used for referring to the last method call
result...
 
D

Douglas Livingstone

form.button.tabgroup.last.(
caption = "Foo"
enabled = true
top -= 5
left += 10
onclick = {
puts "Hello World"
}
)

Does this sound sensible?

Could it act like this:

class Sample
attr_accessor :red
def .
yield self
end
end

m = Sample.new

m.{ |self| red = 'read' }

# or:

m.do |self|
red = 'read'
end

puts m.red #=> read
 
L

linus sellberg

Csaba said:
a call method is invoked implicitly if obj is a local var, why not to
have

obj{}

I thought about it as well, but the idea has problems. One problem is
that it really kills chaining which takes place before the block. Consider:

obj.sort{rand 2}

which could be interpreted as both

(obj.sort){rand 2}

and

obj.(sort{rand 2} )

(only illustrates the different parses, not actual code)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,071
Latest member
MetabolicSolutionsKeto

Latest Threads

Top