10 ms·
The 4-chan Go programmer
- lemursage 2y agoA timeless classic from Buena Vista Social Club comes to mind https://www.youtube.com/watch?v=o5cELP06Mik https://www.youtube.com/watch?v=o5cELP06Mik
- theideaofcoffee 2y agoI'm going to post a low-effort, non-substantive comment because I can. That meme in the first few paragraphs made me actual lol as a recovering C programmer. That is all. I love seeing odd contortions of languages like this, and C has opportunity aplenty, interesting to see it in Go.
- kardianos 2y agoThe "chan chan Value" or "chan struct{resp chan Value}" is actually a pattern I've used in a very specific situation. It's a situation where a message bus probably could have been used instead, but then you'd have a message bus to handle...
- mbivert 2y agoProbably similar to the concurrent chat server from gopl[0] then. The pattern is a bit surprising at first, still fairly readable. [0]: https://github.com/adonovan/gopl.io/blob/master/ch8/chat/chat.go https://github.com/adonovan/gopl.io/blob/master/ch8/chat/cha...
- jerf 2y agoI get "chan chan"s pretty routinely. It is when I send a message to some internal server/actor, and I also send it the channel to reply on in the message. Usually it ends up being a "chan struct { ... something with a chan in it ... }" rather than a literal "chan chan" you can grep for, but the principle is there. Very useful pattern; I consider it one of the basics in Go. "chan chan chan" never, as far as I know. It would be overkill to replace everything like that with a message bus. Very different performance characteristics for one thing. Go's channels are very much an internal-to-the-OS-process construct, if I'm talking internal to the OS process there's no value in "upgrading" to a message bus.
- attilakun 2y agoI used it too. It's useful to implement Promise-like semantics in Go.
- Lucasoato 2y agoThere is a Go developer that defends using channels four times like in this blog, he also made some great medium posts in which he details real use cases. His name is Christopher, if you want to find more about him, just Google “chris chan 4chan”.
- nomorewords 2y agogottem
- zachmu 2y agoYou are a bad bad man
- HideousKojima 2y agoNot to be confused with (current? former?) Google employee Christopher Poole, but I guess that's a moot point at this point.
- quickthrowman 2y agoSomething awful must’ve happened to that guy in the past.
- kurisufag 2y agoformer. he's a free man.
- ge96 2y ago
- _yb2s 2y agoAs a scientist that ends up working closely with actual professional software engineers... lots of the stuff they do looks like this do me, and I can't for the life of me make sense of why you'd do it. I have seen a single line of code passed through 4 "interface functions" before it is called that call each other sequentially, and are of course in separate files in separate folders. It makes reading the code to figure out what it does exhausting, and a few levels in you start to wonder if you're even looking at the right area, and if it will ever get to the part where it actually computes something.
- zimpenfish 2y ago> I have seen a single line of code passed through 4 "interface functions" I once had to deal with a HTTP handler that called `Validate` on interface A which called `Validate` on interface B which called `Validate` on interface C which called `Validate` on interface D which finally did the actual work. There was a lot of profanity that month.
- Cthulhu_ 2y agoI mean to a point that makes sense; you got your base data types like idk, a bank account number which can be validated, which is inside a bank account which can be validated, which is in a customer which can be validated, etc etc. Visitor pattern style, I believe?
- binary132 2y agoimagine allowing invalid values to exist
- ozr 2y agoSounds awful.
- withinboredom 2y agoThis is actually my preferred approach. If you want to put a 4gb base64 as your phone number, go right on ahead; best believe I will truncate it to a sensible length before I store it, but sure. Who am I to question your reality. Sadly, people abuse shit like that to pass messages (like naming Spotify playlists with messages to loved/friends/colleagues while in jail) and maybe we have to assert a tiny bit of sanity on the world.
- spookie 2y ago> There's an old programming joke from back in the days when C and its derivatives dominated the landscape. We are still living in those days.
- fricze 2y agoFun stuff
- PhilipRoman 2y agoThe closest thing to this I've seen in actual code is BlockingQueue<BlockingQueue<List<T>>>, which was used to wait for an element from multiple blocking queues simultaneously, where each element is a list of messages.
- tedunangst 2y agoWould benefit from an explanation of what's wrong with the chan chan pattern, which is fairly common. Are you similarly overwhelmed by the fact http.Response has a pointer to a http.Request?
- caerwy 2y agoHere's a counterpoint blog using a chan that sends a chan in order to implement a dynamic dispatch mechanism. This is in Limbo not Go. But same concepts. Maybe the complexity proves the point. https://ipn.caerwyn.com/2007/07/lab-78-dynamic-dispatch.html?m=1 https://ipn.caerwyn.com/2007/07/lab-78-dynamic-dispatch.html...
- jhoechtl 2y agoFor those wondering, Limbo is a predecessor of Go hand used in the Inferno operating system, a descendant of plan9.
- malkosta 2y agoIt reminds me of Joe Armstrong's "My favorite Erlang Program": https://joearms.github.io/published/2013-11-21-My-favorite-erlang-program.html https://joearms.github.io/published/2013-11-21-My-favorite-e...
- zemo 2y agochannel of channel is a normal pattern, although it more commonly looks like a channel of struct vals, and the struct type has a field that's a channel. it's lets you send requests that are completed, whose values can be awaited on out of order, to avoid head of line blocking. something like `type request struct { params, reply chan response }`, where you send those requests on a channel, they get dispatched to some worker, and then the worker does the work and puts the result on the reply chan. But two is as high as I've ever found useful, I don't think I've ever seen a use case for a chan chan chan.
- david2ndaccount 2y agoSomewhat a reference to https://wiki.c2.com/?ThreeStarProgrammer https://wiki.c2.com/?ThreeStarProgrammer
- __s 2y agoSee also https://esolangs.org/wiki/Three_Star_Programmer https://esolangs.org/wiki/Three_Star_Programmer which shows more indirection is all you need
- twp 2y agoSending channels over channels (i.e. chan chan) is a common pattern for publish-subscribe systems, e.g. https://github.com/twpayne/go-pubsub/blob/master/pubsub.go https://github.com/twpayne/go-pubsub/blob/master/pubsub.go
- cedws 2y agoThat’s some pretty Go code, I like it. Just out of interest, why have a channel for subscribe/unsubscribe? Did you want to avoid a mutex on the subscriber map?
- distortionfield 2y agoNot the OP but yeah, that’s usually the reason I reach for a channel in this case. A mutex or wait group is just begging for a deadlock to rise up when you least expect.
- Thorrez 2y agoCan't channels deadlock too? Sending or receiving on a channel will block by default until the other side receives/sends the value if the channel is unbuffered, or it's buffered but the buffer is full. You have to write extra code to make it non-blocking; the pubsub example under discussion is blocking. I don't see how switching the discussed code to a mutex would make it any more blocking or deadlock prone. I found a bug in production caused by the author not realizing a send on a channel can block.
- ianpenney 2y agoThis is either parody or the making of someone who’s never done real work. Source: if you do this stuff or even dare to name another microservice after a Harry Potter character when 11.5 million people rely on it, I’m going to dedicate the rest of my life to having you fired from everywhere.
- enneff 2y agoGoogle is chock full of services with whimsical names. I’m not a fan either but I think in this industry you’re fighting a losing battle.
- distortionfield 2y agoTbh, I would rather a whimsical but unique and memorable name than another “tax-pool-cache-service” type of name to try and reason about.
- j-bos 2y agoEspecially when the repo gets contorted into some other TPS service.
- mook 2y agoI've had the displeasure of depending on one such project, which means I'd need to look at it once every few months. Need to page all the vocabulary back every time. (Actually it was a project with multiple subprojects done by different teams. So I'd need to page a different set of vocabulary every time.)
- metadat 2y agoHow is this related to 4chan or any other social media? It seems like a nonsense troll title designed to waste every reader's time.
- Uptrenda 2y agoYeah, I have to agree with you here. 4chan does actually have a well-developed programming culture with their own memes. They came up with a quirky algorithm for sorting numbers using sleep (not practical but fun and very out of the box.) And they seem to really appreciate lisp like a lot of people here. There is sometimes good overlap between HN and 4chan. But that doesn't seem to be the case here.
- metadat 2y agoThanks, we are on the same page.
- CyberDildonics 2y ago4chan's programming board is almost completely people who just learned to program arguing about languages, there is very little there that comes from real experience.
- antonyt 2y agoWho said it was related to 4chan? The title promised 4-chan go programming, and that's what the piece delivers (`chan chan chan chan int`). It's just a harmless pun.
- ijustlovemath 2y agoIn LabVIEW code we sometimes do this as a means of receiving asynchronous response data; instead of dumping responses in a queue, the message you pass includes a callback event channel, which you can use to notify exactly the same caller that the operation is done, and what the results are. A little wasteful on memory, but since theyre single use and are closed upon responding, the ergonomics are worth it
- 9cb14c1ec0 2y agoI was expecting something much different when I clicked that link. Guess I'm not enough of a Go programmer to immediately spot the joke.
- fsndz 2y agosome people are just addicted to unnecessary complexity
- Thorrez 2y agoA couple suggestions for improving the code: * Currently sendChanChan() takes 2 params: a 4chan and a 3chan. I think it can be simplified by just taking a 3chan. The send on the 4chan can be done before calling sendChanChan(). * Why does the code have 2 levels of loops in receiveChanChanChan()? I think a single loop would be fine. Especially since there are 0 loops in the place where receiveChanChanChan() is called. So currently, a single goroutine performs N receives all the values from the 4chan, but we have N goroutines to receive values for each 3chan (so each goroutine will perform on average 1 receive). That's inconsistent.
- kofejnik 2y agoa nice one! ᖧchan (instead of _4chan) would've been even cooler
- Twey 2y agoThe irony of the comments in this thread decrying this as a classic example of too much abstraction is that the reason you rarely see three-star variables in C is that, while it's common to have long chains of pointers (and in languages where ~everything is a pointer~ most things are pointers by default, like Python, Java, or JavaScript, you definitely have pointer chains that are much longer than 4!), what is uncommon (but not nonexistent) is the need to manipulate more than two levels at once — you'll have one, maybe two, levels of pointers, but anything deeper is probably going to be hidden in a struct whose internals you don't have to care about (i.e. abstracted away). The same argument applies to channels, which are roughly to concurrent code what pointers are to sequential code: the fatal flaw here, if there is one, is not too much abstraction, but perhaps too little abstraction. But then, I don't know the codebase — maybe a `chan chan` was exactly the right abstraction for what they were trying to write :) Certainly in highly-concurrent languages like Erlang, it is very standard practice to send a PID to another PID, for example to identify the process that should receive the reply to the message.
- chrispine 2y agoHeh! I remember getting shit about this early in my career because of a 3-star commit, because wtf is wrong with someone who needs a pointer to a pointer to a pointer? But it wasn't; it was the address of an array of strings. Because C. It was the most straightforward solution, which I maintain to this day (some 20 years later). (In my co-worker's defense, it probably had no comments. That part was on me.)
- xboxnolifes 2y agoI think the biggest problem with these indirections isn't necessarily the amount of layers, but that they are unnamed (and naming isn't really well supported in most languages outside of just typedef-ing symbols). ***char vs *array<string> (or something similar) Or in the article's case, naming what the 2/4 layers of channels were.
- __s 2y agoI did end up using a `chan chan` when implementing a threadpool: https://github.com/PeerDB-io/peerdb/pull/1613/files#diff-4271e6cae9f94319668184d987f2869fc2abafe2b5a7067d14faf852826f24e7 https://github.com/PeerDB-io/peerdb/pull/1613/files#diff-427... The inner channel represents a future, while the outer channel is fed by threadpool workers to a finalization reader. This way the ordering doesn't get corrupted by parallelism
- ranger_danger 2y agoThat image with the pointer types is also derived from a frequently-posted-on-4chan picture from the youtuber "Laurie Wired" aka "the queen of /g/": https://0x0.st/XvfB.jpg https://0x0.st/XvfB.jpg