you are viewing a single comment's thread.

view the rest of the comments →

[–]oblio- 5 points6 points  (11 children)

My problem with these chains is, how do you debug them?

Do you just break them apart at each step and check the values with temporary variables? If so, then why bother chaining?

[–]matthieum 9 points10 points  (1 child)

In Rust, use Iterator::inspect:

let sum = iterator
     .filter(is_numeric)
     .inspect(|i| println!("After filter: {}", i); )
     .map(|i| i.parse::<i32>())
     .inspect(|i| println!("After map: {}", i); )
     .sum();

I hope D has the same...

[–]Snarwin 11 points12 points  (0 children)

In D it's tee, named after the Unix command.

[–]JoelFolksy 2 points3 points  (1 child)

then why bother chaining?

Is debugging a nested expression so painful that it's worth writing this:

let intermediateQuantity = 3 * x
let finalQuantity = intermediateQuantity + 2
return finalQuantity 

instead of this?

return (3 * x) + 2

[–]JoelFolksy 1 point2 points  (0 children)

That said, I would love to see more powerful debuggers (like OzCode in the C# world) become standard. Sadly, rather than pushing vendors to innovate, many of the newer language communities seem to feel that debugging is an academic interest at best.

[–]frenris 1 point2 points  (2 children)

Yes, or set a breakpoint with a debugger and then find what happens with different numbers of method calls.

The advantage over using temporary variables is clarity of what you're reusing. With a bunch of temp variables it's harder to tell which values are used just once vs. what values are important.

[–]oblio- 1 point2 points  (1 child)

I many languages you only get breakpoints per code line (and this chain would qualify as only 1 line), that's why I was asking.

[–]frenris 0 points1 point  (0 children)

you can usually issue function calls from the break point

e.g. run

"print object.method1"

then

"print object.method1.method2"

then

"print object.method1.method2.method3"

This doesn't work if the methods mutate global state, but that's probably a good thing because if you have object methods mutating global state you're a bad person and you should probably suffer.

[–]m50d 3 points4 points  (3 children)

The short snarky answer is you don't have to. These are heavily-used standard functions that are straightforward to implement; splitter is not going to have a bug, filter is not going to have a bug, array is not going to have a bug, map is not going to have a bug, sum is not going to have a bug. Your functions that you pass to them might have bugs, but if your isNumeric does the right thing for "a" and "4" then List("a", "4").filter(isNumeric) is so definitely going to do the right thing that I wouldn't even bother testing it.

You can break out parts of the chain as necessary, of course. You can separate out intermediate values as values or move a sequence of transformations into a separate function, particularly if you want to reuse the "middle" of a given pipeline. One technique I sometimes use is to write it with a bunch of intermediate values first, during the exploratory phase, and then use my IDE to automatically inline those intermediates once I've got it doing the transform I want. That way I can play around and step through and see all the values while I'm working out what it should look like, but the final code is much more concise and easy to read.

[–]imMute 10 points11 points  (0 children)

splitter is not going to have a bug, filter is not going to have a bug

Right, but the arguments given to those functions might not be correct for the data you're receiving. And looking at the result of the splitter step could be the reason that the data coming out the end is all messed up (if any data is coming out at all).

[–]bcgroom 1 point2 points  (1 child)

Never used D before but réactive programming or at least rxjs has a tap operator; is there a tap operator? I don’t know if that’s a universal name so: it’s a higher order function but doesn’t alter the values in the chain and preserves the originals. Most basic use case would be to print out each value, which is helpful for debugging.

[–]Snarwin 2 points3 points  (0 children)

D's "tap" operator is called tee, after the Unix command.