5 ms·
I've written a lot of Go in the last few years. I stopped chaining calls after I realized how much of a pain it is to debug. When you need to print / log a val
by zlynx 8y ago
I've written a lot of Go in the last few years. I stopped chaining calls after I realized how much of a pain it is to debug.
When you need to print / log a value from the middle of your call chain you have to take the chain apart. So just write it out to start with. It's not any less efficient.
- mike_hearn 8y agoI'd note that's only an issue if you don't have a good IDE. In IntelliJ you can breakpoint on an expression that uses chaining, hold down the alt key and then click on the expression you want to evaluate. It turns into a hyperlink and can be viewed easily.
- kcmp 8y agoI think needing a heavy IDE to comfortably work in a language/paradigm is a negative
- mike_hearn 8y agoYou don't need an IDE but it solves problems like "I don't want to chain my methods because my weak debugger can't handle it well".
- sagichmal 8y agoChained method calls being difficult to debug does not necessarily mean using a debugger. It can mean difficult to insert clarifying code in the middle of the chain: print statements, or additional value checks, and so on.
- bmurphy1976 8y agoChained methods also don't diff as clearly (the one liners anyway).
- pjmlp 8y agoThey do on GUI diff tools by using different colors.
- Cthulhu_ 8y agoIt's still not as clear as just splitting across lines, plus in most tools you need to explicitly indicate you want to highlight word diffs. But more abstractly speaking: are chained calls, from the POV of a human that didn't write it, or wrote it two months ago, more readable than a number of statements on multiple lines?
- pjmlp 8y agoGiven what Lisp, Smalltalk, Mesa/Cedar, Oberon, Delphi, VB introduced as productivity tooling, I see as negative programming as if my computer was still stuck in 80x25 green phosphor terminal.
- scroot 8y agoAmen
- simias 8y agoThat's a silly caricature. I could retort "I see as a negative if my development environment takes 40GB of RAM and half an hour to boot up" and it would be on the same level. Debuggers are great but they're not always practical, for instance for debugging remote targets with limited connectivity. Actually in some situations you may end up having to buy expensive licenses for closed source software to run a debugger on certain hardware (although that might not be a concern for Go). I'm also of the opinion that firing the debugger as soon as something unexpected happens instead of taking 10 seconds to reason about your code might lead you to miss a more general problem with your architecture as you focus solely on the symptoms, but that's of course a lot more subjective and your experience may vary. Personally I generally tend to use debuggers as a last recourse when I really can't make sense of what's going on through "static" analysis.
- marcus_holmes 8y agoalso, if you write a ton of log statements because that's how you debug, then when your production system blows up and you can't fire up the debugger, you have something to work on.
- marcus_holmes 8y agoGenerally speaking, Gophers don't interactively debug. There is some support for it, but it's historically been difficult and we have tended to adjust to logging everything and working out the problem from the logs.
- lucio 8y agoThat's OK but when using "logs" it takes more time and tries to find a complex bug than to search for it using an interactive debug tool.
- ordu 8y agoI don't know about Go, but if I faced such an issue with rust, I would make a trait with one method: fn print(self) -> Self { println!("{:?}", self); self } You can do it nicely, so all you need is to add one `derive` to a type declaration, and then you will be able to insert .print() into the chain. It is like print in lisps, might be really handy for the debug printing.
- gnuvince 8y agoI'm not sure why you would prefer to (1) create a new trait, (2) create a custom derive for that trait, (3) derive that trait for all the types you want to debug over assigning to a variable and printing. It sounds like an over-engineered solution to a non-problem to me.
- Dylan16807 8y agoBecause that's a tiny amount of work that saves you from making bloated junk variables all over.
- zlynx 8y agoThe compiler removes them, and the naming of intermediates often makes the code more understandable.
- Dylan16807 8y agoBy bloat I mean in the source code. Naming intermediates can sometimes make things clearer, but also it can make things less clear. Often there is no good name, or the good name is just as long as the code that made it.
- ordu 8y ago(1), (2) and (3) needs to be one once per lifetime. Or even less, if it was published on cargo.io. It is not an issue. And it doesn't seem as over-engineering for me, it would be like 10 lines of library code, with clear purpose. If it was 50 lines of code, with several methods for different cases, and with a lot of corner-cases, then it might be called over-engineering. But 10 lines of straight self-describing code with two line comment about suggested uses is not. As to derive for each type, I already use `#[derive(Debug)]` for most of my types, to be able to print them with {:?} in format/println!. Though you are right in the sense, that it would be great to make such a method a part of Debug trait. But I personally have never needed it, so maybe complications of Debug trait would be the over-engineering you speak about. From other hand I do not like variables. Variables are complications of code, and complications like this make it impossible to grasp code on the first sight. There are a lot of cases when you need a variable, and it is not apparent on the first sight which case is it. You need to stop and think. I do not like to think just to find that there was no reason to think. Intelligence is a constrained resource, you need to use it wisely.
- acjohnson55 8y agoIn Scala, I use an implicit class to add a `tee` function. I kind of wish it were in the standard library so I wouldn't have to import it.