6 ms·
This just goes to show how hard it is to create a API to last 30 years. So much has changed since then, but libc needs to keep the same API. Things like Unicode
by celeritascelery 4y ago
This just goes to show how hard it is to create a API to last 30 years. So much has changed since then, but libc needs to keep the same API. Things like Unicode and the internet (which brought the need for security to forefront) have come into popularity since then, but you can’t fix the old functions.
- vbezhenar 4y agoSo one should focus on creating versioned API IMO. Nobody does that for some reason. I should be able to declare my program to use v2023 API and consume libraries which use v1975 API.
- eadler 4y agoRust kind of does this. Language feature uses "edition" and has crates for its standard api instead of built in.
- tialaramex 4y agoIt really doesn't, though, although in principle Editions could provide an escape hatch it would be so drastic as to be largely unthinkable. Because of Rust's commitment to long term stability all of the standard library is there forever, even if it's a mistake and thus deprecated. std::u8::MAX will be in the library forever, even though you can write u8::MAX to get the same constant, name.trim_left_matches(remove) will exist forever even though you ought to write name.trim_start_matches(remove) because "left" assumes an LTR writing system. If NonZeroI64 is a bad idea, too bad it's in the standard library forever. If AddAssign is a bad idea, too bad it's in the standard library forever. The language syntax is allowed to evolve via Editions, but the standard library never breaks backward compatibility.
- actionfromafar 4y agoHey, don't get me started on semantics... what if you trim from the left or right, but are using RTL. Imagine your surprise when the exact opposite happens.
- masklinn 4y agoWhich is exactly why, as GP noted, rust deprecated trim_left/trim_right and added trim_start/trim_end.
- nicoburns 4y ago> It really doesn't, though, although in principle Editions could provide an escape hatch it would be so drastic as to be largely unthinkable Editions don't currently work for the std library, but I don't see why doing so would be drastic or unthinkable.
- tialaramex 4y agoWhereas C++ versions are each distinct languages which aren't necessarily interoperable, Rust Editions promise to interoperate and this is used all over the place. As a result the Editions can only "really" touch syntax, how you in some sense spell Rust, not what it means. Essentially Rust 2015 Edition and Rust 2021 Edition are actually exactly the same language except that the spelling is different, and as a result there are some things you can spell in one that you don't have a way to spell in the other. So in Rust 2015 Edition I can name my function async, because I want to, in Rust 2021 Edition that's fine, but it's written "r#async". Same function, same name in a sense, but different spelling. On the other hand, if I have an actual async function in Rust 2021 Edition, I can't write that in 2015 Edition at all - there's no way to spell the async keyword in that edition. The library however, is mostly semantics, not syntax. We care about what it does, not how to spell things on the whole.
- kelnos 4y agoBut the point is that there's no reason why editions couldn't touch semantics, too. There's no reason why they couldn't add a new attribute #[available_in_editions(2018, 2021)] that could allow them to actually remove deprecated functions, structs, trait, macros, whatever in newer editions. Code written for edition X would still compile and work, but code written for edition Y wouldn't be able to use things marked as unavailable.
- tialaramex 4y agoSo, with this hypothetical attribute, the thing isn't gone, but it doesn't compile any more, whereas with the current situation the thing also isn't gone, and you get a warning (unless you told the compiler to forbid this rather than warning, in which case it doesn't compile). This is identical to a [really_deprecated] attribute. We know it's deprecated but kelnos wants to force us to use a separate crate to use it for some reason.
- jmillikin 4y agoThat doesn't work in languages with nominal types, and even structurally-typed languages can have a hard time. You call a function that returns a v1975/SomeStruct and pass it to a function expecting a v2022/SomeStruct. What does the new function do when some of the struct fields are set to invalid values?
- netr0ute 4y agoWhat if the type was a "Some1975Struct" that got passed? Then that would give a huge blaring error because it's not the same type as a "Some2022Struct".
- jmillikin 4y agoThe naming convention isn't important. If the structs are from different versions of the library then you either have a type error (nominal types) or have to figure out the semantics of arbitrarily uninitialized fields.
- admax88qqq 4y agoYou do option 3, manually adapt one type to the other because they are different types. If you call one function that returns an int and need to pass it to another function that expects a char* what do you do?
- jmillikin 4y agoI think you may have lost track of what this thread is discussing. To be more concrete, let's say you have the following code (pseudo-Go): package main import "example.com/http_server" // v2 import "example.com/utils" // transitively depends on http_server v1 function main() { var srv *http_server.Server = http_server.New() utils.ServeHttp(srv) } You're going to have a problem, because ServeHttp expects an HTTP server struct from the v1 API, and you're passing in a struct from the v2 API. If you tried to write an adapter for this then (1) you'd need to somehow construct a concrete proxy of a type you don't control, and (2) there's no way to import both the v1 and v2 APIs at the same time in your code, so you don't have a way to declare a conversion function. That's why APIs with versions declared by dependency edges are impractical.
- yxhuvud 4y agoNo, one should focus on creating smaller independent modules and don't mix unrelated stuff like networking fundamentals with string convenience functions.
- beebmam 4y agoAre you familiar with linker hell? I agree in theory with your suggestion, but not for C or C-like languages which have such a poor story for linking compilation units.
- dietr1ch 4y agoWell, the problem is not getting easier and having poor versioning has been hunting everyone for around as long as null references
- kstenerud 4y agoWell, that and also a showcase of just how BAD the design of the C stdlib string functions was, and continued to be with every iteration where someone introduced a new bad-and-poorly-thought-out API to replace the old bad-and-poorly-thought-out API. It still boggles the mind how a function that leaves a C string potentially unterminated ever even made it into the standard...
- krelian 4y ago>It still boggles the mind how a function that leaves a C string potentially unterminated ever even made it into the standard... Easy to say with 50 years of hindsight. These are the pioneers we are talking about here. It boggles the mind how much they got right.
- kstenerud 4y agoWhile strcpy is forgivable considering it came from the primordial soup, there is no excuse for strncpy since it was designed to solve the problems with strcpy. And the string library is full of gaffes like this.
- tialaramex 4y agoNo, strncpy is not "designed to solve the problems". strncpy is a function to fill out fixed size data structures such as some on-disk data structures, with a variable length string it right pads the structure with null bytes. It does exactly what it was supposed to do, something that was pretty useful in 1970s UNIX programming but rarely if ever what you need today.
- icedchai 4y agoEvery teen hacker I knew had their own "my_strncpy" function that did something like strncpy(dst, src, len); dst[len-1] = 0;
- nicoburns 4y ago> Easy to say with 50 years of hindsight. These are the pioneers we are talking about here. Eh, well kinda. Languages with decent string handling predate C, so it's not like there wasn't precedent to follow. The creators of C were pioneers, but they were also people who favoured a quick, hacky approach over a clean careful one. That has certain advantages, but it's rather unfortunate that C has become so foundational and we've been stuck with those hacks.
- amelius 4y agoYou can make new functions while keeping the old functions, but making them print a warning that they are obsolete now.
- flohofwoe 4y ago> but libc needs to keep the same API The "needs to" is debatable. Compared to the C++ stdlib, the C stdlib is so small that adding a modernized and incompatible "v2" next to "v1" is realistic. The effort could start as a 3rd party implementation similar to MUSL. The old headers with the old APIs would still exist for "legacy code" but would generate "deprecated" warnings. Once that new "3rd party stdlib" has proven itself in the real world, the C commitee might consider it for inclusion in the standard.
- stephc_int13 4y agoWould you keep the naming style?
- flohofwoe 4y agoNo, at the very least I would add a (reserved) stdc_ prefix to all stdlib functions (and defines, and header filenames...), and keep options open for API versioning (e.g. stdc2_...). That way we could also get rid of all the random reserved identifiers we need to (theoretically) adhere to now.
- stephc_int13 4y agoThat's a good start, at least for function names, not sure if I would use the same logic for everything. In practice I find the prefix style better than namespaces, as a way to avoid information scattering. Snake case would probably win the race, but I am not sure that many old-timers would be ready to let go the old habit of abbreviated function names. After a while we don't see it for what it is, but I think that most of the naming is ugly, especially the string functions...