On Tue, Feb 06, 2007 at 07:33:20AM +0900, dblack / wobblini.net wrote:
> > p1 = Proc.new { |x=nil,y=nil| ... }   # accept 2 or fewer args
> > p2 = Proc.new { |x, y, *z| ...    }   # accept 2 or more args
> > p3 = Proc.new { |*x| ...          }   # no more single-arg special case!
> >
> >That is, use the same argument list handling for a 'def' method 
> >definition,
> >a block, and a Proc/proc/lambda.
> 
> I think the reason for there not being default arguments in code
> blocks is the ambiguity of the pipe character:
> 
>   {| x=1 | 2 | 3 }  # {(x=1) 2 | 3 }  or  {(x=1|2) 3 }  ?
> 
> Dave Thomas suggested something at RubyConf 2005 along the lines of:
> 
>   def m(a,b,c)    # method
>   end
> 
>   def (a,b,c)     # anonymous function
>   end
> 
> I'm not sure what happened to that; I haven't seen it mentioned much
> in discussions of this stuff.

But make this into block syntax and you still have potential ambiguities:

   3.times { (i=nil) puts i }

   3.times do (i=nil)
     puts i
   end

(i=nil) is a valid expression/statement in its own right, so you'd need some
more lexical rules to disambiguate this from an argument list. I'd hate more
whitespace-sensitive rules to be added, e.g.

   {(i) ... }       # block args
   { (i) ... }      # expression :-(

Perhaps you use parentheses, and if a block starts with an open-parenthesis
then it's always an argument list. If you want it otherwise, you have to
explicitly avoid it:

   3.times { nil, (a=b, c=d) }

It seems the rest of the ASCII set has been used already; e.g.
angle-brackets would have the same problem.

   3.times { < i = j > 4 > puts i }

Incidentally, I do see a reason for not doing full argument-checking in
blocks: there are methods which pass block arguments which it's convenient
for the block to ignore if it doesn't want them.

   3.times { puts "hello" }
   3.times { |x| puts "hello #{x}" }

You could get around this by:

(a) having different methods which pass or do not pass arguments:

   3.times { puts "hello" }
   3.times_i { |x| puts "hello #{x}" }

(b) making such arguments explicitly ignorable at the caller side, which I
think I prefer. At worst:

   class Integer
     def times(&blk)
       if blk.arity == 1
         ... loop, yield with index
       else
         ... loop, yield without index
       end
     end
   end

Regards,

Brian.