Symbol#to_proc is just so beautiful

There must be a way to ditch that '&' somehow. I'd prefer this:

[1,2,3].all?(:positive?)

Dan

···

-----Original Message-----
From: Daniel Schierbeck [mailto:daniel.schierbeck@gmail.com]
Sent: Wednesday, April 19, 2006 10:27 AM
To: ruby-talk ML
Subject: Symbol#to_proc is just so beautiful

When is this ever getting into Ruby Core?

   class Symbol
     def to_proc
       proc{|obj| obj.send(self) }
     end
   end

Consider this:

   class Numeric
     def positive?
       self > 0
     end
   end

   [1, 2, 3].all? &:positive? => true
   [-1, 2, 3].all? &:positive? => false

It's just so damn beautiful!

It's possible be adjusting the definition of Enumerable#all?

  $ cat test.rb
  class Symbol
    def to_proc
      proc{ |obj| obj.send(self) }
    end
  end

  module Enumerable
    alias :old_all? :all?

    def all?(non_block=nil, &block)
      if non_block
        if block.nil? and non_block.respond_to?(:to_proc)
          block = non_block.to_proc
        else
          raise ArgumentError
        end
      end
      old_all? &block
    end
  end

  class Numeric
    def positive?
      self > 0
    end

    def even?
      (self % 2).zero?
    end
  end

  puts [1, 2, 3].all?
  puts [1, 2, 3].all? { |i| i < 4 }
  puts [1, 2, 3].all? { |i| i > 2 }
  puts [1, 2, 3].all?( :positive? )
  puts [1, 2, 3].all?( :even? )

  $ ruby test.rb
  true
  true
  false
  true
  false

I'm not sure if there's anyway to do it without redefining the target
function, however.

Jacob Fugal

···

On 4/19/06, Berger, Daniel <Daniel.Berger@qwest.com> wrote:

> -----Original Message-----
> From: Daniel Schierbeck [mailto:daniel.schierbeck@gmail.com]
>
> When is this ever getting into Ruby Core?
>
> class Symbol
> def to_proc
> proc{|obj| obj.send(self) }
> end
> end
>
> Consider this:
>
> class Numeric
> def positive?
> self > 0
> end
> end
>
> [1, 2, 3].all? &:positive? => true
> [-1, 2, 3].all? &:positive? => false

There must be a way to ditch that '&' somehow. I'd prefer this:

[1,2,3].all?(:positive?)

Berger, Daniel wrote:

   [1, 2, 3].all? &:positive? => true
   [-1, 2, 3].all? &:positive? => false

There must be a way to ditch that '&' somehow. I'd prefer this:
[1,2,3].all?(:positive?)

The beauty of Symbol#to_proc is that you don't need to change any method definitions for it to work; any method that yields a single argument can take a symbol proc as a block, and it'll work out of the box.

   arr.any? &:positive?
   arr.none? &:positive?
   arr.each(&:upcase!)
   arr.collect(&:downcase)

Daniel

Berger, Daniel wrote:

There must be a way to ditch that '&' somehow. I'd prefer this:

[1,2,3].all?(:positive?)

What you are suggesting is basically an expansion of something that was
suggested in RCR50 and rejected.
(RCRS)

I like the idea too. It would be even better if you could also pass
parameters along.

Here are a few more examples:

[1,2,3].any?(:>, 4)

[1,2,3].map(:*, 4) #-> [4,8,12]

people.collect(:name)

people.select(:retired?)

people.sort_by(:age)

favorite_numbers.select(:between?, 100,200)

people.each(:give_compensation, 50)

Here's the implementation:
# enumerable-rcr50.rb
class Array

    enumerable_methods = [:each,:select, :find_all, :map, :collect,
:any?, :all?,
                          :sort_by, :reject, :detect, :find, :find_all]
                        # max_by, min_by, partition_by can be added in
Ruby 1.9
    for method_name in enumerable_methods
        old_method_name = "old_" + method_name.to_s
        module_eval %Q{
            alias_method :#{old_method_name}, :#{method_name}
            def #{method_name}(message = nil, *args, &block)
                block ||= Proc.new { |e| e.send(message, *args) }
                if message or block_given?
                    #{old_method_name}(&block)
                else # method was called without passing any
parameters, nor a block
                    #{old_method_name}
                end
            end
        }
    end

    alias_method(:do, :each)
    alias_method(:in_order_of, :sort)
end

Basically, if the callers passes a block, the original method is called.
If he specifies a message and some arguements, they are converted into a
block and passed to the original method. If he gives both, the block is
used and the message is ignored. If neither is given (as in:
[1,2,3].map), the original method is called with no parameters.

Take note that :detect (and its alias :find) accept an additional
parameter (representing what should be returned if nothing is found), so
changing them WILL break existing code.

Now, I need help. As you may notice - the above only extends Array. If
you change "class Array" to "module Enumerable" in the above code, it
turns out that Ruby (1.8.4) refuses to let you override methods such as
map, while at the same time, "any?" and "all?" work fine. Anyone got a
clue as to how I could fix this?

P.S. for other approaches to this problem, see the Higher Order
Messaging in Ruby articles by Nat Pyrce
(http://nat.truemesh.com/archives/000535.html\), the Junction library,
and Ruby Facets.

···

--
Posted via http://www.ruby-forum.com/\.

An approach similar to Facets' Enumerable#every would look better

arry.any?.positive?
arr.none?.positive?
arr.each.upcase!
arry.collect.downcase # arry.every.downcase

But it would require modification of all those methods, like Jacob
says. It is worthwhile, in my opinion.

···

2006/4/19, Daniel Schierbeck <daniel.schierbeck@gmail.com>:

Berger, Daniel wrote:
>> [1, 2, 3].all? &:positive? => true
>> [-1, 2, 3].all? &:positive? => false
> There must be a way to ditch that '&' somehow. I'd prefer this:
> [1,2,3].all?(:positive?)

The beauty of Symbol#to_proc is that you don't need to change any method
definitions for it to work; any method that yields a single argument can
take a symbol proc as a block, and it'll work out of the box.

   arr.any? &:positive?
   arr.none? &:positive?
   arr.each(&:upcase!)
   arr.collect(&:downcase)

--
Gerardo Santana
"Between individuals, as between nations, respect for the rights of
others is peace" - Don Benito Juárez

Omer Raviv wrote:

Berger, Daniel wrote:

There must be a way to ditch that '&' somehow. I'd prefer this:

[1,2,3].all?(:positive?)
    
What you are suggesting is basically an expansion of something that was suggested in RCR50 and rejected. (RCRS)
  

Heh, I'd forgotten it was ever suggested before.

I like the idea too. It would be even better if you could also pass parameters along.

Here are a few more examples:

[1,2,3].any?(:>, 4)

[1,2,3].map(:*, 4) #-> [4,8,12]

people.collect(:name)

people.select(:retired?)

people.sort_by(:age)

favorite_numbers.select(:between?, 100,200)

people.each(:give_compensation, 50)

Here's the implementation:
  
<snip>

Here's my implementation for Array#map.

class Array
   def map(*args)
      array =
      hash = {}
      current = nil
      args.each{ |arg|
         if arg.is_a?(Symbol)
            hash[arg] =
            current = arg
         else
            hash[current] << arg
         end
      }

      self.each{ |e|
         hash.each{ |m, params|
            unless params.empty?
               e = e.send(m, *params)
            else
               e = e.send(m)
            end
         }
         yield e if block_given?
         array << e
      }

      array
   end
end

(Logan Capaldo refactored this somewhat)

Now, I need help. As you may notice - the above only extends Array. If you change "class Array" to "module Enumerable" in the above code, it turns out that Ruby (1.8.4) refuses to let you override methods such as map, while at the same time, "any?" and "all?" work fine. Anyone got a clue as to how I could fix this?
  

Array#map is overridden in array.c, so modifying it in Enumerable won't affect Array.

Regards,

Dan

Gerardo Santana Gómez Garrido wrote:

An approach similar to Facets' Enumerable#every would look better

arry.any?.positive?
arr.none?.positive?

These already have another meaning in 1.8. any?() without a block returns true if any of the elements is a true value.

arr.each.upcase!
arry.collect.downcase # arry.every.downcase

These will have another meaning in 1.9. each() and collect() without a block will return enumerators.

···

--
http://flgr.0x42.net/