242 comments

[ 3.1 ms ] story [ 274 ms ] thread
All well and good until you find out half of it is written in C or something like that. I like to go out and find the most starred repos on github or just ask around for a library that people think highly of and read that.
C itself is actually a pretty good example of where this fails as well. Its standard library is usually more macros than code. It's necessary if you are going to ship portable code that deals with every conceivable architecture and endianness and permutation of toolchain from 1976 until now, but it's not how most C code looks.

K&R is a much more meaningful guide than unistd.h.

Ugh Crystal is so beautiful and sorry to make this typical complaint but I wish the compile time was 1/10th of what it is. The dev cycle loop is just not there for me but the language is incredible.
Does Crystal benefit from these new M1 beasts? My Rust compile times became much more pleasant.
LLVM implements fewer optimization passes for ARM, so it's doing less work than compiling for x86.
Does that hurt performance of the output?
Well, if it didn't, they could optimize the compiler by removing those optimizations. :)
That was(is?) the benchmark for gcc optimizations: it has to pay for itself on the compiler: if the resulting compiler code is faster but the added compilation time is even greater, it s not worth it.
I think I first heard of this criterion being used by Niklaus Wirth for Pascal and Oberon compilers.
I think it's pretty clear the GP was asking if the optimizations implemented for x86 that aren't implemented for aarch64 would actually improve performance of generated aarch64 code. It's a question about CPU architecture/microarchitecture. That's a different question as to if the optimizations improve performance of generated x86 code.

For instance, I imagine the x86_64 register allocation does some variant on graph coloring for register allocation, with an additional pass to assign lettered registers (rax, rbx, etc.) to the most heavily used registers, since using higher numbered registers requires a REX prefix byte. In addition, many instructions have more compact encodings when eax/rax is the destination register. At a minimum, excess REX prefixes take up instruction cache space. There's no parallel for aarch64, so there's no sense in implementing logic to try and make sure the low-numbered aarch64 registers are used more. (Though, on 32-bit ARM with Thumb/Thumb2, only a subset of registers are available, so there is a similar optimization for 32-bit ARM targets that support Thumb/Thumb2 when optimizing for space.)

I imagine there are better examples, but my point is that some optimizations are useless on some architectures.

> I think it's pretty clear the GP was asking if the optimizations implemented for x86 that aren't implemented for aarch64 would actually improve performance of generated aarch64 code.

Oh, of course! That must be what was meant. Sorry.

There is high probability that it can hurt runtime perf. Because less passes means less optimization (in general)
Compile times on M1 seems to be fairly nice yes, according to those that have tested. But it is still perhaps not what one could wish it to be. So far most of the efforts in the compiler seems to be around making it correct, rather than faster.
Elixir’s standard library is incredible and so simple and 99% written in Elixir.
Reading the Python stdlib was a brilliant move a peer encouraged me to do. I learned three themes of stuff:

1. A good look at long lasting, durable pure python.

2. A good look at long lasting, durable C implementations of python (dict is the core of Python. Read the source!)

3. A look at a bunch of libraries that basically never get used and a sense of how they compare to popular third party libraries. (Doing this is why I’ll never complain when a language’s stdlib is small. I get it now.)

I'd love if you could elaborate on your thoughts for 3. Is it a problem of not being feature complete in unpopular parts of the stdlib?
What I learned is that once a library is in the stdlib, it's usually stuck being maintained for a long time. And then you've got all these libraries that see no use because they were ill-conceived or better ideas came along.

I don't think stdlibs should be barren, but I completely get the ideas of languages like Rust that want to include only the most obvious of candidates and leave the rest for the community to organically evolve.

urllib vs requests is one prominent example
Until you realize most of the time the code is loaded with Macros, ifdefs, cross-platform conditions(windows,macos,linux...), hacks for different versions of OSes/dependency/whatever, the real meat is hard to spot on, it's easy to get lost in those noises.
Funny enough, this was a source of much criticism for OpenSSL as well, at least in the 1.0x series.
What would the JS/TS equivalent?
Mozilla's JS Reference, I'd guess. A genuinely great resource.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

Edit:

And the Web API reference (which is what I was actually thinking of when I posted): https://developer.mozilla.org/en-US/docs/Web/API

Reading these things is good advice! But it's distinct from the advice given in the article, which is about reading libraries close to the heart of the language that are written in that language itself.

JS is a bit odd here, possibly for good reasons (ubiquity + dynamic nature of the lang itself might mean runtime considerations override any degree of self-hosting), but it basically means it's not possible for standard definitions of standard library. So the question becomes... what are some masterful libraries written in JS?

Definitely true for Golang, learned a ton reading its stdlib (and even found a bug this way)
I agree, Golang is a good language for this. Rust, too.

The Python standard library is pretty crufty — lots of stylistically dated code. Same with Perl 5, and I would guess other similar languages but I can't speak from direct experience. I bet that's more a function of their age — back in the 1990s maybe it might have been unreservedly good advice to read those standard libraries.

I've also learned a lot from writing FFI extensions for languages, which for Rust and Go involves a lot of standard library diving but for languages like Python, Perl 5 and Ruby where the core is mostly written in C, it's a different experience than reading the standard library.

This is what I immediately thought of as well. The Golang stdlib is so good it makes me wonder if the author wrote this with it in mind
I actually partially disagree with this. Sure, for the higher level OS independent packages (e.g. “archive/tar”) this is great.

For stuff in the “os” package it’s a different matter. All of that code is a bit messy. Not because the authors did a bad job, but simply because it’s hard to implement the same API on half a dozen operating systems.

Then there is the “runtime” package which relies on a lot of global state, and has to work well in cases where heap allocations are not permitted.

Sure you’ll learn how the language works, but you shouldn’t draw inspiration from those on how to write your own code.

I suspect that the part of the stdlib that deals with the OS and runtime will be fairly messy and un-idiomatic in any language. They are inherently hairy problems.
I find languages are pretty easy to pick up after the first few. What seems to be the barrier nowadays is frameworks. How do people go about learning a new framework? Obviously most of them have an intro project to follow on with, but they tend to be pretty simplistic, and once that's done, getting to the complicated bits that you actually want to implement seem to be the barrier.
I usually prefer working with mature stacks that don't require me to constantly work with flavor of the month frameworks.

It's great to broaden your horizons and all, but that is something that needs to be done judiciously and deliberately if it is to actually be of any use.

Me too, but to get started on a non-trivial project in such a stack is, IMO, the tricky part. It would be nice if I could do everything in the stack(s) I already know, but that's not always possible (e.g. backend vs web vs mobile app vs desktop app).
Re-implement a toy version of the framework from scratch. :-)

Or, try to add a non-trivial feature to the framework.

How do you know what a "toy version of the framework" looks like when you're not experienced with the framework itself?

It seems to me like you've already solved the issue of "learning the framework" if your mental model of the framework is that solid.

Knowing what a framework does (its function) and knowing how to use it (its functions) are two different levels of knowledge. sometimes you can work backwards from knowing what it does to make a very quick-and-dirty implementation.
Perhaps by addressing the issues that led to you adopting the framework?
Wouldn't building a small, toy application with the framework be a more efficient way to learn it?
IMHO, if a framework isn't well documented, I avoid it. It means they probably didn't think it through very well and it will soon be unmaintained.
If you stay strictly in the procedural/OO paradigm then sure: it's just a new std lib and syntax. When you jump to functional, logical, or more esoteric paradigms things start to get pretty different.

Of course if you've already got one of each major paradigm under your belt things get progressively easier because you're exposed to more ideas...

/me trying to learn OCaml after learning a bunch of other languages. Disagrees.
In my experience, one of the hard part when learning OCaml is that you can write your own code without modules, but to use someone else's code, you have to know how they work.
Same experience, I thought I had things figured out but after a few weeks struggling with OCaml I felt like a complete retard :-) But it was an eye opener in some way on how to write programs in a different way. I think OCaml also suffers a bit the same problem as C++ does. You need to learn quite a lot to be able to read others code. There are many advanced concepts with cryptic syntax that is hard to search for on the net.
This doesn’t seem like a great idea in general, since the standard library is usually low-level code, aggressively optimized for speed, that is not at all typical of applications written in that language.
To anyone that considers this approach for learning C++, I would advise strongly against it. Standard library code has to deal with too many cases and tries to be optimal for as many of them as possible. Also the formatting is highly unusual, compared to other C++ code found in the wild.
Yeah... for C++ it's more like s/To learn/To really learn/
Uh yeah if you try and read the Haskell standard library, you're screwed. It's list fusion and fancy algorithms to hack asymptotics all the way down.
What does it mean to “hack asymptotics”?
Algorithm runtime analysis is usually measured with asymptotic estimates (Big O, Little O, Big Theta, etc). In something as commonly and generically used as the standard library of any performance focused language you're liable to find a focus on optimizing these algorithmic performance guarantees for corner cases over optimizing for simple and straightforward code.
That’s actually what I do whenever I’m lacking humility as a coder. One F12 on std::iterator (which is now deprecated lol [0]) is enough for me to remember that I don’t know anything.

[0] https://www.fluentcpp.com/2018/05/08/std-iterator-deprecated...

My gripe with C++ is that you need to know so many things to build even the most basic abstractions. When writing a custom data structure you always end up with having headaches about the specifics of std::iterators and rvalue references and all that jazz rather than focusing on the actual algorithm. There’s a reason why people want to go back to Orthodox C++ (using C++ as C with a few extra features)
This. Want to define a class? Remember the rule of 3 (or is it 5?).
Yeah, the first thing that popped into my head was some poor n00b spending the next six years of their life deciphering glibc. There have got to be easier ways :)
musl, I would expect? (granted, that's C, but vs glibc...)
Yeah, musl is simpler and cleaner than glibc. I was referring to C, by the way. libstdc++ is actually part of gcc, not glibc.
I would have commented that if you didn't
That's probably true of quite a few languages actually. I mean if there is any library you want to be as fast as possible, it's the standard library. Take python for example, a big chunk of the standard library is actually written in c (although there is often an equivalent implementation in python). In rust the standard library uses quite a bit of unsafe code, and even nightly only features and standard-library-only features that you don't usually need in real code, because you will be calling something in core or std that does it for you, and has been rigorously checked to be safe. The java standard library has parts that are written in c++. Etc. Etc.
> I mean if there is any library you want to be as fast as possible, it's the standard library.

To a decent extent yeah, though I would emphasize the issue isn't quite being as fast as possible (although in some cases it is, but also see I/O streams...), but rather having near-absolute correctness, and achieving high performance as a secondary goal. You can usually find a faster third-party implementation of anything in the C++ standard library. The problem is third-party implementations always cut corners somewhere, whether it's strong exception-safety, proper trait/concept handling, thorough testing of rare edge cases, extensibility/customizability, or other stuff. Even the syntactic complexity rises as a result, let alone the complexity of the actual semantics. (Even minor stuff like using difference_type/size_type instead of ptrdiff_t/size_t makes things more verbose and harder to read in the standard library; third-party implementations would often opt for the latter.)

> In rust the standard library uses quite a bit of unsafe code, and even nightly only features and standard-library-only features that you don't usually need in real code

I have found the Rust std lib to be a great resource. Much of the unsafe is necessary because there is literally no other way to build up abstractions like Box or certain data structures without it. Obviously once those are written though, you want to build on top of them where possible and not use unsafe.

It's probably important to delineate between a good example of application code vs library code also. The std lib is not going to help much with application code.

Exactly. The rust standard library can be very informative (and the same is true of other standard libraries as well), but it isn't necessarily going to be the same as what you would do in application code, because in application code you have the benefit of the standard library.
Do you know of any examples of good quality application code in rust?
There are many like firecracker vm, polkadot, linkered proxy, tokio, hyper etc. And these are the one I know and I think there are too many good quality application code in Rust.
I wouldnt have thought of tokio as application code bc its a library, but I guess it kinda is because it also provides a runtime.

Thanks for the list

I was about to post the same but here you are. The only time I would study std:: namespace would be is I needed to write some generic library. Trying to learn C++ by reading std:: can make one's head explode.
(comment deleted)
And the C++ standard library has too many fundamental design issues that are just bad advice for writing actual maintainable and performant code. For example, the standard specifies all map containers to have pointer stability, which is often not needed in reality. Because of this all <unordered_map> implementations are hilariously slow. Or what about std::vector<bool>, <iostream>, std::string, you name it.

The frustrations people have with it are sometimes up to the point where they begin writing everything from scratch…

The same is true of clojure. Don’t read clojure.core as idiomatic.
One thing I have wondered about is the number of multi-arity functions that individually specify 1, 2, 3, 4, 5 argument cases in 3rd-party library code. Is it a result of people thinking that’s idiomatic, or is

  ([a b c d])
performant enough to matter in library code instead of using

  ([a b & more])
? I hope it’s the former.
This is also true for .NET, especially modern .NET where performance is critical nearly everywhere. It's representative of perf-sensitive code, but not general purpose C#.
I’m using Zig a lot right now, and I have been pleasantly surprised by the standard library. Maybe it’s due to Zig being a very straightforward language, but I find most everything I read in the standard library to make immediate sense.

I don’t know about reading std alone to learn Zig though, I used other sources like ziglings and ziglearn which taught me syntax and patterns. Had I started with the standard library I doubt I would have picked up the language as quickly as I have.

So having learned Zig I am much more inclined to look at standard libraries for languages I learn in the future, but I don’t think it’s wise to rely only on standard libraries for learning.

Is there any learning resource for Zig that doesn’t assume you already know C? I couldn’t find any.
I'll be honest there aren't really any complete learning resources even for those that do know C. For what there is the language is changing whatever you read in documentation might not be true between stable and master at the moment. The stdlib source is probably the best reference but even reading strictly from that it doesn't mean you'll learn to make code that works it means you'll learn to make code that's correct. There are plenty of known compiler and language issues on GitHub and the self hosted compiler is still being written.

At the moment since Zig isn't complete you aren't going to find documentation that is either. Particularly for newcomers.

Ziglings [0] is a series of small problems where you solve bugs in Zig code, and I enjoyed working through them. The readme claims "you are not expected to have any prior experience with... C." When I did ziglings I found the most difficult part was adjusting to the type syntax (which I now love).

I admittedly know C very well, but assuming you already know a language with a C-like syntax (C++, Java, JavaScript, C#, Rust, etc.) most things like functions, control flow, variables, statements map to Zig with only small syntactic differences.

One thing that may be difficult to learn not knowing C is working with strings, since they are just arrays of bytes. Most other languages have a string type, but Zig is much more like C in that strings are null-terminated fixed-sized arrays of bytes.

[0] https://github.com/ratfactor/ziglings

Learning Zig by reading its standard library source has worked for me. The source files also include quite self explanatory tests that compensate for incomplete documentation on main Zig website.
If you've ever tried reading the source for glibc or libstdc++ you know this is terrible advice.
In the past, people supported pundits who would sit in the monasteries/huts their whole life perfecting their knowledge of scriptures and their skills of explaining them to others (incl. picking the right verse when it's needed and commenting the way helpful to the seeker). Some times I feel like today, besides free software projects, meant to be ran on donations, there should be entire separate projects dedicated to writing really good (also taking usability, eloquence, ease of understanding and practical examples very seriously) documentation for programming languages and libraries.
(comment deleted)
This is how I approached learning Nim at the time, and it was quite an excellent approach in my opinion. Even gives you the opportunity to contribute to the language in question, when you inevitably come across something that isn't optimal, a bug, or even something as small as formatting inconsistencies, which is a nice feeling
I say best way to learn is to start coding and break stuff :-)

Also once one gets more experienced, its better to decouple the language from programming concepts like object oriented programming, continuations, avoiding side effects in functional programming, etc. Useful to read books like SICP and algorithm books, then learn multiple languages and you will see they are not that different overall.

I recommend learning Lisp or Scheme or related languages because they have a lot of good conceptual resources to learn from. But maybe that’s my background talking, I loved SICP. I learnt SO much from studying Common Lisp in the last year. Of course it helps that it has a great language specification.

In terms of choosing a language, standard library matters a lot. Hence the explosion of JS due to NPM and also being in browser. I’m suprised SWIFT is not more popular, given Apple’s place in the ecosystem. I really liked programming in Swift, very enjoyable language. And it has generic functions too!

I'm not sure how many languages this works well for but it certainly doesn't work with C. You won't learn the important distinctions between implementation defined, unspecified and undefined behavior. And reading a C standard library implementation will not really teach you to write normal idiomatic C code. You will also not learn the strict discipline required to avoid doing things unless you absolutely know with certainty that they are either strictly conforming or defined for your particular set of target platforms (usually much harder to answer this second question which is why when writing C you should avoid having to).

That being said, for Erlang, I think this is a good idea. I learned a lot when reading the standard library.

(comment deleted)
The goals of library authors are frequently different from those of application developers. Library authors usually have to consider how to make their code work on more environments, on older language versions, more configurable, handling more edge cases.

I think there’s definitely value to reading a language’s standard library, and I’ve also gotten lots of value from even reading the language’s implementation! But I hope people take this advice to start with reading the standard library with a grain of salt: it can be one extra resource for you, and likely to show you new things that weren’t written in docs somewhere, but if it doesn’t click or is proving difficult to understand, that’s fine!

In particular, for the “reading code to learn” bit, consider reading an application written in that language (command line tool, web application, etc.) and it might serve as a more reduced intro to the language.

IMHO, any language worth learning has a large enough user base in both corporate and academic settings and that there are books written on it. That's my criteria. If it doesn't have at least 5 books on Amazon, I'm suspicious of it (and from real publishers, and not publishing mills). There's too much wheel-reinventing happening for me to get excited about new languages.

In the last decade I've spent time learning five sufficiently novel languages: Scheme, Ada, Erlang, Rust, and Go. Three of those are from the late 70's. I'm really glad I learned Ada and Rust. I started using Ada for embedded programming, and I'm trying to find more opportunities to use Rust because it so robust. I only studied Erlang, Scheme, and Go because smart people I knew told me I should investigate, but I have not used them.

What is novel about Go? It just seems like C repacked with a garbage collector and basic concurrent programming support to me. Which both weren't novel even when Go was designed.
It's just a little bit more work to write a non-trivial program in Go than in a scripting language such as Python, so if you need/want the good tooling and you need/want the significant speed improvement, it could be a good choice.
Go doesn’t have the libraries of python however. In certain computing domains you would first have to spend 10 years rewriting the python frameworks in golang before you could even be on parity with python.

I am most familiar with scientific computing and GIS applications — python is miles ahead of golang in ecosystem support in those domains.

For scripting and "just throwing something together for internal use," Python is a pretty traditional choice, but I think Go is able to outclass it for maintainability -- static typing and somewhat better package management are a big help, and Go has a comparable ecosystem.
If that was a useful argument for the future we'd all still be using Perl.

I mean, I still write a lot of Perl, but everyone else would be too.

And if this wasn't a useful argument many more people would use OCaml, Crystal, D, etc ; and every new language wouldn't be met with "I like it but it doesn't have the libraries I need so I'll keep writing Java".
(comment deleted)
Naive question, can't you just call Python code from Go? I don't see the problem besides that performance of the Python libs wouldn't be as good as if they were written in Go.
(comment deleted)
Go doesn't have any substantial innovations in the set of language features, or maybe none at all. I believe it is instead the whole of a sober and efficient toolchain that effects tedious but simple to understand and work with code, which gave it such a great appeal.

It's like a modernized C with a good implementation and out-of-the-box no-nonsense tooling, and the tricky bits left out. Nothing really new to see, but there was nothing really like it when it took off. Very utilitarian. Rust, Java, C#, C++ - those are all very 'complex' or 'bloated' in many different ways to an avid Go programmer.

I'm not promoting Go (I personally don't like it), I'm just trying to understand it's appeal and popularity and what is different about it. Quality and completeness of implementation, tooling, etc. by having a big company behind it also helps of lot of course - but it isn't sufficient.

Golang is effectively from the late 70's as well.
Could I trouble you to compare Ada and Rust, especially in terms of safety? I'd like to be able to write low-level code without dealing with the insanity of C, and my top picks are Ada (traditional answer to "safe language"), Rust (the up and coming answer to "safe language"), and Pascal (friendlier than C and way better at least for memory safety), but I'm reluctant to learn all of them to sufficient depth to be able to decide in retrospect which one was worth learning:)
Memory "unsafety" isn't bad. It's just a feature. In fact it's a feature some people want and need. I'd still start with C. There are all the ressources in the world to learn and it's better to know how to manage boundaries and memory allocation by hand than the other way around.
Most of the time memory unsafety isn't a feature people want or need.
There's a significant niche for such features, we're dozens. That's the reason why people like stuff like Zig. Pointer arithmetic isn't just dangerous, it's cool too.
I'm not saying that memory unsafety is useless or even undesirable, I think the "problem" is that it's the default in "current popular and actually used high-performance languages", which means that stuff that needs to be fast and secure (like web browsers) have a hard time.

Maybe my "most of the time" was too strong and I ignored big domains that I don't know. But for the part that I relatively know (web stuff mostly), safety and speed together matters a lot. The thing is, since unsafety tends to be viral (an unsafe part of your stack can compromise everything), people get very paranoid about "unsafe languages".

I understand your point, but for the most part memory-safety features and speed of execution run contrary to one another.

Garbage collection and bounds checking is big sticking point for systems / high performance programming, and to an extent real-time programming.

As far as I can tell, Rust should be about the limit of what's possible if you want to have your cake and eat it too. But I'll doubt it'll ever replace C for speed-critical applications.

Of those three, Ada and Pascal are as I understand it pretty closely related syntax-wise (in the same "family"), so learning one of those will help a lot with the other.

I'd recommend starting with Pascal, because of the comprehensive and high quality standard libraries (or "frameworks" -- see related recent HN discussion...) in Free Pascal and Delphi, and because Free Pascal can target a lot of different OSes. OTOH, a downside might be that you learn (to rely on) too much of the FCL/VCL in stead of just syntax, so you get confused at the lack of those libraries in Ada; that could speak for taking it the other way around.

TL;DR: IMO, in a way that's perhaps closer to two new languages to learn than three.

LCL, not FCL: Lazarus Component Library.
What's the difference between a real publisher and a publishing mill ?

How would you categorize No Starch Press, Manning, OReilly as per this rubric ?

As someone who reads 10-20 tech books per year I rate PragProg the highest, then O’Reilly, though not everything is of the same quality there. PacktPub is absolute trash tier. Don’t remember others you’ve mentioned as good or bad
where's the french standard library I can't find it
In any French city or town near you.
lol, here's what the standards body has published [0]. Version 9 still in dev, with "recent" releases of:

1992 - French v9.a.e

2000 - French v9.e.m

2011 - French v9.m.q

Don't be deceived by the dates, though. The project isn't dead. The Larousse and Le Robert forks are actively developed.

[0] https://en.wikipedia.org/wiki/Acad%C3%A9mie_Fran%C3%A7aise#D...

(comment deleted)
A different take: read unit tests for the standard library.

That shows you call sequences in context, rather than just getting lost in the code.

lol this advice might not apply as much to Scala. All the CanBuildFrom stuff was overwhelming to look at the first time, and a lot of things in the stdlib are implemented in a way that’s not idiomatic in app-level Scala. They’ve simplified things a bit and made it better since I was baffled by it all long ago though.