Project

General

Profile

Feature #14609

`Kernel#p` without args shows the receiver

Added by ko1 (Koichi Sasada) about 1 month ago. Updated about 1 month ago.

Status:
Open
Priority:
Normal
Target version:
[ruby-core:86152]

Description

Abstract

Kernel#p(obj) prints obj as inspected.
How about to show the receiver if an argument is not given?

Background

We recently introduce yield_self which encourages block chain.

https://zverok.github.io/blog/2018-01-24-yield_self.html
Quoted from this article, we can write method chain with blocks:

construct_url
  .yield_self { |url| Faraday.get(url) }.body
  .yield_self { |response| JSON.parse(response) }
  .dig('object', 'id')
  .yield_self { |id| id || '<undefined>' }
  .yield_self { |id| "server:#{id}" }

There is a small problem at debugging.
If we want to see the middle values in method/block chain, we need to insert tap{|e| p e}.

With above example,

construct_url
  .yield_self { |url| Faraday.get(url) }.body
  .yield_self { |response| JSON.parse(response) }.tap{|e| p e} # debug print
  .dig('object', 'id')
  .yield_self { |id| id || '<undefined>' }.tap{|e| p e} # debug print
  .yield_self { |id| "server:#{id}" }

Proposal

obj.p shows same as p(obj).

We can replace
block{...}.tap{|e| p e}
to
block{...}.p

For above example, we can simply add .p at the end of line:

construct_url
  .yield_self { |url| Faraday.get(url) }.body
  .yield_self { |response| JSON.parse(response) }.p # debug print
  .dig('object', 'id')
  .yield_self { |id| id || '<undefined>' }.p # debug print
  .yield_self { |id| "server:#{id}" }

Compatibility issue

(1) Shorthand for nil

This spec change can introduce compatibility issue because p returns nil and do not output anything.
That is to say, p is shorthand of nil. Some code-golfers use it.

Maybe we can ignore them :p

(2) make public method

Kernel#p is private method, so if we typo obj.x to obj.p (not sure how it is feasible), it will be NoMethodError because of visibility.
We need to change this behavior.

Note

Past proposal and discussion

Endoh-san proposed same idea 10+ years ago in Japanese.
I think we should revisit this idea because of yield_self introduction.

At this thread, Matz said "simple p shows p(self), it is not clear".

  p

はどう動くのかとか(p selfと同じ、は変な気が)

  self.p(obj)

はどうなのかとか。その辺が解決(納得)できたら、ということで。

English translation:

What the behavior of (I feel strange that it is similar to `p(self)`):

  p

What happen on

  self.p(obj)

pp

If this proposal is accepted, we also need to change pp behavior.

gems

tapp method is provided by gem.
https://github.com/esminc/tapp

I'd thought to propose this method into core. But I found that p is more shorter than tapp.
Disadvantage is p is too short and difficult to grep.

History

#1 [ruby-core:86153] Updated by mame (Yusuke Endoh) about 1 month ago

+1

Kernel#p is one of the greatest feature in Ruby. It would be further great to make it useful.

#2 [ruby-core:86155] Updated by zverok (Victor Shepelev) about 1 month ago

Small notice: If #13581 would be once acted upon, attaching p in the middle of the chain could be as simple as (using one of the proposed syntaxes)

  .yield_self { |response| JSON.parse(response) }.tap(&:.p)
  .dig('object', 'id')
  .yield_self { |id| id || '<undefined>' }.tap(&:.p)

Just .p is still nicer, though.

#3 [ruby-core:86156] Updated by Hanmac (Hans Mackowiak) about 1 month ago

hm i have a slightly problem with this

check out the different return types there:


a = []
p *a #=> nil

a = [1]
p *a #=> 1

a = [1,2]
p *a #=> [1,2]

Also available in: Atom PDF