6 ms·
It's funny that this is the first time I've seen a language explicitly condone "print debugging." It's one of those things that everyone says you're not suppose
by KeyboardFire 8y ago
It's funny that this is the first time I've seen a language explicitly condone "print debugging." It's one of those things that everyone says you're not supposed to do and then does anyway.
Does any other language have a similar feature?
- the_duke 8y agoThe person who proposed it is a (previous) Haskell developer. The dbg! macro is definitely inspired by https://hackage.haskell.org/package/base-4.12.0.0/docs/Debug-Trace.html https://hackage.haskell.org/package/base-4.12.0.0/docs/Debug....
- SilasX 8y agoHow so? In Rust, the macro is just a very thin wrapper around some output formatting boilerplate. In Haskell, it exists because you'd otherwise have to change the type of the function to add print-debug statements.
- the_duke 8y agoBecause it can be used to wrap any expression. I haven't seen a debug function that returns the value again anywhere else.
- eslaught 8y agoIt was definitely in Arc first, and probably in other lisps before that: https://github.com/arclanguage/anarki/blob/master/arc.arc#L1628-L1631 https://github.com/arclanguage/anarki/blob/master/arc.arc#L1... Edit: Yes, it's in Common Lisp too, so it goes back at least to the 1980s and probably further. http://www.lispworks.com/documentation/HyperSpec/Body/f_wr_pr.htm http://www.lispworks.com/documentation/HyperSpec/Body/f_wr_p...
- yxhuvud 8y agoThe debug printing method p in Ruby have been returning it's argument for years now. Dbg seems possibly better for the usecase though as you get the location as well.
- majewsky 8y agoIt also reminds me of Ruby's .tap { |x| puts x } For those of you who don't know Ruby, its map/filter/reduce functions chain like this: values.map { |x| x + 2 }.select { |x| x > 3 } So when you want to look at an intermediate result, there's the .tap() method that runs a lambda with that intermediate result, then passes it on to the next step in the chain. [0, 1, 2, 3].map { |x| x + 2 }.tap { |x| puts x }.select { |x| x > 3 } This returns [4, 5] after printing [2, 3, 4, 5]. ("puts" is Ruby's println.)
- killercup 8y agoTook me a while to find it, but Rust's iterators have an `.inspect` method that gives you a read-only reference, so println debugging works fine. For more advanced tap-like stuff, use the `tap` crate (which allows you to write `array.tap(|xs| xs.sort())` for example, even though `sort` mutates in place and doesn't return the array).
- nostrademons 8y agoPython/Ruby have long had a tradition of printf + unit test debugging. I assume Python has a debugger now but I honestly don't know anyone who uses it.
- rcfox 8y agoI use it all the time. If my program crashes, I swap `python foo.py` with `ipython --pdb -- foo.py` and it will drop me into an ipython session at the exception so that I can inspect variables and the backtrace. Also, if I want to pause at a point and step from there, just drop `import ipdb; ipdb.set_trace()` at the line I want to set a breakpoint.
- globuous 8y agoI use it all the time as well and know many people that do. It was no longer than yesterday that I discovered it worked in a jupyter notebook ! So I assume enough people use it that it was integrated
- pjmlp 8y agoI was using Python debuggers in 2002, courtesy of ActiveState.
- Too 8y agoprint is a considered a big antipattern in python since it's equally easy to use the built in logging.debug. With pycharm launching the debugger is as easy as right-click a file and press debug, i don't know anyone who doesn't use it.
- deleted 8y ago[deleted]
- pjmlp 8y agoPrint debugging is only necessary when support for trace points in debuggers is lacking for the respective language. With good support, a couple of trace points, even on a live running instance is all that is needed, without any extra recompiles.
- FartyMcFarter 8y agoThis is just not true. For example, some bugs are so rare that you can't hope to reproduce them by just looking at "a live running instance". In this case, print debugging can be the only practical way to go.
- pjmlp 8y agoWhich is exactly what trace points are for. More devs should learn about JTags, IntelliTrace, DTrace, .... No need for manually writing printf-debugging and recompiling all the time, when the debugger can do that for you.
- hoytech 8y agoIt's pretty much exactly like Ingy's XXX perl module from 2006: https://metacpan.org/pod/XXX https://metacpan.org/pod/XXX Prints a YAML dump of "my str", followed by file/line number information, then returns its argument so it can be embedded in expressions: $ perl -MXXX -E 'say uc(WWW("my str"))' --- my str ... at -e line 1 MY STR Data::Dump's "ddx" (1996) is also commonly used for print debugging, except it doesn't return the argument.
- kbenson 8y agoAlmost. dbg! also shows the statement in addition to what it evaluates too. That is: dbg!(n * factorial(n - 1)) shows up as: [src/main.rs:5] n * factorial(n - 1) = 2 [src/main.rs:5] n * factorial(n - 1) = 6 [src/main.rs:5] n * factorial(n - 1) = 24 Although, IIRC there was some crazy dumper module by dconway (who else) that worked similar to this, but I don't recall if it returned the value. ... So, I just looked it up, and I found it. Actually, Damien wrote two. One that he updated from 2014 to 2016, and one he started in 2017 and has maintained to the present. I have no idea the reason for that. 1: https://metacpan.org/pod/Data::Show https://metacpan.org/pod/Data::Show 2: https://metacpan.org/pod/Data::Dx https://metacpan.org/pod/Data::Dx
- hoytech 8y agoHehe nice ones, thanks for the links! :) Yep leave it Damien. I remember he did something similar for a test module where it would show the expressions that were evaluated in the test failure output.
- bbatha 8y agoThe `p` function in ruby is designed for quick pretty printing. `console.` in the browser are only* useful for debugging. Go has a `println` keyword so that you don't need to import the `fmt` package, and has a `%+v` format helper to get a pretty printed debugging friendly representation of an object.
- empath75 8y agoAs a new go programmer you just saved me a bunch of time, I didn’t know about either of those.
- root_axis 8y agoI think debuggers are not worth the cost for many kinds of debugging scenarios. They're great for stepping through projects you're not really familiar with or in situations where code seems to be in violation of baseline expectations, but fiddling around with breakpoints and watches and other UI particularities of the debugger carries more cognitive overhead than "print debugging". Additionally, I think the problem is better solved by using thoughtful and contextualized logging with appropriate severity levels. Couple this with a TDD approach to development and you'll end up in a workflow that is just faster than stepping through lines of code when you could have your assumptions verified through logs and test assertions.
- jasonhansel 8y agoAgreed! Personally I've found that I can find and fix problems much faster with a few print statements than with a debugger--debuggers can make it harder to trace through the full execution of a program.
- naasking 8y ago> debuggers can make it harder to trace through the full execution of a program. Except you often don't want a full execution, you often just want a partial execution where you suspect the problem arises. I can assure you that a good UI debugger is extremely helpful. Command line debuggers less so.
- mikekchar 8y agoThis is why unit tests are so helpful. You only debug the part of the code that's broken. It's a kind of a different way of thinking and people often write what I consider to be "bad" tests -- i.e. tests that don't actually exercise real portions of the code, but rather make tautological statements about interfaces. I spend a considerable amount of time designing my code so that various scenarios are easy to set up. If you find yourself reaching for a fake/mock because it is hard to set up a scenario with real objects, it's an indication of a problem. This extra work pays off pretty quickly, though. When I have a bug, I find a selection of tests that works with that code, add a couple of printf-equivalents and then rerun the tests. Usually I can spot the error in a couple of minutes. Being an older programmer (I worked professionally for over 10 years before Beck published the first XP book), I'm very comfortable with using a debugger. However since I started doing TDD, I have never once used one. It's just a lot faster to printf debug. The way I've explained it before is that it's like having an automated debugger. The tests are just code paths that you would dig into if you were debugging. The expectations in the tests are simply watch points. You run the code and look at the results, only you don't have to single step it -- it just runs in a couple of seconds and gives you the results. You may think that the overhead of writing tests would be higher than the amount saved with debugging and if it were only debugging, I think that would be true. However, one of the things I've found over the years is that I'm actually dramatically faster writing code with tests compared to writing it without tests (keep in mind that I've got nearly 20 years of TDD experience -- yes... I started that early on). I'm pretty good at it. The main advantage is that when you are writing code without tests, usually you sketch together a solution and then you run the app and see if it works. Sometimes it does pretty much what you want, but usually you discover some problems. You use a debugger, or you just modify the code and see what happens. Depending on the system, you often have to get out of the context of what you are doing, re-run the app, enter a whole bunch of information, etc, etc. It takes time. Debuggers that can update information on the fly are great time savers, but you still have to do a lot of contextual work. It takes me some extra time to write tests, but running them is super quick (as long as you aren't writing bad tests). Usually I insist that I can run the relevant tests in less than 2 seconds. Ideally I like the entire suite to run in less than a minute, though convincing my peers to adhere to these numbers is often difficult. That 2 seconds is important, though. It's the amount of time it takes your brain to notice that something is taking a long time. If it's less than 2 seconds (and run whenever you save the file), usually you will barely notice it. In that way, I've got better focus and can stay in the zone of the code, rather than repeatedly setting up my manual testing and looking at what it is doing. Overall, it's a pretty big productivity improvement for me. YMMV.
- FartyMcFarter 8y agoThis "print debugging is bad" idea never made sense. Debugging is largely an effort to understand what's going on inside a program - having the program "talk back" to you via prints can be a great way to do this. Sometimes, print debugging is the only practical way to fix a bug. For example, very rare bugs which can only be reproduced by running many instances of the code for a long time, or situations where attaching a debugger is not feasible (as in live services).
- pjmlp 8y agoThen one uses trace points like IntelliTrace or DTrace.
- majewsky 8y agoDoes this also work with 10 replicas of something in Alpine containers on clusters spread across different regions?
- pjmlp 8y agoIntelliTrace works perfectly fine on Windows clusters and across Cloud deployments on Azure. Never used Alpine containers, so no idea how it works with DTrace / SystemTap. However a quick web search revealed the following right away, surely there are other results available. "Systemtap for CoreOS Container Linux" https://medium.com/makingtuenti/systemtap-for-coreos-container-linux-b2c0a0b5dce3 https://medium.com/makingtuenti/systemtap-for-coreos-contain... "App Trace Roll: Users Guide" . For Red Hat Linux clusters https://docs.huihoo.com/rocksclusters/app-trace/4.1/index.html https://docs.huihoo.com/rocksclusters/app-trace/4.1/index.ht...
- 613style 8y agoElm has a Debug.log function which takes a string and any value, prints "String: value" in the console, and then returns the value.
- losvedir 8y agoElixir has a very similar function, IO.inspect(..), which prints a debug version of its argument and then returns it, very similar to dbg! here. Coupled with Elixir's pipeline operator `|>` which passes the output into the first argument of the next function, it's pretty handy: foo() |> IO.inspect |> bar()
- rng_civ 8y agoDo people really not want people to print debug? The professors of all my CS classes so far (from data structures to assembly) explicitly tell students to print debug.
- LandR 8y agoWhen you're learning it's fine. I would hope at some point you would get to the point where you move past this, once you get to Enterprise scale print debugging stuff would be slow and laborious.
- zeckalpha 8y agoBash has a -x mode that does this.