[1]: http://number-none.com/blow/john_carmack_on_inlined_code.htm...
If I had a dollar for every time I asked someone "Okay, look for other uses of this variable" and they grab the mouse and stare at the screen because THEY'RE READING THE ENTIRE FILE LOOKING FOR A PIECE OF TEXT, instead of using any of the dozens of search mechanisms at their disposal, I could retire and write self-help books for programmers.
Stop trying to white-knuckle everything all the time, you masochistic people. Use tools like a sentient creature is supposed to.
Mostly I think the exploratory phase of debugging is a battle between your limited short term memory and your imagination. It's a think you need to do alone and without someone stopping you to ask why every fifteen seconds. I think that's why people are so big on repro steps, so they can go off and start the process of elimination without having to dump state while defending checking possibility A before checking possibility B.
I think the only reason (besides tenacity) that I fix bugs that others gave up on is that I have tricks that let me walk the code faster, which lets me make forward progress on deduction before I forget WHY I was looking under this rock in the first place. My debugging practices look not unlike a guy looking for his missing keys. You have to be careful about when to hit him for questions, and most of the time it's better to just start your own parallel search if you want to help.
There's nothing quite as deflating as finally getting to the end of diagnosing a particularly pernicious bug just to have the understanding trigger the realization that you've encountered this bug before a year or two prior, spent just as much time originally figuring it out, and had somehow forgotten about it the interim. Yay for wasted days of our lives.
I think I need to make a concerted effort to get more sleep.
// time spent = 30
Personally, when I find out / solve something tricky I note down the details in a notes file exactly for cases like this. I'm yet to use it though, because I usually remember the weirder bugs I've seen.
Already in late 90's I couldn't believe what UNIX coders considered state of art, versus what we had on Mac/Windows/Amiga environments (the ones I knew before UNIX).
Having paired quite a bit, it's rather amazing how many programmers just don't know the full utility of their existing tooling. Not because they are stupid, but just because they never made that particular connection.
I have been on the receiving end of this dozens of times, and it is one of my favorite things about pairing as a programmer: where my pair shares a neat trick that saves me a notable amount of time and/or aggravation.
I would say that most compilers don't even assume the programmer is human. The root cause of bad errors is that they emanate from the compiler, and are reported from the perspective of the compiler. That's why you get errors like "can't access method undefined of undefined", or the hell that is C++ template errors. Sure, the errors make perfect logical sense from the perspective of a compiler, but they make my human eyes bleed.
My favorite is a single missing semicolon that causes a cascade of unrelated errors, none of which indicate there's a missing semicolon.
grammars, parsers, compilers, and languages as a whole should be designed for humans first. Even if it makes the language implementation a little more complex, human users will benefit greatly and thank you for it.
int foo = 2
bar = 3;
Am I missing a semicolon there? Or maybe a comma? You could probably guess it based on whether or not "bar" exists in the lexical scope during compilation, but then again, maybe I want to shadow it with a "more local" definition?The point is - you can make suggestions based on probabilities, and maybe it is a good idea to integrate a compiler with a linter. But to actually get error messages that make sense, your compiler would have to understand what you mean.
I get in a funk and my brain refuses to push past it. I feel like Neo when they tell him about the Matrix and he pukes and passes out.
In any lisp compiler/interpreter suited for the Real World, the sexpr tree you see in your code isn't the datastructure the compiler uses. Why? Because that sexpr tree doesn't account for information like line numbers, which are needed by the compiler for error messages, and other data. The sexpr tree that macros are given is an abstraction.
As for error messages, macros make it hard to give good ones C programmers who think their preprocessor macros cause bad problems with error messages, get this: If there's an error inside a macro expansion, the compiler/interpreter is getting errors in code that litterally didn't exist in the source code in any form. Furthermore, there are the standard problems of line numbering and giving the programmer information about where the error was in the actual source, all caused by macros. And Lisp macros are far more pervasive in Lisp than preprocessor macros are in C.
Racket tries to solve this with syntax-case, but syntax-case is awkward and breaks the standard macro abstraction in a lot of ways. So the problem remains, and is fairly nasty. Although Lisps and Schemes HAVE gotten better about macro errors over the years...
Want to see that in action? Look at e.g. the intermediate language between Erlang source and BEAM bytecode - it reads like macroexpanded Lisp code with square brackets instead of parentheses.
That is, there is a bit of a forcing function.
In all seriousness,
Ada the programming language is named after a person - the English mathematician Ada, Countess of Lovelace known as "the first computer programmer" ( https://en.m.wikipedia.org/wiki/Ada_Lovelace)
http://libre.adacore.com/developers/technical-papers-single/...
But none of them caught on because they were either too removed from the hardware for its time, or not "macho" enough for the guys in the computer labs.
The way to human friendly languages is not to take the phenomenal power and precision of computer programming languages and slather on a wafer-thin English layer that fuzzes and obscures the underlying complexity. Just because you've fuzzed and obscured the underlying complexity doesn't mean you've made it go away, but in practice you generally have removed huge swathes of the power.
I'd say the sort of thing we see Rust doing here is a better path. It is one of the most demanding, detail-oriented languages there is, but this sort of error details and explanation helps the programmer through that, and what comes out the other side is someone who still has the full power of programming, but far more accessibly.
[0]: usually people use this: http://www.mew.org/~kazu/proj/ghc-mod/en/ghc-modi.html
Like Haskell Servant[1], where you define your API as a type, and then you can derive functions from that type to query the API, because the type itself contains all the information necessary.
Pretty fascinating stuff.
[1] http://haskell-servant.readthedocs.io/en/stable/tutorial/Api...
warning: stpcpy() is dangerous GNU crap; don't use it
warning: strcpy() is almost always misused, please use strlcpy()
warning: strcat() is almost always misused, please use strlcat()
warning: sprintf() is often misused, please use snprintf()
I do have one link handy, since I participated in this thread myself: https://news.ycombinator.com/item?id=11620962
So apparently you have a habit of projecting support for Rust on anyone who criticizes C?
So not a reasonable connection to make, if you know a bit more of the context. I certainly don't blame you for making the jump, but I seem to be in the minority.
Actually I would enjoy C, if the language just had a few things from Wirth side:
- Real enumerations, decay to integers requires explicit cast
- No decay of arrays to pointers, &array[0] required for pointer conversion
- References (no need to pass around pointers for out parameters)
- bound checked arrays, for Assembly like performance use #pragma or compiler switch to disabled them
I could remember a few more, this are just a few examples.
However, we already had these features out of the box in C++ in 1993 when I used for the first time[0], hence why I never was a big C fan.
[0] - To honest some of them required writing classes and overloading operators, but they were there.
...Which is pretty much my point: You don't like C, because it didn't take from Wirth enough.
Do you have some kind of program that automatically notifies you whenever your name gets mentioned on HN? Because whenever I mention you, you show up within a day...
Close but not there yet. He doesn't like C because it ignored and still ignores basic, safety features on commonly-used operations. Wirth's stuff is often cited because it did safe one's as baseline, simple syntax/semantics, and for weak hardware. Closer to C than ALGOL, PL/0, and other languages we could cite that also had more safety. Telling a C programmer Thompson and Ritchie could've done ALGOL68 on a PDP-7 less believable than saying they could've done Modula-2 or something similar. ;)
Note: I also can't overemphasize enough that the first language they actually designed, feature by feature, to replace C was a modification of Wirth's Oberon-2 with features like we're talking about. Their Go work is indeed reducing number of crashes and vulnerabilities in systems versus C work while performing fast and improving. Imagine if they just started with Wirth's stuff then kept it practical and long-lasting (unlike Wirth).
No... The first language they designed to replace C was Plan9 C. Then Go, which drew heavy inspiration from Limbo, apparently.
It's mostly ANSI C, but with some variations (you can only #include once), and a lot of work put into ensuring portability without #ifdef and auto*.
To be fair to me, though, note that I also heavily promote memory-safe C (eg Softbound, SAFEcode), memory-safe UNIX (CheriBSD, SVA-OS), and user-mode UNIX (eg seperation kernels, GenodeOS) to help UNIX/C users reduce serious problems they might encounter. I just default on countering root causes of problems as first resort. ;)
I will totally have to check out some of those project now. Although I do wonder why Cyclone, the project that inspired Rust, wasn't on your list for memory-safe C.
Far as recommendations, putting my name and those terms into Google gives me six hits on HN where I recommend them both. In several, I push alternatives like microkernels, SPARK, and Rust plus those if person stays with C. In reply to ZeroTier developer, I reference Code-Pointer Integrity too while pointing out he's less safe with C++ because he can't use these C features. Ironic.
"Although I do wonder why Cyclone, the project that inspired Rust, wasn't on your list for memory-safe C."
I get 47 hits on that as I give Morissett et al's work plenty credit. Often when someone says Rust did the dynamic safety first where I counter they got it from Cyclone. Reason I don't push Cyclone so much is it isn't actively developed any more like most safer C's. C people just ignore them. Others included Safe-C, Clay, and Vault languages although I don't think all of them were OSS'd. Many non-C languages also compiled to C to get portability and performance benefits while app gets type-checked or other checks (eg bounds). Hardly any take-up there either. At this point, I just recommend them putting work into compiler extensions that make C safer with reasonable performance hits. SAFEcode already in LLVM, too.
Your performance comes specifically from not knowing your program will work upon malicious or faulty input. So, that claim is almost always apples to oranges. It's "language X let's me work and fail very fast" whereas "language Y let's me work fast." I think the 2nd one should be default option given speed of computers these days. Also, remember users' tolerance of program crashes may not be enough justification for riskier languages: silent corruption, which they don't like, can also result from unsafety.
Far as languages, the Oberons used in OS's [1] and Modula-3 [2] used in CVSup were very fast. The safe variants of C [3] were faster than inserting checks yourself given the compiler optimized some checks away with static analysis. Annotation and backward compatibility varies with each. SPARK Ada [4] can go as fast as C for static code but with provable absence of common errors. Ada, its parent, systematically [5] prevents/catches errors across whole process of programming. Rust is very CPU efficient with dynamic memory and concurrency safety. D language [6] is like a better C++ without C's risks. Fast as Free Pascal last I saw. ATS [7] is a programming-with-proofs language that uses dependent types to prove safety. It's been used on a 8-bit MCU. Finally, you can use typed, assembly languages [8] if performance really matters that much to you. Some academics & companies also mock-up assembly's in things like Rust, SPARK, etc to leverage their analyses with extraction to actual ASM.
[1] https://en.wikipedia.org/wiki/Bluebottle_OS
[2] https://en.wikipedia.org/wiki/Modula-3
[3] https://www.cs.uic.edu/pub/Main/PhDQualifyingExam/Sample4.pd...
[4] http://www.adacore.com/press/spark-skein/
[5] http://libre.adacore.com/developers/technical-papers-single/...
Also, I find the criticism of C you responded to quite valid, not "talking smack." It is a problem with C that it has several basic functions which are deprecated for security issues. This is a problem that C has much more than most other languages.
In other words, it didn't devolve into a flamewar, because that's where you started it out.
C isn't going away any time soon, and there's no value in decrying it as a response to being told about a feature that makes it safer!
"Although the first edition of K&R described most of the rules that brought C's type structure to its present form, many programs written in the older, more relaxed style persisted, and so did compilers that tolerated it. To encourage people to pay more attention to the official language rules, to detect legal but suspicious constructions, and to help find interface mismatches undetectable with simple mechanisms for separate compilation, Steve Johnson adapted his pcc compiler to produce lint [Johnson 79b], which scanned a set of files and remarked on dubious constructions. "
-- https://www.bell-labs.com/usr/dmr/www/chist.html
So in 1979 it was already clear that C wasn't for the faint of heart, but it took until clang for developers to take static analysis in C seriously.
And even today I hardly know of any enterprise project in companies we have access to, that cares about such tooling in C.
Strawman. C's predecessors did it better with foresight. One of best such systems was built by C's inventors. BCPL author and C inventors deliberately removed safety at OS and language level due to hardware constraints plus personal preferences. Then nobody changed it to avoid work or broken apps. Hindsight aspect has nothing to do with it.
"C isn't going away any time soon, and there's no value in decrying it as a response to being told about a feature that makes it safer!"
Got mixed feelings on when to slam C or help its users out. I agree with you that bashing it at a comment on a safety improvement is probably a bad idea. It might have effect of reducing number of people promoting safer C strategies. I usually ignore, upvote them, or mention stuff like SAFEcode/Softbound depending on what's in comment. I go anti-C when misinformation shows up or on more general topics of safe, efficient apps.
All the same coin to me. Decry C, laud the tools that make it safer, and sympathize with the poor bastards stuck with it all in one breath. I C++ for a living - you may pity me too. Games, you know.
> It might have effect of reducing number of people promoting safer C strategies.
It might have the effect of increasing the number of people promoting safer programming strategies. Maybe not contributing to the depressing statistics on buffer overflow CVEs by writing their startup's entire serverside stack in vanilla memory-unsafe C. Why, they could even use some kind of LISP variant...
(yes yes, here's some tinder, may any and all do with my strawman as they please...)
I've had the displeasure of dealing with it myself. So, a little bit. It can give you a bit of extra safety and flexibility with acceptable performance. Side effect, as I noted for ZeroTier, is you forfeit almost all the static analysis, memory-safe compilers, and other goodies for C language. You're in the category that might benefit from experiments with Rust given it's the C++ competitor, but with more safety. Maybe those ridiculous bugs I see in AAA titles will go away without much extra QA. Nah, I'll be unlucky and they'll be algorithmic. ;)
" Why, they could even use some kind of LISP variant..."
That's so 90's. Next you're going to want them to try to round up all the hackers, programmers, and VC's on one discussion forum. Can't expect people to pull that kind of stuff off. Far as LISP, check out PreScheme: static LISP w/ manual, memory management and compiled to C at first. Got mathematically verified during VLISP project. Enhancement of that could do games or startups. (taps head)
Actually, CARP Lisp is doing that with Rust-style safety if I recall. Has active contributors with an OpenGL snippet as demo:
http://www.cve.mitre.org/cgi-bin/cvekey.cgi?keyword=memory+c...
3795 and counting, every day something new!
F# is also adopting some of that style in F# 4.1: https://blogs.msdn.microsoft.com/dotnet/2016/07/25/a-peek-in...
Hell, just the underline showing you what area is wrong really helps you spend the fewest cycles on simple typos and etc. It's really a joy.
Fwiw, the frustrating times are usually involving some semi-obscure type system issue i've caused. But seeing as i'm probably their target user for these sort of messages (beginner), i guess my issues are still probably relevant.
What is next? Clippy? [1]
Clippy – A bunch of lints to catch common mistakes and improve your Rust code https://github.com/Manishearth/rust-clippy