Functor Opinion

T

Trans

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.
 
R

Robert Klemme

Trans said:
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.

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 http://en.wikipedia.org/wiki/Function_object
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
 
D

dblack

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

--
David A. Black ([email protected])
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

"Ruby for Rails" chapters now available
from Manning Early Access Program! http://www.manning.com/books/black
 
T

Trans

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

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.
 
T

Trans

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.
 
J

James Edward Gray II

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 give example of where you used it?

James Edward Gray II
 
T

Trans

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top