Project

General

Profile

Actions

Feature #10394

closed

An instance method on Enumerator that evaluates the block under with self being the block variable.

Added by sawa (Tsuyoshi Sawada) about 10 years ago. Updated over 5 years ago.

Status:
Feedback
Assignee:
-
Target version:
-
[ruby-core:65767]

Description

Background

There has been desire to omit the | | and the explicit receiver in a block used with an enumerator or an enumerable. Currently, when the content of the block is a single method that takes no argument, symbol-to-proc is used with the & syntax so that:

["foo", "bar"].map{|s| s.upcase}

can be written as:

["foo", "bar"].map(&:upcase)

There has repeated been proposals (#8987, #9076, #10318) that express this desire to do this even when the block involves a method chain or a method with arguments like the following:

["foo", "bar"].map{|s| s.concat("ber")}
["  foo ", "\tbar\n"].map{|s| s.strip.upcase}

Focus has been on modifying how a block is passed to the enumerable/enumerator, and there has not been consensus on how the syntax should be.

Proposal

Unlike the earlier proposals, I suggest that there should be an instance method on Enumerator, let's say Enumerator#as_self, that evaluates the block each time with self being the block variable that would be passed otherwise. With such method, the cases above would be written like this:

["foo", "bar"].map.as_self{concat("ber")}
["  foo ", "\tbar\n"].map.as_self{strip.upcase}

This adds no modification to the syntax, it just requires a new method Enumerator#as_self to be implemented. I consider this method being along the lines of Enumerator#with_index, Enumerator#with_object; it intervenes between an enumerator (related to a block-taking method) and a block, and let the block-taking method work in a modified way.

It resembles instance_eval, but is different in that it assigns to self what would be a block variable (which changes for each iteration), instead of assigning the receiver.

Updated by matz (Yukihiro Matsumoto) about 10 years ago

  • Status changed from Open to Feedback

I like the idea itself. But I don't think as_self is a good name.
Any other name proposal? Anyone?

Matz.

Updated by gogotanaka (Kazuki Tanaka) about 10 years ago

Sounds good for me.
(Actually I face the difficulties to implement #10318 ... and it's painful)

I just come up with...

#as
#as_arg(s)
#through
#member
.... ; (

And I'd be happy to call method which needs more than 2 args, if it could be.

just idea.

Updated by sawa (Tsuyoshi Sawada) about 10 years ago

If the name is less than five characters, then it would need less typing than the original form, (i.e., five-letter name .xxxxx{foo} would require as much typing as {|x| x.foo}), so there would be more motivation for using this method. It may be good to use some preposition:

["foo", "bar"].map.by{upcase}
["foo", "bar"].map.by{concat("ber")}
["  foo ", "\tbar\n"].map.by{strip.upcase}

If one wants to go with a non-letter method name, | may be a candidate:

["foo", "bar"].map.|{upcase}
["foo", "bar"].map.|{concat("ber")}
["  foo ", "\tbar\n"].map.|{strip.upcase}

The | character here is reminiscent of the unix pipe, which may be interpreted here as passing the values to the block, and it also resembles the || notation in the block. But I don't know if people might think that ugly. & would keep resemblance to the notation using & with symbol_to_proc, but I don't feel the necessity to do so:

["foo", "bar"].map.&{upcase}
["foo", "bar"].map.&{concat("ber")}
["  foo ", "\tbar\n"].map.&{strip.upcase}

Even an asterisk or caret may work.

["foo", "bar"].map.*{upcase}
["foo", "bar"].map.*{concat("ber")}
["  foo ", "\tbar\n"].map.*{strip.upcase}
["foo", "bar"].map.^{upcase}
["foo", "bar"].map.^{concat("ber")}
["  foo ", "\tbar\n"].map.^{strip.upcase}

Updated by avit (Andrew Vit) about 10 years ago

It might be confusing if such a thing only exists for Enumerator blocks and nothing else.

["foo", "bar"].map.as_self { clear }
["foo", "bar"].tap.as_self { clear } # (not an Enumerator)

What would be the correct receiver in your proposal for the following example? What happens when the method is not defined in the block's "self" object?

@members = ["foo", "bar"]
def transform_all
  @members.each.as_self { transform }
end
def transform
  raise "this one?"
end

A little bit related: #10095 (for the proposed syntax "as" vs. "as_self")

Updated by nobu (Nobuyoshi Nakada) over 5 years ago

Isn't it instance_eval?

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0