Counting At The Cloak 'N' Bind #
So Method#to_proc is basically totally bankrupt. It is now the most severely mocked typecast in the book. From RailsConf:
[10, 20, 30].map &4.method(:+) ary.each &(hsh={}).method(:store) def initialize(hsh) hsh.each &method(:instance_variable_set) end
Clearly, fun stuff. But, you know, bankrupt.
If you don’t mind drawing a bit more blood, did you know you can use this
stuff to kill (\w+)_with_index
?
%w[joe tim francis].map &with_index { |x| [x, index] } #=> [["joe", 0], ["tim", 1], ["francis", 2]]
Here’s how it works: the index
accessor is added to the Proc. And
each time we loop, the accessor is incremented.
It takes some glue, though. Proc#bind (from Rails, originally cloaker)
is used to turn the block into a method of itself! Then with_index
adds an incrementing wrapper over it.
The Method#to_proc
happens when you amp it into each
.
# from activesupport class Proc def bind(object) block, time = self, Time.now (class << object; self end).class_eval do method_name = "__bind_#{time.to_i}_#{time.usec}" define_method(method_name, &block) method = instance_method(method_name) remove_method(method_name) method end.bind(object) end end # there are three interesting variables here: # blk:: the block you've passed in. # bound:: the same block, but now a method of itself. # wrap:: the wrapper proc which does the counting. def with_index(&blk) class << blk attr_reader :index def even; @index % 2 == 0 end def odd; @index % 2 == 1 end end i = 0 bound = blk.bind(blk) wrap = proc do |*a| blk.instance_variable_set('@index', i) x = bound[*a] i += 1 x end wrap end
Now, for some exercises:
>> %w[joe tim francis].map &with_index { |x| [x, index] } => [["joe", 0], ["tim", 1], ["francis", 2]] >> %w[badger goat mule eagle shark].select &with_index { even } => ["badger", "mule", "shark"]
However, 1.9’s Enumerator#with_index is definitely less boggling and sloppy. If you have instance variables inside the block, they’ll be lookin in the block object for a home.
Daniel Berger
If all we want is to pass arguments to Array#map (and that’s what every to_proc example I’ve seen has done), let’s just modify Array#map instead of introducing new and ugly code constructs that make me not want to program in Ruby any more:
The above code assumes that non-symbol arguments are meant as parameters to the most immediately previous symbol.
Downcase, then reverse each string:
Add 4, then double, each element of the array:
Yes, I’m sure my map implementation could stand some major refactoring.
Danno
Couldn’t currying handle most of this?
jes5199
makes me start thinking about ‘generator’. is generator in 1.9 still using continuations and being too slow to have fun with?
jes5199
.enum_for(:map!).each_with_index{
not only does the each_with_index return a Enumerator object instead of the output of map, it fails to filter the results of its block back into map. so sad.why
Daniel Berger: I can’t seem to figure out why you read this blog.
Danno: Not really. Look back at how
index
andeven
are used in the last examples.jes5199: Maybe swap like:
.enum_for(:each_with_index).map
...I dunno.riffraff
jes5199: there is still a lot of road to walk to get serious generators support in ruby, but IIRC at the moment it does not use
callcc
anymore, relying instead on a thread-based implementation.Which recalls me we should convince ko1 to provide M:N threading, otherwise doing this tricks with OS threads may be sad.
trans
Is to_proc still alive!? Another…
class Hash def to_proc Proc.new { |o| each { |k,v| o.send( ”#{k}=”, v ) } } end end
class X attr_accessor :a def initialize( &h ) h[self] end end
X.new(&{:a=>1})
trans
Is to_proc still alive!? Another…
Confused
I didn’t understand a single line. Where can I enlighten me?
MenTaLguY
I’m not really a fan of M:N threading to be honest. And besides, OS threads are already M:N on many platforms.
I think we’re better off combining threads with explicit support for first-class coroutines (i.e. not thread- or continuation- based), like GNU pth threads or Win32 fibers.
MenTaLguY
I don’t know … the Kernel.with_index thing has something going for it, although as far as the specifics go I’m starting to think I’d prefer something like this:
But, here’s another thing: why don’t we have
Integer#even?
andInteger#odd?
anyhow?MenTaLguY
(I realize that misses the point of exercising
Method#to_proc
though)why
Confused: You poor guy, I’m sorry. I really do regret posting this one.
I wouldn’t try to understand the insides of the
Proc#bind
method. It’s a completely complicated hack. If you happen to know the Prototype JavaScript library, it works just like thebind
method in Prototype.Basically, when you
bind
a proc to an object, whenever that proc gets called, it’ll act like a method (with instance variables andself
and all that.)The
with_index
binds the Proc to give itindex
,even
andodd
methods.trans: Oh, man, you’re givin me carnal thoughts here…
MenTaLguY
Okay, here’s one for the party.
MenTaLguY
why: Okay, that’s just bent. Wow.
Daniel Berger
why: I read your blog because I find it interesting, as well as many of the responses.
I’m just trying to figure out why people are getting so excited about this convoluted syntax when it seems to me that they’ve never bothered to ask themselves what problem it is that they’re solving.
jes5199
why: what happens when your hash has more than one pair?
MenTaLguY
jes5199: it ignores all but one of them—whichever one happens to come last in the iteration. Note how the injection state argument is ignored.
MenTaLguY
Dan: We’re playing, basically. Exploring. This stuff’s fun! It doesn’t have to solve anything.
MenTaLguY
Uh, both of those questions were directed to why, weren’t they? Geez, sorry. I don’t know what’s with me this week.
ix
berger: i dont understand this post either. but its giving me perl flashbacks ;)
and it seems the commentbox thinks perl is a typo..coincidence? :D
Asztal^_^
stop the madness!
My head hurts enough from template metaprogramming. :)
jes5199
oh! i was making a mistake: why’s code explodes if you use
%|a|
in your own block, as he is already using|a|
in his metablockpsmith
Anything you can do, we can do meta.