Robert Klemme
10/12/2007 10:37:00 PM
On 12.10.2007 20:22, John Woods wrote:
>>> I'm trying to redefine the >> operator for a particular class such that
>>> it takes a block as its argument. It works if I invoke the redifined >>
>>> operator using "." syntax, but causes a syntax error otherwise.
>>>
> >> [...snip...]
>>>
>>> Any pointers? Thanks.
>>
>> Redefining operator behavior cannot change Ruby's syntax. >> is with
>> a block is just not valid Ruby syntax - as you have seen.
>
> I don't understand why it's invalid syntax. I'm new to Ruby and am
> looking to better understand.
>
> Please consider these two different definitions of >>
>
> def >>(arg) ....
> def >>(&arg) ....
>
> and these two different invocations of >>
>
> c >> { }
> c.>> { }
>
> In the following example, the first invocation works with the first
> definition, and the second with the second. However the first/second
> invocation doesn't work second/first definition, respectively.
>
> class C
> def >>(arg)
> puts arg.class
> end
> end
>
> c = C.new
>
> c >> { } # outputs 'Hash'
> #c.>> { } # syntax error: wrong number of args (0 for 1)
>
> class C
> def >>(&arg)
> puts arg.class
> end
> end
>
> c.>> { } # outputs 'Proc'
> #c >> { } # syntax error: wrong number of args (1 for 0)
>
> It seems that if the operator is invoked with only optional whitespace
> between the receiver "c" and the operator ">>" then ruby interprets the
> following { ... } to be a hash, and if the operator is invoked with a
> "." then ruby interprets { ... } to be a proc. And this appears to be
> regardless of what's actually between the braces, or regardless of how
> the argument to >> is defined (ie with or without the "&").
I cannot be different because - as I have said before - a method /
operator (re-)definition cannot change the syntax of the language. For
syntax it is completely irrelevant how you define a method / operator.
Here are some examples:
robert@fussel ~
$ ruby -e 'def self.>>(*a,&b) p a,b end; self.>> {}'
[]
#<Proc:0x00000000@-e:1>
robert@fussel ~
$ ruby -e 'def self.>>(*a,&b) p a,b end; self.>>({})'
[{}]
nil
robert@fussel ~
$ ruby -e 'def self.>>(*a,&b) p a,b end; self.>>(){}'
[]
#<Proc:0x00000000@-e:1>
robert@fussel ~
$ ruby -e 'def self.>>(*a,&b) p a,b end; self >> {}'
[{}]
nil
robert@fussel ~
$ ruby -e 'def self.>>(*a,&b) p a,b end; self.>> {|x|}'
[]
#<Proc:0x00000000@-e:1>
robert@fussel ~
$ ruby -e 'def self.>>(*a,&b) p a,b end; self.>>() {|x|}'
[]
#<Proc:0x00000000@-e:1>
robert@fussel ~
$ ruby -e 'def self.>>(*a,&b) p a,b end; self >> {|x|}'
-e:1: syntax error, unexpected '|', expecting '}'
def self.>>(*a,&b) p a,b end; self >> {|x|}
^
When you use the dot notation then the expression is a normal method
invocation just with an unusual method name. So all the normal method
invocation syntax applies and in that case {} is interpreted as block -
no matter what.
When you use the operator syntax (i.e. no dot) Ruby tries to parse the
right hand side as an expression, which could only be a Hash in this
case since a block is not an expression. You need an expression on the
right hand side because >> is a binary operator.
> I'm wondering why ruby doesn't instead interpret { ... } to be either a
> hash or a proc based on what's inside the braces. For example, { 1 =>
> "one" } is a hash, and { |x| x + x } is a proc. It seems to me this
> distinction should be made by what's between the braces, and not whether
> there's a " " or "." between the receiver and the operator. Then, if
> there's a mismatch between what's being passed and the operator
> definition, I would expect an error.
And what is {}? It's both a valid Hash and block. And actually Ruby
does parse accordingly:
robert@fussel ~
$ ruby -ce 'a >> do |x| end'
-e:1: syntax error, unexpected kDO
a >> do |x| end
^
robert@fussel ~
$ ruby -ce 'a >> {|x| }'
-e:1: syntax error, unexpected '|', expecting '}'
a >> {|x| }
^
robert@fussel ~
$ ruby -ce 'a >> {}'
Syntax OK
> So is this an area where Ruby's parsing could be improved to enable
> passing a block to an operator (without having to use "." to invoke the
> operator)? Or, am I misunderstanding something? Any insight would be
> appreciated.
I would not consider it an improvement since I don't see a point in
passing a block to an operator. I don't see need for improvement here.
Kind regards
robert