Syntax error when redefining >> operator to take a block

J

John Woods

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.

This code illustrates my problem:

class C
def >>(&block)
block.call("inside >>")
end
end

c = C.new
c.>> { |x| puts x } # outputs "inside >>"
#c >> { |x| puts x } # syntax error, if uncommented

This is the syntax error reported:

test.rb:10: syntax error, unexpected '|', expecting '}'
c >> { |x| puts x } # syntax error, if uncommented
^
test.rb:10: syntax error, unexpected tIDENTIFIER, expecting kDO or '{'
or '('
c >> { |x| puts x } # syntax error, if uncommented
^

Any pointers? Thanks.
 
R

Robert Klemme

2007/10/12 said:
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.

This code illustrates my problem:

class C
def >>(&block)
block.call("inside >>")
end
end

c = C.new
c.>> { |x| puts x } # outputs "inside >>"
#c >> { |x| puts x } # syntax error, if uncommented

This is the syntax error reported:

test.rb:10: syntax error, unexpected '|', expecting '}'
c >> { |x| puts x } # syntax error, if uncommented
^
test.rb:10: syntax error, unexpected tIDENTIFIER, expecting kDO or '{'
or '('
c >> { |x| puts x } # syntax error, if uncommented
^

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.

robert
 
J

John Woods

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

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

John Joyce

Generally, you don't need to try and create or override operators in
Ruby.
It's just not worth the trouble.
It's a lot easier (probably intentionally) to just create a normal
method or function.
It's not C++
Try to make it readable by humans.
 
J

John Woods

Thanks guys, I now understand much better what's going on with my
attempt to redefine >>. This list is great!
 
R

Rick DeNatale

Hi,



I think the problem is summed up shortly in another reply, but --

arguments are treated in a special manner (e.g. the arity of a method
which takes only a block is 0), and thus aren't valid as the other
operand of a binary operator!

Note from the post which started this thread:

c.>> { |x| puts x } # outputs "inside >>"
#c >> { |x| puts x } # syntax error, if uncommented

It really doesn't have to do with the arity of the method. It's the
way the statement is parsed, the parser doesn't check arity, in fact I
don't think it could if it wanted to, it doesn't know what method
would be bound to :>> for an arbitrary value of c.

That '.' on the first line makes all the difference, The c >> x is
syntactic sugar for sure which the parser turns into what gets
evaluated effectively as c.>>(x) but since the parser doesn't see a
valid x it results in a syntax error.
 
R

Robert Klemme

Note from the post which started this thread:

c.>> { |x| puts x } # outputs "inside >>"
#c >> { |x| puts x } # syntax error, if uncommented

It really doesn't have to do with the arity of the method. It's the
way the statement is parsed, the parser doesn't check arity, in fact I
don't think it could if it wanted to, it doesn't know what method
would be bound to :>> for an arbitrary value of c.

That '.' on the first line makes all the difference, The c >> x is
syntactic sugar for sure which the parser turns into what gets
evaluated effectively as c.>>(x) but since the parser doesn't see a
valid x it results in a syntax error.

Yeah, and that's why operators are special in a way when it comes to
arity. Although you can define them with arbitrary arity the normal
usage of a binary operator enforces exactly one argument (plus self of
course) and thus practically enforces arity one - even if you can pass
more arguments when using dot notation.


irb(main):001:0> def +(*a)p a end
=> nil
irb(main):002:0> self.+ 1,2,3
[1, 2, 3]
=> nil
irb(main):003:0> self + 1
[1]
=> nil

Kind regards

robert
 

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

Forum statistics

Threads
473,772
Messages
2,569,593
Members
45,111
Latest member
KetoBurn
Top