proposal-standardized-debug icon indicating copy to clipboard operation
proposal-standardized-debug copied to clipboard

Let debugger.log support any number of arguments

Open Jack-Works opened this issue 5 years ago • 12 comments

Jack-Works avatar Nov 04 '20 09:11 Jack-Works

Would be very nice to have. My recommendation would be to have it log all arguments and only return the last.

For example:

const result = Math.PI * debugger.log('radius square', r ** 2)

theScottyJam avatar May 22 '21 15:05 theScottyJam

I think valid arguments can be made for both the first and last values being the return value, so I'm hesitant to allow this. Additionally, I would expect implementations to print the source code passed to debugger.log (If you read the spec you can see the implementation is given access to the |Expression| which is passed).

For example in rust:

fn main() {
    let r = 6.28f64;
    let result = std::f64::consts::PI * dbg!(r.powf(2.0f64));
}

// stdout:
// [src/main.rs:3] r.powf(2.0f64) = 39.4384

devsnek avatar May 22 '21 18:05 devsnek

What's the argument for returning the first parameter? I think the most common use case would be to use earlier parameters to label what the later parameters are - normally that's not done in reverse order.

I'm also a little hesitant of having it try to smartly "auto-label" the output with the source code - I might be logging out a larger expression, or I might be logging out multiple similar expressions, or I just don't want the clutter of the source code mixed with the output.

...though I can also see this being a convenient thing to have in some scenarios. Maybe if I were to actually use it, I would learn to like it.

theScottyJam avatar May 22 '21 21:05 theScottyJam

What's the argument for returning the first parameter?

For this code

return arr.map(debugger.log).map(x => x + 1)

Jack-Works avatar May 23 '21 01:05 Jack-Works

That seems like a weak argument; everyone already will have had to figure out that they need to convert it into a one-arg arrow function, or, use a function that ignores the 2nd and 3rd arg - this seems no different than parseInt to me.

Put another way, if that's an argument, then we basically couldn't ever have any built-in functions with more than one arg.

ljharb avatar May 23 '21 02:05 ljharb

I'm not inclined to allow debugger.log in that form anyway, these aren't functions.

devsnek avatar May 23 '21 04:05 devsnek

If it cannot be used as functions, they're less useful because I cannot write this arr.map(debugger.log)

arr.map(debugger.log)
arr.map(x => debugger.log(x))
arr.map(x => (console.log(x), x))

Jack-Works avatar May 25 '21 04:05 Jack-Works

Everyone debugs differently - I personally wouldn't find arr.map(debugger.log) all that useful, as I normally just want to print out the whole array at once, not each individual item - especially when working in a browser environment that provides tools to collapse/expand the array, but also in node, as I find their default array formatting to work just fine.

debugger.log(arr)

However, I realize that everyone has different styles of debugging that they find comfortable to them, and it's good to try and recognize everyone's preferences.

theScottyJam avatar May 25 '21 05:05 theScottyJam

@Jack-Works I continue to not understand your dislike of arrow functions, and I'm happy to lean on them for this and other proposals.

devsnek avatar May 25 '21 12:05 devsnek

@Jack-Works I continue to not understand your dislike of arrow functions, and I'm happy to lean on them for this and other proposals.

I just think add more wraps is annoying when I want to quick peak on the intermediate value when I write a long composed expression.

Jack-Works avatar May 25 '21 13:05 Jack-Works

Ah, so if I understand you right @Jack-Works, you want it for this purpose?

const doTransfomration = data => data
  .filter(x => x != null)
  .map(x => x ** 2)
  .map(debugger.log)
  .filter(x => x > 100)

e.g. you're wanting to stick a debugger.log in the middle of an array's fluent API, to see what an intermediate value is? I can certainly sympathize with that. The current alternative would be something like this:

const doTransfomration = data => debugger.log(data
  .filter(x => x != null)
  .map(x => x ** 2)
)
  .filter(x => x > 100)

// or ...

const doTransfomration = data => data
  .filter(x => x != null)
  .map(x => x ** 2)
  .map(x => debugger.log(x))
  .filter(x => x > 100)

(The .map(debugger.log) solution only helps with the array's fluent API - ideally, we would find a solution that works for any fluent API)

Another alternative, as discussed a bit in this thread on the tc39 forums, is this clever trick that uses the pipe operator, and which works in any fluent API - I'm using hack-pipes in this example. (That particular thread talks a good amount of debugging large expressions).

const doTransfomration = data => data
  .filter(x => x != null)
  .map(x => x ** 2)
  |> debugger.log(?)
  .filter(x => x > 100)

The debugger.log(?) and the .filter(...) get combined into one line: debugger.log(?).filter(...), which actually works just fine. And since this is for debugging, clever hacks like this aren't that bad to do.

theScottyJam avatar May 25 '21 13:05 theScottyJam

In your example you can just do this:

const doTransfomration = data => data
  .filter(x => x != null)
  .map(x => debugger.log(x ** 2))
  .filter(x => x > 100)

devsnek avatar May 25 '21 13:05 devsnek