yeah, i get it the points about how some things are irritating.

and i run into problems now and then, fairly often, with resource leaks.

a lot of people overuse interfaces, i agree about that. interfaces should be an after thought that makes extension simpler. until there is two viable implementations you should not make an interface out of it. it's just a waste of space and if you never need any more than one implementation of a method set then it's crazy to throw all that overhead into accessing the methods (resolving the indirection).

i sorta do sometimes wish there was an explicit keyword that manually designates a variable to be unused - nilling variables and using sync.Pool to mitigate this doesn't really quite work, and having no control over freeing the memory for especially tight, iterative stuff, can sometimes be quite an obstacle to getting it to not blow up the heap. but aside from that one little thing, which could even be a second reuse of the delete keyword (where it doesn't expect a key) and its function would be to explicitly discard the variable.

running into these invisible, opaque memory allocation behaviours most of the time you don't even have to think about it but when the heap piles up with junk and it isn't seen until the next mark/sweep it can make a situation where your baseline memory requirements are reasonable but frequently it uses 2-3x as much memory for short periods - yeah, if that baseline was 500mb and you try to run it on a 1gb VPS it will often OOM die and restart the service.

or maybe better would just be an explicit "free" operator. probably could cobble something together with a specific package that does all that unsafe and reflecty stuff, and alias the import to the package (i use this with logging so it doesn't have a cumbersome, long path to access the logger, just creates local variables referring to the main variables).

i don't really mind the for syntax of Go. making quick and dirty ascending count with `for range ` is often very handy, and it's generally idiomatic most DB transaction interfaces provide the initializer, test and iterate clauses of the for loop in trad C style syntax. also i think it's quite handy being able to use for by itself to create a single channel select statement, effectively.

anyway, as regards to the channels and goroutines, the absence of keywords and meaningful symbols to make usage compact and clear, instead of wrapping it all in methods... i like my <- -> stuff

also, as regards to using goroutines, i generally just try to stick to a 1 client 1 thread pattern. i've seen code that needlessly makes everything in a database query method concurrent, but a) it can't guarantee ordered delivery (this is implicit in the query contract of the REQ method of the API, though most clients just sort it again anyway, khatru and the eventsore based KV store databases that fiatjaf made... spawn like 5-10 concurrent threads for no reason to interact with a bottlenecked back end that can't do more than actually one read or write at a time anyway (the LSM logs). IMO, part of the edge of performance that #orly has over khatru is precisely that the database queries have zero goroutine overhead. goroutines are cheap but people are so easily seduced into peppering their code with `go` just because woo cool concurrency when it doesn't fit the purpose...

the other thing about concurrency, is that it really should be avoided as much as possible. go's work stealing scheduler is better now but there is not a 1:1 correspondence between the behaviour of a coroutine and a kernel thread, as regards to parallelism. threads are parallel. coroutines are not quite parallel. 6 years ago the difference was about 20%, it's probably more like 10% now with the many improvements since go 1.20.

anyhow, i'm not getting off the Go train without someone actually not focusing on the things i don't care about. i don't care about memory mutability, much more important is the ability to control the memory for such as storage of secrets. i don't care so much about not being able to explicitly free memory, but it's unquestionable for high performance servers there's almost always a glitch where the GC is either blowing up the heap or pushing latency up at the wrong time.

in order of motivations, these are why I am a golang maxi:

- fast compilation

- (because) no fucking inheritance

- simple language, consistent pretty printing format

- first class coroutines and channels

- package/module build system and modules caches make builds even faster after the first time, it's mere seconds most of the time to restart, almost makes hot-reload not so beneficial (most web apps are 5-15 seconds before they are fully started), when i can just restart my server with ctrl-c, up arrow, enter. 4 keypresses and one mouse move and click.

- last but not least, hipsters hate it, which means they won't waste their time trying to contribute to my projects (the Linus Torvalds Reason For C) - people who like C/C++, Rust and Java who think all that manual memory management is worth the cost for the pissy 5% boost in performance - i don't want to work with them, and being stubborn about sticking with Go most of the time keeps me out of such situations.

in order of irritation, the things that i don't like about Go:

- very poor support for GUI app dev

- lack of a manual memory free operator

- the broken semantics of slice/map types mutating themselves (and all the stars and ampersands and brackets) - imo they should BEHAVE like pointers for this case, and in general there is a bit of a nebulous cloud about which version of such reference types, which are essentially pointers but get value treatment. if it just kept the nice implicit dereference semantics but made mutations to the receiver happen outside of the scope of a method. meh. i just use struct tho

Reply to this note

Please Login to reply.

Discussion

Yeah.

Green threads are also not that useful on the client.

You often need to do operations on particular threads (eg macOS forces the main thread to b white GUI thread).

In cases where thread identity matters goroutines get in the way.

If Go had a way to drop into manual memory management for a block of code I’d be down with that.

yeah, manual freeing of memory and being able to access kernel threads are probably the two biggest pain points for me with Go. the first is needed to eliminate spikes in memory utilization, the second is for background scheduling of bulk processing (eg, PoW mining nostr notes or converting media formats, etc).

when it comes to UI dev with Go, i also have done a lot of work with Gio and it was quite challenging to design a scheme that efficiently leveraged the CSP concurrency, without putting too much compute in the render threads on that single mandatory thread. but i did it - the framework i was building had a mechanism for scheduling background tasks with a simple queue (just passing a closure by a channel) which let me push all the compute into that and leave the render code to focus on the rendering and give it little slots of memory (hey, they didn't have to be atomic, because one thread) to render from, and the moment a signal comes back from a background thread or external resource it starts rendering that data.

yeah, for graphics rendering/compute, for bulk processing of any kind, the ability to manually create CPU threads is probably the other thing really missing from the language, other than being able to manually free memory.

there's a few other spots here and there where it could be a bit better but it's ok, those two points are just the main tangibles for me.