Syntax sugar: treating an object like a method

Hi --

> I'm thinking of yet another RCR and would like to see if
> anybody likes it. I think this would be a sweet feature.
Here
> are the basic components:

I'm not sure what the canonical definition of syntactic sugar
is, but
I don't think that I think this is exactly that. Anyway....

In my book, syntactic sugar is when you have a more
concise/readable syntax that is equivalent to a more
fundamental (and longer) syntax. So by that definition, all of
the operators that equate to methods are syntactic sugar. I
think this thing I'm talking is similar to adding another
operator.

> 1. Allow a null/default method to be defined in a class so
that
> the syntax "obj(*args,&block)" could be used. obj could be
any
> expression yielding a object. When you try to treat that
> object as a method passing arguments and/or attaching a
code
> block to it, the null/default method would be called. I
was
> thinking "@" would be a good name for this method - like
"+@"
> and "-@". Here are a few example uses:
>
> * klass(*args) : you alias the null method to "new" so that
> this would be equivalent ot klass.new(*args). I tend to
forget
> the ".new" more often than I'd like to admit.

Forgetting .new is a weak reason for making it optional. It
would be
hard to argue that calling MyClass.new to create a new
MyClass object
is arcane or hard to get the hang of.

I know it is weak. But, it did seem to prod me to develop this
idea a little more - because the vast majority of time the only
thing you do with a class is call new.

> * obj() : this could be aliased to clone in Object so that
> default would be to just obj.clone
>
> * enum {...} : you could alias the null method to "each" so
> that this would be equivalent to enum.each {...}
>
> 2. Allow an assignment method to be defined for an object
('='
> is probably best). This would be called when you find an
> object expression on the LHS whereas #1 is the equivalent
for
> the RHS. The ugly thing about this is that you have to
make
> sure that the object being assigned looks like an
expression as
> opposed to a variable or attribute assignment - surrounding
is
> parentheses makes it an expression that is normally illegal
to
> assign to. Along with '=', '+=', '-=', etc. becomes
meaningful
> in this context. You could also incorporate RCR 157/307 in
> there allowing arguments. Here are a few example uses for
> this:
>
> * (str) = value : this could default to "replace" for any
> objects that respond to it (or if you use evil.rb -
"become")
>
> * (mutable) += value : now the real += can be implemented
for

+= is already the real +=. It's not the C or Perl +=, but
it's the
real Ruby +=.

Correct. "real" was the wrong word. How about "in-place"? A
normal += in Ruby creates a new object whereas this one
modifies an object in-place.

> this since you are accessing the object rather than
assigning
> the variable. Of course Fixnum is immutable, so it doesn't
do
> you any good there. Other mutable classes (or ones that
could
> be made mutable - Float) could take advantage of this (for
> example Array).

To a large extent, my reaction to all of these examples comes
down, as
it often does, to the question: how would I explain this to
someone to
whom I was teaching Ruby (in person or in writing)? I think
I would
find it quite difficult to explain that in this:

   s = "hi"
   t = s
   (s) = "bye"

I think this is a good example to show the difference.
Programmers need to understand the difference shown above. One
assigns to a variable and one assigns into an object that a
variable has. Both are normal things you would want to do and
both are assignment-like.

t is now "bye". I understand that there's really no such
thing as
"intuitive" computer language syntax, but this seems to be
pushing it
way in the other direction. I just can't see how wrapping
something
in parentheses can be hooked into any kind of explanation. I
mean, it
could be explained, just as ${var} = "hi" can be explained in
Perl,
but I really believe that very few things in Ruby, if any,
travel this
far from something reasonably self-evident to a reasonably
large
number of programmers.

I also continue to be unconvinced of the need, usefulness, or
desireability of any of the extra level of reference kind of
thing.

Why do some of you think that everything I suggest has to do
with references? The initial reason I was wanting this was in
my BNF like classes thing (specify syntax in Ruby directly
rather than in some other language - yacc/lex). Right now I
have things like this:

int = (("0".."9")*(1..+1.0/0)).qualify { |m| m.to_s.to_i }

I use operator overloading quite a bit in this to create these
Syntax (renaming it to Grammar) objects. In the example above,
int now is used to match anywhere from 1 to an infinite number
of digits and the resulting parse tree (an array of digit
characters) is converted to an integer. In the above, it would
have been nice not to have to put the .qualify in there and
have it look like this:

int = (("0".."9")*(1..+1.0/0)) { |m| m.to_s.to_i }

That is where the idea originated. Nothing to do with
references.

Similarly, obj() requires one to figure out whether or not
there is a
method called obj, and if there isn't to try to figure out
what has
been defined as () for this particular object.

Good point. It is similar to differentiating between a local
variable and a method call. Doing the above would do the same
between a method call and calling the default method in an
object:

xyz - could be a local variable (default) or a method call
xyz() - could be a method call (default) or a null method call
(xyz)() - definitely a null method call

I fear that's the
slippery slope to what people call "write-only code".

You could argue that. That is what Java designers said. And
that is why they didn't allow operator overloading. I think
operator is a good thing, but someone can easily abuse it and
make write-only code. I think what I'm suggesting here is like
adding more operators.

Yahoo! Mail
Stay connected, organized, and protected. Take the tour:
http://tour.mail.yahoo.com/mailtour.html

···

--- "David A. Black" <dblack@wobblini.net> wrote:

On Thu, 19 May 2005, Eric Mahurin wrote:

I'm not sure what the canonical definition of syntactic sugar is,
but I don't think that I think this is exactly that. Anyway....

In my book, syntactic sugar is when you have a more
concise/readable syntax that is equivalent to a more fundamental
(and longer) syntax. So by that definition, all of the operators
that equate to methods are syntactic sugar. I think this thing I'm
talking is similar to adding another operator.

Yes. It's adding the #() operator, which is evil.

Forgetting .new is a weak reason for making it optional. It would
be hard to argue that calling MyClass.new to create a new MyClass
object is arcane or hard to get the hang of.

I know it is weak. But, it did seem to prod me to develop this
idea a little more - because the vast majority of time the only
thing you do with a class is call new.

Very weak. And speak only for your own development. I use classes in
different ways for different projects.

* (str) = value : this could default to "replace" for any
objects that respond to it (or if you use evil.rb - "become") *
(mutable) += value : now the real += can be implemented for

+= is already the real +=. It's not the C or Perl +=, but it's
the real Ruby +=.

Correct. "real" was the wrong word. How about "in-place"? A
normal += in Ruby creates a new object whereas this one
modifies an object in-place.

The case where you will need this is the very case that makes the ++
operator inappropriate and impossible in Ruby -- numeric values.

If you need to append to something in place, make a method that does
exactly that.

  str = "foo"
  str << "bar"

  puts str # -> "foobar"

If you need a counter:

  class Counter
    def initialize(start = 0)
      @counter = start
    end

    def inc
      @counter += 1
    end

    attr_reader :counter
  end

  lines = Counter.new(0)
  3.times { lines.inc }
  puts lines.counter # -> 3

I'd much rather see the replacement concept explicitly specified as
making #replace a "standard" method that can (potentially) do a
bitwise copy of the object. This would also mean having #__replace__
in the same way that we have #__send__ and #__id__.

This is *clear*. Your proposal is not.

Why do some of you think that everything I suggest has to do
with references?

Because nearly everything you bring up has a reference-like mention
in it. This part is your own fault.

The initial reason I was wanting this was in my BNF like classes
thing (specify syntax in Ruby directly rather than in some other
language - yacc/lex). Right now I have things like this:

int = (("0".."9")*(1..+1.0/0)).qualify { |m| m.to_s.to_i }

I use operator overloading quite a bit in this to create these
Syntax (renaming it to Grammar) objects. In the example above,
int now is used to match anywhere from 1 to an infinite number
of digits and the resulting parse tree (an array of digit
characters) is converted to an integer. In the above, it would
have been nice not to have to put the .qualify in there and
have it look like this:

int = (("0".."9")*(1..+1.0/0)) { |m| m.to_s.to_i }

That is where the idea originated. Nothing to do with
references.

Sorry, but ick. I don't get where you even remotely think that not
having "qualify" is more intuitive or cleaner than having it. I'd
have to have some way of knowing and looking up that the "default
operator" is #qualify in this case. Talk about impossible to
maintain code.

I fear that's the slippery slope to what people call "write-only
code".

You could argue that. That is what Java designers said. And that
is why they didn't allow operator overloading. I think operator is
a good thing, but someone can easily abuse it and make write-only
code. I think what I'm suggesting here is like adding more
operators.

Definable operators are a good thing. The () operator is not.
Therefore, this suggestion is not. (There are several operators that
are bad ideas in C++, which is why the Java designers prohibited
them. Specifically operator&, conversion operators, operator+=, and
a few others. This doesn't invalidate the whole idea, but it does
indicate that there are really dumb operators to overload.)

-austin

···

On 5/18/05, Eric Mahurin <eric_mahurin@yahoo.com> wrote:

--- "David A. Black" <dblack@wobblini.net> wrote:

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

"Eric Mahurin" <eric_mahurin@yahoo.com> schrieb im Newsbeitrag news:20050518193919.90114.qmail@web41105.mail.yahoo.com...

Hi --

> I'm thinking of yet another RCR and would like to see if
> anybody likes it. I think this would be a sweet feature.
Here
> are the basic components:

I'm not sure what the canonical definition of syntactic sugar
is, but
I don't think that I think this is exactly that. Anyway....

In my book, syntactic sugar is when you have a more
concise/readable syntax that is equivalent to a more
fundamental (and longer) syntax. So by that definition, all of
the operators that equate to methods are syntactic sugar. I
think this thing I'm talking is similar to adding another
operator.

I would agree the "more concise" part if you mean "shorter" but I don't follow you on the "more readable".

<snip/>

Forgetting .new is a weak reason for making it optional. It
would be
hard to argue that calling MyClass.new to create a new
MyClass object
is arcane or hard to get the hang of.

I know it is weak. But, it did seem to prod me to develop this
idea a little more - because the vast majority of time the only
thing you do with a class is call new.

That might be true for you, but there's a lot of people around that do a lot more with classes than just using them as POF (plain old factory).

+= is already the real +=. It's not the C or Perl +=, but
it's the
real Ruby +=.

Correct. "real" was the wrong word. How about "in-place"? A
normal += in Ruby creates a new object whereas this one
modifies an object in-place.

I like the clean way Ruby does this: you define +, -, * and / and you get +=, +-, *= and /= automatically - and they all behave similarly. Introducing in place modification would greatly complicate the matter - for the ruby runtime as well as for readers of code.

Once I thought, it would have been better to do it the other way round: you can define += and get + for free. But I have abandoned that thought because it does not yield seamless type conversions, i.e. if you add something to a big Fixnum you get a Bignum:

(1<<29).class

=> Fixnum

(1<<29+1).class

=> Bignum

Although C++ folks might argue that this sacrifices performance (and they would be right) I prefer the cleaner approach of Ruby and wait for faster hardware / Ruby runtimes; apart from that I either use a C extension or another language if I am in desparate need for speed.

<snip/>

To a large extent, my reaction to all of these examples comes
down, as
it often does, to the question: how would I explain this to
someone to
whom I was teaching Ruby (in person or in writing)? I think
I would
find it quite difficult to explain that in this:

   s = "hi"
   t = s
   (s) = "bye"

I think this is a good example to show the difference.
Programmers need to understand the difference shown above. One
assigns to a variable and one assigns into an object that a
variable has. Both are normal things you would want to do and
both are assignment-like.

"an object that a variable has"? Variables refer objects but they don't exactly *have* them. I have to admit that I find the third line of the example above very confusing. One of the reasons might be that there is a quite similar construct - apparently with different semantics:

(a,b)=%w{a b}

=> ["a", "b"]

a

=> "a"

b

=> "b"

(a,)=%w{a b}

=> ["a", "b"]

a

=> "a"

<snip/>

Similarly, obj() requires one to figure out whether or not
there is a
method called obj, and if there isn't to try to figure out
what has
been defined as () for this particular object.

Good point. It is similar to differentiating between a local
variable and a method call. Doing the above would do the same
between a method call and calling the default method in an
object:

xyz - could be a local variable (default) or a method call
xyz() - could be a method call (default) or a null method call
(xyz)() - definitely a null method call

I fear that's the
slippery slope to what people call "write-only code".

You could argue that. That is what Java designers said. And
that is why they didn't allow operator overloading. I think
operator is a good thing, but someone can easily abuse it and
make write-only code. I think what I'm suggesting here is like
adding more operators.

IMHO Ruby has the right balance between no operator overloading at all (Java) and too much operator overloading (C++).

Another note: as far as I remember there are plans to make () work for lambdas. So currently we have

f = lambda {|x| x+x}

=> #<Proc:0x100c3e60@(irb):15>

f[10]

=> 20

f.call 10

=> 20

Then we had additionally

f(10)

=> 20

(Please correct me someone if I'm wrong here.) At the moment I don't fully overlook the consequences and whether there were collisions but I have the feeling that there might be - if not technical then at least in the area of understanding and clearness.

That's my 0.02 EUR...

Kind regards

    robert

···

--- "David A. Black" <dblack@wobblini.net> wrote:

On Thu, 19 May 2005, Eric Mahurin wrote:

Hi --

To a large extent, my reaction to all of these examples comes
down, as
it often does, to the question: how would I explain this to
someone to
whom I was teaching Ruby (in person or in writing)? I think
I would
find it quite difficult to explain that in this:

   s = "hi"
   t = s
   (s) = "bye"

I think this is a good example to show the difference.
Programmers need to understand the difference shown above. One
assigns to a variable and one assigns into an object that a
variable has. Both are normal things you would want to do and
both are assignment-like.

There's no checklist of "normal things" in this context. Programming
languages are allowed to be designed differently :slight_smile:

In Ruby's object/reference (variable) model, changing the object via
assignment is *not* normal, and not something I would want to do.
Assignment is to variables, and variables hold references to objects
(or, in some exceptional cases like Fixnums, they are immediate
values).

Why do some of you think that everything I suggest has to do
with references?

I don't think that of everything you suggest, but in this case:

   s = "hi"
   (s) = "bye" # changing the object to which s is a reference

it seems to be clearly a way of adding that extra level of remove.

David

···

On Thu, 19 May 2005, Eric Mahurin wrote:

--- "David A. Black" <dblack@wobblini.net> wrote:

--
David A. Black
dblack@wobblini.net

>> To a large extent, my reaction to all of these examples
comes
>> down, as
>> it often does, to the question: how would I explain this
to
>> someone to
>> whom I was teaching Ruby (in person or in writing)? I
think
>> I would
>> find it quite difficult to explain that in this:
>>
>> s = "hi"
>> t = s
>> (s) = "bye"
>
> I think this is a good example to show the difference.
> Programmers need to understand the difference shown above.
One
> assigns to a variable and one assigns into an object that a
> variable has. Both are normal things you would want to do
and
> both are assignment-like.

"an object that a variable has"? Variables refer objects but
they don't
exactly *have* them.

Bad wording - sorry.

I have to admit that I find the third
line of the
example above very confusing. One of the reasons might be
that there is a
quite similar construct - apparently with different
semantics:

>> (a,b)=%w{a b}
=> ["a", "b"]
>> a
=> "a"
>> b
=> "b"

>> (a,)=%w{a b}
=> ["a", "b"]
>> a
=> "a"

And with what I proposed add to that list (assuming the
()/default/null method is replace):

(a) = %w{a b} # a.replace(%w{a b})

TypeError: cannot convert Array into String

(a),(b) = %w{a b} # a.replace("a"); b.replace("b")

=> ["a", "b"]
>> a
=> "a"

This doesn't collide with any syntax, but I could see confusion
especially in light of the existing multi-assignments.

IMHO Ruby has the right balance between no operator
overloading at all
(Java) and too much operator overloading (C++).

Obviously I would like a little more, but Ruby is the closest
to my tastes also.

Another note: as far as I remember there are plans to make ()
work for
lambdas. So currently we have

>> f = lambda {|x| x+x}
=> #<Proc:0x100c3e60@(irb):15>
>> f[10]
=> 20
>> f.call 10
=> 20

Then we had additionally

>> f(10)
=> 20

Cool. So, this sounds like the ()/null operator is being
implemented with procs/lambdas. Now if we could just have it
for the rest of the objects.

···

__________________________________
Yahoo! Mail Mobile
Take Yahoo! Mail with you! Check email on your mobile phone.
http://mobile.yahoo.com/learn/mail

Eric Mahurin wrote:

To a large extent, my reaction to all of these examples comes
down, as
it often does, to the question: how would I explain this to
someone to
whom I was teaching Ruby (in person or in writing)? I think
I would
find it quite difficult to explain that in this:

   s = "hi"
   t = s
   (s) = "bye"

I think this is a good example to show the difference.
Programmers need to understand the difference shown above. One
assigns to a variable and one assigns into an object that a
variable has. Both are normal things you would want to do and
both are assignment-like.

"an object that a variable has"? Variables refer objects but
they don't
exactly *have* them.

Bad wording - sorry.

I have to admit that I find the third
line of the
example above very confusing. One of the reasons might be
that there is a
quite similar construct - apparently with different
semantics:

(a,b)=%w{a b}

=> ["a", "b"]

a

=> "a"

b

=> "b"

(a,)=%w{a b}

=> ["a", "b"]

a

=> "a"

And with what I proposed add to that list (assuming the
()/default/null method is replace):

(a) = %w{a b} # a.replace(%w{a b})

TypeError: cannot convert Array into String

(a),(b) = %w{a b} # a.replace("a"); b.replace("b")

=> ["a", "b"]

a

=> "a"

This doesn't collide with any syntax, but I could see confusion
especially in light of the existing multi-assignments.

And it looks totally ridiculous to me. Why is the instance where you send
the method to *inside* brackets and not *before* them? That's probably as
unobvious as it can get.

IMHO Ruby has the right balance between no operator
overloading at all
(Java) and too much operator overloading (C++).

Obviously I would like a little more, but Ruby is the closest
to my tastes also.

Another note: as far as I remember there are plans to make ()
work for
lambdas. So currently we have

f = lambda {|x| x+x}

=> #<Proc:0x100c3e60@(irb):15>

f[10]

=> 20

f.call 10

=> 20

Then we had additionally

f(10)

=> 20

Cool. So, this sounds like the ()/null operator is being
implemented with procs/lambdas. Now if we could just have it
for the rest of the objects.

It'll probably work as general mapping to #call so it would work with
everything that has #call.

Cheers

    robert