Project

General

Profile

Actions

Feature #11717

closed

Object#trap -- pass object to block and return result

Feature #11717: Object#trap -- pass object to block and return result

Added by zverok (Victor Shepelev) almost 10 years ago. Updated over 8 years ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-core:71590]

Description

Object#trap can be thought as useful counterpart for Object#tap: like tap, it passes object to the block; unlike tap, it returns results of the block, not object itself.

Rationale

#trap could allow to gracefully chain processing of objects, which isn't Enumerable, therefore enforcing "functional" style in Ruby (which considered good).

Use case

# Assume we grab some resource from web: SomeWebClient.get('http://some/url', param1: 'value1', param2: 'value2').body # And now, the body of response is JSON, and we want it parsed. How to express it? # Option 1: wrap: JSON.parse(SomeWebClient.get('http://some/url', param1: 'value1', param2: 'value2').body) # Downside: messy parenthesis, and need to jump back and forth to understand the meaning # Option 2: intermediate variable: s = SomeWebClient.get('http://some/url', param1: 'value1', param2: 'value2').body JSON.parse(s) # Downside: intermediate variable is not elegant # Option 3: monkey-patch (or refine) SomeWebClient.get('http://some/url', param1: 'value1', param2: 'value2').body.from_json # Downside: monkey-patching is a last resort; also, your classes should be already patched when you stuck with this case # Option 4 (proposed): trap SomeWebClient.get('http://some/url', param1: 'value1', param2: 'value2').body. trap{|s| JSON.parse(s)} # => parsed JSON 

And when you are thinking with code, experimenting with code (especially in irb, but in editor too), only last option is "natural" river of thoughts: do this, then do that (extract data from web, then parse it).

Naming

  • it is similar enough to tap;
  • it is specific enough to not be used widely in some popular library (or isn't it?);
  • mnemonic is "do something and trap (catch) the value".

WDYT?


Related issues 3 (0 open3 closed)

Updated by 0x0dea (D.E. Akers) almost 10 years ago Actions #1 [ruby-core:71591]

You're looking for #instance_eval:

'foo'.instance_eval { |obj| obj.size } # => 3 

Updated by zverok (Victor Shepelev) almost 10 years ago Actions #2 [ruby-core:71594]

Nope.

I'm aware of #instance_eval last 10 years or so (I even can recall times when #instance_exec were external library method, not part of the core).

Primary goal/usage of #instance_eval is to "dig inside". Primary goal/usage of #trap is to "apply next transormation". Even if #trap will be implemented as a simple alias of #instance_eval, it will have some usage/popularity outside of #instance_eval's domain. On my opinion only, of course.

Updated by phluid61 (Matthew Kerwin) almost 10 years ago Actions #3 [ruby-core:71595]

Some related issues and historical readings:

Clearly this is something the Ruby community wants. Just as clearly, it's something nobody can name.

Updated by phluid61 (Matthew Kerwin) almost 10 years ago Actions #4 [ruby-core:71593]

Victor Shepelev wrote:

Naming

  • it is similar enough to tap;
  • it is specific enough to not be used widely in some popular library (or isn't it?);
  • mnemonic is "do something and trap (catch) the value".

WDYT?

"trap" already means "trap a signal", it comes from long-standing Unix terminology; see Signal#trap

Updated by 0x0dea (D.E. Akers) almost 10 years ago Actions #5 [ruby-core:71596]

Victor Shepelev wrote:

Even if #trap will be implemented as a simple alias of #instance_eval...

If you did in fact know that you were essentially requesting an alias for #instance_eval, this was a remarkably roundabout way to go about it.

Updated by zverok (Victor Shepelev) almost 10 years ago Actions #6 [ruby-core:71610]

"trap" already means "trap a signal", it comes from long-standing Unix terminology

Ooops. Completely forgot about this one :(

Clearly this is something the Ruby community wants. Just as clearly, it's something nobody can name.

Yeah, can see it now.

Thinking further, I wonder if just Object#yield could be parsed correctly...

If you did in fact know that you were essentially requesting an alias for #instance_eval, this was a remarkably roundabout way to go about it.

But hey. It is NOT, in fact:

class MyClass def with_instance_eval(filename) File.read(filename).instance_eval{|s| p [s, self]; parse(s)} end def with_trap(filename) File.read(filename).trap{|s| p [s, self]; parse(s)} end def parse(str) JSON.parse(str) end end puts "#trap:" p MyClass.new.with_trap('test.json') puts "#instance_eval:" p MyClass.new.with_instance_eval('test.json') 

Output:

#trap: ["{\"test\": 1}\n", #<MyClass:0x911f428>] {"test"=>1} #instance_eval: ["{\"test\": 1}\n", "{\"test\": 1}\n"] trap.rb:in `block in with_instance_eval': undefined method `parse' for "{\"test\": 1}\n":String (NoMethodError) 

Updated by nobu (Nobuyoshi Nakada) almost 10 years ago Actions #7

Updated by nobu (Nobuyoshi Nakada) about 9 years ago Actions #8

  • Has duplicate Feature #12760: Optional block argument for `itself` added

Updated by nobu (Nobuyoshi Nakada) almost 9 years ago Actions #9

  • Has duplicate Feature #13172: Method that yields object to block and returns result added

Updated by nobu (Nobuyoshi Nakada) over 8 years ago Actions #10

  • Status changed from Open to Closed

Applied in changeset trunk|r58528.


object.c: Kernel#yield_self

  • object.c (rb_obj_yield_self): new method which yields the
    receiver and returns the result.
    [ruby-core:46320] [Feature #6721]
Actions

Also available in: PDF Atom