From: Robert Klemme Date: 2007-10-13T07:40:09+09:00 Subject: Re: Syntax error when redefining >> operator to take a block 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.>> {}' [] # 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.>>(){}' [] # 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|}' [] # robert@fussel ~ $ ruby -e 'def self.>>(*a,&b) p a,b end; self.>>() {|x|}' [] # 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