Functor Opinion

So here's the Functor class:

  class Functor < BasicObject
    def initialize(*obj, &func)
      @obj = obj
      @func = func
    end
    def method_missing(op, *args)
      if @obj.empty?
        @func.call(op, *args)
      else
        @func.call(op, *(@obj + args))
      end
    end
  end

Anyone who has seen the older version of this cllas will notice that I
have added the *obj parameter. This allows the Functor to act like a
simple delegator too. But strictly speaking it isn't at all necessary
since one could just use the object(s) passed to the initialize
directly in the block. Eg.

  foo = 1
  Functor.new(foo) { |op, obj, *args| obj.send(op, *args) }

is the exact same as

  foo = 1
  Functor.new { |op, *args| foo.send(op, *args) }

The only time this really is a little more handy is when passing
'self', because of the ambiguity between contexts. Eg.

  Functor.new(self) { |op, obj, *args| obj.send(op, *args) }

vs.

  slf = self
  Functor.new { |op, *args| slf.send(op, *args) }

Not a big deal but there it is. Is there any other reason anyone can
think of that the *obj parameter is acctually needed or more convenient
vs. direct reference in the block itself? If not, is it worth keeping,
or should I just get rid of the *obj parameter altogether?

Settling that issue, I'd like to request that the Functor class be made
part of Ruby's standard library. It is simple, generic and generally
useful --I have had use for it myself on more than a few occasions now
across multiple projects.

Thanks,
T.

I'm not the one who ultimately will decide whether something goes into Ruby's standard lib or not. But I have doubts in this case. I cannot recognize a clear cut task of this class; it's very generic and doesn't really do much apart from propagating (delegating) arbitrary method calls. I'm not even sure that the name is appropriately chosen. If one looks at the definition in Wikipedia at Function object - Wikipedia a functor needs at least the syntactic looks of a function. In the case of Ruby, implementing # and / or #call seems to be a minimum requirement.

Can you elaborate a bit more the benefits and use cases of this class? Thanks!

Kind regards

    robert

···

Trans <transfire@gmail.com> wrote:

So here's the Functor class:

class Functor < BasicObject
   def initialize(*obj, &func)
     @obj = obj
     @func = func
   end
   def method_missing(op, *args)
     if @obj.empty?
       @func.call(op, *args)
     else
       @func.call(op, *(@obj + args))
     end
   end
end

Anyone who has seen the older version of this cllas will notice that I
have added the *obj parameter. This allows the Functor to act like a
simple delegator too. But strictly speaking it isn't at all necessary
since one could just use the object(s) passed to the initialize
directly in the block. Eg.

foo = 1
Functor.new(foo) { |op, obj, *args| obj.send(op, *args) }

is the exact same as

foo = 1
Functor.new { |op, *args| foo.send(op, *args) }

The only time this really is a little more handy is when passing
'self', because of the ambiguity between contexts. Eg.

Functor.new(self) { |op, obj, *args| obj.send(op, *args) }

vs.

slf = self
Functor.new { |op, *args| slf.send(op, *args) }

Not a big deal but there it is. Is there any other reason anyone can
think of that the *obj parameter is acctually needed or more
convenient vs. direct reference in the block itself? If not, is it
worth keeping, or should I just get rid of the *obj parameter
altogether?

Settling that issue, I'd like to request that the Functor class be
made part of Ruby's standard library. It is simple, generic and
generally useful --I have had use for it myself on more than a few
occasions now across multiple projects.

Hi --

So here's the Functor class:

class Functor < BasicObject
   def initialize(*obj, &func)
     @obj = obj
     @func = func
   end
   def method_missing(op, *args)
     if @obj.empty?
       @func.call(op, *args)
     else
       @func.call(op, *(@obj + args))
     end
   end
end

Anyone who has seen the older version of this cllas will notice that I
have added the *obj parameter. This allows the Functor to act like a
simple delegator too. But strictly speaking it isn't at all necessary
since one could just use the object(s) passed to the initialize
directly in the block. Eg.

foo = 1
Functor.new(foo) { |op, obj, *args| obj.send(op, *args) }

is the exact same as

foo = 1
Functor.new { |op, *args| foo.send(op, *args) }

The only time this really is a little more handy is when passing
'self', because of the ambiguity between contexts. Eg.

Functor.new(self) { |op, obj, *args| obj.send(op, *args) }

vs.

slf = self
Functor.new { |op, *args| slf.send(op, *args) }

You shouldn't need to do that, though; self will be preserved from the
context of the block's creation:

     class C
     def f(&b)
       b.call
     end
   end

   C.new.f { puts self } # main

David

···

On Sun, 19 Mar 2006, Trans wrote:

--
David A. Black (dblack@wobblini.net)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

"Ruby for Rails" chapters now available
from Manning Early Access Program! Ruby for Rails

Can you give example of where you used it?

James Edward Gray II

···

On Mar 19, 2006, at 12:33 AM, Trans wrote:

Settling that issue, I'd like to request that the Functor class be made
part of Ruby's standard library. It is simple, generic and generally
useful --I have had use for it myself on more than a few occasions now
across multiple projects.

Can you elaborate a bit more the benefits and use cases of this class?
Thanks!

Sure.

I'm aware that technically a Functor is nothing more than a first class
function. But Ruby already has a class for this, Proc (not to mention
Method). The class I present could more precisely be labeld a
"MetaFunctor". But since there's nothing of use to define as Functor in
Ruby I just dropped the "Meta" prefix. I'm all ears, if anyone has a
truly better name.

One could define #call and # for this class too, but we don't really
want to, because we want to keep as many methods available for passing
thru to method_missing as possibe. (Hence the subclass of BasicObject,
ie. BlankSlate).

I generally use it to create a separate method space for a class.
Here's an example of one place I've used it:

# facet/kernel/__meta__.rb

begin
   require 'calibre/functor'
rescue LoadError
   require 'facet/functor'
end

class Object

  # Returns a Functor that allows one to call any
  # Kernel method bound to self.

···

#
  # class A
  # def object_id ; "OBTUSE" ; end
  # end
  #
  # c = C.new
  # c.object_id #=> "OBTUSE"
  # c.__meta__.object_id #=> 6664875832
  #
  def __meta__
    @__meta__ ||= Functor.new do |meth, *args| # &blk|
      Kernel.instance_method(meth).bind(self).call(*args) # ,&blk)
    end
  end

end

Additionally, the Functor can also be used as a quick and simple
delegator or proxy.

T.

You shouldn't need to do that, though; self will be preserved from the
context of the block's creation:

    class C
    def f(&b)
      b.call
    end
  end

  C.new.f { puts self } # main

Thanks David I stand corrected on that point. I was confusiing it with
define_method blocks.

So even less reason to keep the *obj parameter? Hmm...if I added a
method to access those objects, then at least it could be used as
reference to what objects might be involved. That seems like a fair
usage.

T.

Can you give example of where you used it?

Check two posts back at 'facet/kernel/__meta__'. Here's another for a
project I've been working on this week:

  class Program

    # ...

    def self.compile( yml, prg=nil )
      prg ||= Program.new

      sig = (class << prg; self; end)
      yml.each { |key, val|
        case val
        when RubyFn
          #val.out = val.out || key
          sig.class_eval {
            define_method( key ) { eval( val.code ) }
          }
        when Fn
          sig.class_eval {
            define_method( key ) { calc( val.code ) }
          }
        when Hash
          sig.class_eval {
            ftr = Functor.new { |op, *args| prg.send(op, *args) }
            res = compile( val, ftr )
            define_method( key ) { ftr }
          }
        when Type
          # do nothing
        else
          sig.class_eval {
            define_method( key ) { val }
          }
        end
      }

      return prg
    end

  end