Idiom I've not seen before

I came across this :
  elements.sort_by(&:position).each
in something I've taken over. I thought sort_by
took a block. Obviously I'm missing something.
Can anyone shed some light on this trick?

  ~Rob

You've found Symbol#to_proc

That line is equivalent to:

elements.sort_by {|e| e.send(:position) }.each

The "something" is a Rails project, isn't it?

== vendor/rails/activesupport/lib/active_support/core_ext/symbol.rb ==
class Symbol
   # Turns the symbol into a simple proc, which is especially useful for enumerations. Examples:

···

On Feb 6, 2008, at 10:55 PM, Rob Saul wrote:

I came across this :
  elements.sort_by(&:position).each
in something I've taken over. I thought sort_by
took a block. Obviously I'm missing something.
Can anyone shed some light on this trick?

  ~Rob

   #
   # # The same as people.collect { |p| p.name }
   # people.collect(&:name)
   #
   # # The same as people.select { |p| p.manager? }.collect { |p| p.salary }
   # people.select(&:manager?).collect(&:salary)
   def to_proc
     Proc.new { |*args| args.shift.__send__(self, *args) }
   end
end

I don't know how you can send additional arguments however.

-Rob

Rob Biedenharn http://agileconsultingllc.com
Rob@AgileConsultingLLC.com

Sure. &:position == :position.to_proc == proc { |element, *args|
element.position(*args) }

So elements.sort_by(&:position) is shorthand for sorting the elements
by position.

jeremy

···

On 2/6/08, Rob Saul <wyrd@code-gnomemad.org> wrote:

I came across this :
        elements.sort_by(&:position).each
in something I've taken over. I thought sort_by
took a block. Obviously I'm missing something.
Can anyone shed some light on this trick?

Loosely-related aside:

I've been learning Io[1] recently. Io (like Lisp, I gather) allows
lazy evaluation of method arguments. In Ruby terms, this would mean
that I can write something like this:

  # Ruby-esque pseudo-code; neither Io nor Ruby
  class Array
    def select
      result =
      self.each{ |el| result << el if el.sendArg(0) }
      result
    end
  end

  my_array.select( isCool? )
  my_array.select( > 5 )
  my_array.select( roughlyEquals( jim.newSize ) )

In Io, the "isCool?" method/message isn't (necessarily) evaluated when
you call the select method. Instead, you can perform some
introspection on the parsed message tree for each argument and choose
to ignore it, change it to a string, or send it as a message to any
object you want.

To be clear, in the above, the "isCool?" message/method would be send/
invoked on each array element. Or each element would be sent a
">( 5 )" message.

The Symbol#to_proc technique is clever, but not quite as clean as
being able to write (in Io):
  elements sortBy( position )
  elements map( * 2 )
  elements select( size > 5 )

This same ability in Io allowed me to add a debugging method "p" (in
homage of Ruby) that labels a value with the exact call you made. For
example:

  # In Io with my custom method (not Ruby)
  p( gk )
  #=> gk is Person_0x4de0d8:
  #=> name = "Gavin Kistner"
  #=> nick = "Phrogz""

  p( gk name )
  #=> gk name is Gavin Kistner

  p( gk nick size )
  #=> gk nick size is 6

[1] http://www.iolanguage.com/

···

On Feb 6, 8:55 pm, Rob Saul <w...@code-gnomemad.org> wrote:

I came across this :
        elements.sort_by(&:position).each

Jeremy Kemper wrote:

Sure. &:position == :position.to_proc == proc { |element, *args|
element.position(*args) }

So elements.sort_by(&:position) is shorthand for sorting the elements
by position.

jeremy

thanks to both Jeremy and Rob for the enlightenment. And
yes, it is a Rails project.

Buyer beware:

#!/usr/local/bin/ruby -w

# # of iterations = 10000
# user system total real
# null_time 0.000000 0.000000 0.000000 ( 0.001561)
# map 0.880000 0.000000 0.880000 ( 0.878500)
# to_proc 2.790000 0.000000 2.790000 ( 2.799291)

# # of iterations = 100000
# user system total real
# null_time 0.020000 0.000000 0.020000 ( 0.013951)
# map 8.730000 0.010000 8.740000 ( 8.766693)
# to_proc 27.960000 0.030000 27.990000 ( 28.001288)

require 'benchmark'

class Symbol
   def to_proc
     Proc.new { |*args| args.shift.__send__(self, *args) }
   end
end

a = (1..100).to_a

raise "stupid" unless a.map { |n| n.to_s } == a.map(&:to_s)

max = (ARGV.shift || 1_000_000).to_i

puts "# of iterations = #{max}"
Benchmark::bm(20) do |x|
   x.report("null_time") do
     for i in 0..max do
       # do nothing
     end
   end

   x.report("map") do
     for i in 0..max do
       a.map { |n| n.to_s }
     end
   end

   x.report("to_proc") do
     for i in 0..max do
       a.map(&:to_s)
     end
   end
end

···

On Feb 6, 2008, at 20:04 , Rob Biedenharn wrote:

You've found Symbol#to_proc

That line is equivalent to:

elements.sort_by {|e| e.send(:position) }.each

Phrogz wrote:

I've been learning Io[1] recently. Io (like Lisp, I gather) allows
lazy evaluation of method arguments.

...

In Io, the "isCool?" method/message isn't (necessarily) evaluated when
you call the select method.

This is known as "call by name", as opposed to by reference or by value.
It was, more than anything, the single thing that caused the most difficulty
for the authors of Algol68 optimising compilers :-).

Niklaus Wirt once joked that Europeans (who know how to pronounce his name)
called him by name, whereas Americans called him by value (nickel's worth).
Maybe apocryphal, but funny :-).

Clifford Heath.

Hello Rob

  We can send arguments, Following is the way, But it throws an error.

  def to_proc
    Proc.new { |*args| args.shift.__send__(self, *args) }
  end

  arr.collect(&[:concat,"Rocking"])

* *sample.rb:41:in `__send__': [:concat, "dee"] is not a symbol
(TypeError)
    from sample.rb:41:in `to_proc'
    from sample.rb:51:in `collect'
    from sample.rb:51

But if we change the implementation of to_proc then it works.

   def to_proc
      lambda {|target| target.send *self}
   end

   arr.collect(&[:concat,"Rocking"])

o/p => ["rubyRocking", "railsRocking", "rjaxRocking"]

I don't know how.

···

On Feb 6, 2008 8:04 PM, Rob Biedenharn <Rob@agileconsultingllc.com> wrote:

On Feb 6, 2008, at 10:55 PM, Rob Saul wrote:
> I came across this :
> elements.sort_by(&:position).each
> in something I've taken over. I thought sort_by
> took a block. Obviously I'm missing something.
> Can anyone shed some light on this trick?
>
> ~Rob

You've found Symbol#to_proc

That line is equivalent to:

elements.sort_by {|e| e.send(:position) }.each

The "something" is a Rails project, isn't it?

== vendor/rails/activesupport/lib/active_support/core_ext/symbol.rb ==
class Symbol
  # Turns the symbol into a simple proc, which is especially useful
for enumerations. Examples:
  #
  # # The same as people.collect { |p| p.name }
  # people.collect(&:name)
  #
  # # The same as people.select { |p| p.manager? }.collect { |p|
p.salary }
  # people.select(&:manager?).collect(&:salary)
  def to_proc
    Proc.new { |*args| args.shift.__send__(self, *args) }
  end
end

I don't know how you can send additional arguments however.

-Rob

Rob Biedenharn http://agileconsultingllc.com
Rob@AgileConsultingLLC.com

Io (like Lisp, I gather) allows lazy evaluation of method arguments.

Are you referring to Lisp's macro facility? AFAIK macros are usually
expanded at load/compile time. Since everything is a s-expression in
lisp, the macro gets its arguments as list and can process them. The
macro call is then replaced with the result. Or something along this
line.

It seems Io provides a similar facility to treat code as data, which
is cool. Unfortunately I'm not able to compile it. IIRC Forth was able
to do something similar too BTW (just a side-note).

Since ruby doesn't have this feature (unless you use parsetree and
ruby2ruby maybe), the ruby equivalent are strings, I'd say. One could
thus write something like:

    class Array
        def select_by(snippet)
            rv =
            self.each do |e|
                rv << e if e.instance_eval '%s %s' % [e.inspect,
snippet]
            end
            rv
        end
    end

    a = [1,2,3,4,5]
    a.select_by '> 3'

Which is about the same as your io example. It would be nice of course
to be able to have real macros that are expanded at load time so that
one could write something like the following which is incorrect ruby
syntax and would have to be rewritten before the parser tries to make
sense out of it:

    module Macros
        def select_by(code)
            %{select {|e| e #{code}}}
        end
    end

    a = [1,2,3,4,5]
    a.select_by(> 3)

This would require that macro names are unique though. But it's just a
delirious day-dreaming anyway.

Regards,
Thomas.

Also, I believe Symbol.to_proc is making it into 1.9

martin

···

On Feb 7, 2008 4:14 AM, Rob Saul <wyrd@code-gnomemad.org> wrote:

thanks to both Jeremy and Rob for the enlightenment. And
yes, it is a Rails project.

that will change in ruby1.9 since to_proc is builtin.
eg, a run in windows,

C:\ruby1.9\bin>.\ruby.exe test.rb 1_000
# of iterations = 1000
                          user system total real
null_time 0.000000 0.000000 0.000000 ( 0.000000)
map 0.203000 0.000000 0.203000 ( 0.203000)
to_proc 0.156000 0.000000 0.156000 ( 0.172000)

C:\ruby1.9\bin>.\ruby.exe test.rb 10_000
# of iterations = 10000
                          user system total real
null_time 0.016000 0.000000 0.016000 ( 0.015000)
map 1.937000 0.000000 1.937000 ( 2.515000)
to_proc 1.610000 0.000000 1.610000 ( 1.954000)

C:\ruby1.9\bin>.\ruby.exe test.rb 100_000
# of iterations = 100000
                          user system total real
null_time 0.328000 0.000000 0.328000 ( 0.328000)
map 19.344000 0.000000 19.344000 ( 24.953000)
to_proc 17.296000 0.015000 17.311000 ( 18.016000)

not bad, imho.
kind regards -botp

···

On Feb 7, 2008 1:16 PM, Ryan Davis <ryand-ruby@zenspider.com> wrote:

Buyer beware:

# # of iterations = 10000
# user system total real
# null_time 0.000000 0.000000 0.000000 ( 0.001561)
# map 0.880000 0.000000 0.880000 ( 0.878500)
# to_proc 2.790000 0.000000 2.790000 ( 2.799291)

# # of iterations = 100000
# user system total real
# null_time 0.020000 0.000000 0.020000 ( 0.013951)
# map 8.730000 0.010000 8.740000 ( 8.766693)
# to_proc 27.960000 0.030000 27.990000 ( 28.001288)

Yes, it is in.
Raganwald has a good blog entry about it, he also mentions
String#to_proc:
http://weblog.raganwald.com/2007/11/fun-with-symboltoproc.html

···

On Thu, Feb 7, 2008 at 6:04 AM, Martin DeMello <martindemello@gmail.com> wrote:

Also, I believe Symbol.to_proc is making it into 1.9