Hacker News new | ask | show | jobs
by skulk 1124 days ago
Cool project, but this project demonstrates the reason I've stopped writing things in C. The standard library has garbage string functions and it seems every project has its own version of this file:

https://github.com/robdelacruz/lkwebserver/blob/main/lkstrin...

It's fun to write this (and read others' versions) the first 3 or 4 times, but it gets old quickly.

5 comments

That's the "lazy, dumb" way of doing it --- write another string library. A much better way is to design your algorithms so they need a minimum of string manipulation, which is unfortunately on the more difficult side for text-based protocols like HTTP.

Personally, I wish HTTP messages were closer to something like ASN.1 DER; there's little in the way of string manipulation necessary for those, and all the lengths are prefixes instead of "try to find the terminator" (and don't forget to not run past the end of the buffer...)

> ASN.1 DER

This has also had serious security bugs because it's so hard to understand.

Peak HN right here folks. Pack it in we are done.
Impossible– no part of the thread has involved someone suggesting rewriting it in Rust, which means there's no opportunity for someone else to reply "This project seems like a perfect fit for golang, why would you suggest they use Rust instead?"
I could write this thread in an afternoon.
Deep cut! For newer HN users, that's a reference to this comment responding to the original DropBox announcement. https://news.ycombinator.com/item?id=17732980
I have the same issue, but I blame the absence of good package management. If it had that, one of the thousands of these libraries would have won out and become quasi-standard.
I always thought the glib part of gtk was really nice for this, but adoption is spotty at best.
glib is a massive dependency just for this, and I think many would argue that gtk in general is not a good idea for painless cross-platform development in 2023.
This is how I felt writing Go. Writing the same nongeneric functions with slightly different type signatures and/or endless interfaces and/or interface{} signatures. It's 2023.
You know Go has had generics for the last 15 months, right?
Yep! Why?
never mind than MANY people dont need generics, and that generics have a significant compilation and runtime cost, in terms of time and memory. who cares right?

and never mind that Go has had generics for over a year now right? sometimes having a small, stripped down language is better than having a huge bloated monster. I would point to examples, but you know what they are.

The Gopher narrative timeline as I remember it was:

1. Generics are bloated and don't allow us to have a single-pass compiler, which we need.

2. You don't need generics because go generate covers all the cases that generics cover (please ignore that this is a second pass).

3. Go has generics!

My recollection of the timeline is a bit different:

1. The current proposals for generics are bloated and don't fit properly with our vision for Go.

2. We will do heaps and heaps of work over many years until we get something we like.

3. Go has generics!

Also, AFAIK Go is not a single-pass compiler, at least not in the way I learned about compilers.

> 1. The current proposals for generics are bloated and don't fit properly with our vision for Go.

> 2. We will do heaps and heaps of work over many years until we get something we like.

I suppose repeatedly suffering the pain of not having generics until you finally get enough of your community to admit generics aren't just bloat can be described as "heaps and heaps of work". ;)

I'd love to hear what meaningful differences you think exist between early proposals for Go generics, and later proposals for Go generics. Or Go generics and generics which had existed in other languages for decades, for that matter.

The result of this silly delay will be felt basically forever, because millions of lines of go were written with error codes, which could have been much-less error-prone option types. I'm not sure it would even be a good idea to start using option types at this point because it would create such inconsistencies in codebases.

> Also, AFAIK Go is not a single-pass compiler, at least not in the way I learned about compilers.

Correct! But it was single pass and that was very important because multiple passes were bloat. But multiple passes aren't bloat any more, because now Go has multiple passes and Go doesn't have the bloat of other languages. See how that works?

"The past was alterable. The past never had been altered. Oceania was at war with Eurasia. Oceania had always been at war with Eurasia."

> option types

Go couldn't have Option before, and it cant have option now. Option is not only generic, but its also an Enum, which Go still doesn't have:

https://doc.rust-lang.org/std/option/enum.Option.html

so its not really clear what you mean by this.

Quoting 1984 when discussing the history of a programming language really detracts from any point you’re trying to make.

It’s not like we’ve seen some massive regression in compiler performance. Circumstances changed, and the Go developers changed their minds. What would you do?

Yeah, for years C++ said they didn't need sealed/final classes. Then they finally added it. Similar "re-editing" of history was done.

When new C++ standard libraries are written, there is now active, public effort to review what exists in other languages. Finally.

> never mind than MANY people dont need generics, and that generics have a significant compilation and runtime cost, in terms of time and memory. who cares right?

I'm saying that I wanted them...? I didn't say need. I was able to operate without them. I simply _wanted_ them.

Also maybe consider the way you're communicating? I didn't say Go was wrong. I said I didn't enjoy it. I'm allowed to not like things, and I'm allowed to post about them.

_Also_ this concept of "need" is amusing to me. You don't _need_ a garbage collector, or a statically linked binary, or static typing, or IDE support, or a debugger, or, or ,or.

And yet, people _want_ them.

> and never mind that Go has had generics for over a year now right?

Their generics implementation is pretty bad IME. Interfaces lacking type parameters seems pretty untenable to me. I'd rather just not use them.

> sometimes having a small, stripped down language

Of all the things Go is, I wouldn't say its either of these.

But also, if you respond, please try to not be so defensive. You can like Go, that is a valid thing to do.

Very good points about need vs want. Every time I hear a response to a reasonable software request at work with, "Well, do you really need it?". I pat my left leg. It's a good leg; I like it; Very helpful, but, sadly not strictly necessary.
It's still on my "I keep meaning to play with this" list because I keep getting distracted by other shiny things but you might find gomacro interesting.
Many people don't need generics? Significant runtime cost? I would argue that everyone should be using type-safe collections like maps, lists, etc. (I know these are built into the language in Go, but they're basically special-cased generics). And, if anything, not having generics has more cost because you end up boxing your values to place into collections rather than letting the compiler monomorphize it. The compilation cost can be more expensive, yes, but it saves a lot of work you would be doing by hand.

While Go has had generics for a while now, the vast swath of existing Go code doesn't. The situation is definitely improving but there is still a lot of non-generic legacy cruft.

> you end up boxing your values to place into collections rather than letting the compiler monomorphize it.

you know that Go has interfaces right? and I dont just mean the "any" interface. you can write your own interface similar to io.Reader and others, then add types that implement that interface, then no type matching is needed.

> non-generic legacy cruft

AKA normal, fine code. generic code is not some magic pixie dust that makes bad code into good code. plenty of awful bloated slow generic code around as well.

If you are using interfaces, the value is necessarily boxed as the storage for the value may be heterogeneous. Once a value is typed as e.g. `io.Reader`, dispatching to its methods necessarily requires a vtable lookup (i.e. runtime cost!). Compare this to parametric polymorphism where you can avoid the type erasure and perform static dispatch at compile time. Though, unfortunately, Go's implementation of generics ("GC shape with stenciling" instead of full monomorphization) still ends up incurring some runtime cost.

With regards to your second point, there are definitely situations where generics are vastly preferred: type-safe collections being a big one. For instance, the standard library containers (https://pkg.go.dev/container) are still non-generic with no generic versions in the standard library yet. This is the kind of cruft I mean: generic collections can be turned into concrete collections with type safety, but not the other way around. I make no claims about use of generics making your code being absolutely good or bad, but I do make claims that use of generics can make your code less error-prone and more safe.

> I make no claims about use of generics making your code being absolutely good or bad, but I do make claims that use of generics can make your code less error-prone and safe.

Right. this is the issue right here. people only see that Go didn't have generics, and they never stop for a second to think WHY Go didn't have them. Generics have an implementation cost for the Go project, and maintenance cost for the Go project, and end user negative impacts in regards to time and memory. but advocates often dont know or care about these drawback, and only howl that GENERICS ARE MISSING until they are added, consequences be damned.

I'm just saying that while generics are useful in some cases, they are not always the right answer, and it shouldn't just be assumed that every language needs them, nor that any non-generic language "sucks".

What kind of argument is this? Nobody says it’s “magical pixie dust that makes bad code into good code.” It’s a way to avoid writing repetitive code over and over and the runtime cost can be insignificant depending on implementation.
I know it's not your code, but related to this comment, it looks like it missing a check for lks == null?

https://github.com/robdelacruz/lkwebserver/blob/main/lkstrin...

That assert is checking a library invariant; it should never fail unless there’s a bug in the string library itself (although I’m not entirely sure this string library would tolerate a malloc failure from a quick glance through).

This is distinct from checking the parameters; if lks is null then the user of the API has made an error. Some libraries may sanitise user parameters, others don’t. At any rate, an assert would be the wrong choice to check user parameters since this would result in a (recoverable) user error leading to an abort unless the assert is disabled at compile time (-DNDEBUG), returning an error would be a better choice.

assert is an acceptable way to deal with precondition failures.
Thanks, much of the asserts were scaffolding while I was programming the helper functions. They function similar to comments, reminding me of the internal conditions that should always be valid.
>It's fun to write this (and read others' versions) the first 3 or 4 times, but it gets old quickly.

I mean, you can always just use an existing string library or reuse your own. There's no reason to rewrite string operations for every project.