[HN Gopher] The 4-chan Go programmer
___________________________________________________________________
The 4-chan Go programmer
Author : ingve
Score : 177 points
Date : 2024-08-28 18:35 UTC (4 hours ago)
(HTM) web link (www.dolthub.com)
(TXT) w3m dump (www.dolthub.com)
| lemursage wrote:
| A timeless classic from Buena Vista Social Club comes to mind
| https://www.youtube.com/watch?v=o5cELP06Mik
| kardianos wrote:
| The "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 wrote:
| Probably 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/cha...
| jerf wrote:
| I 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.
| theideaofcoffee wrote:
| I'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.
| UniverseHacker wrote:
| As 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 wrote:
| > 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_ wrote:
| I 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 wrote:
| imagine allowing invalid values to exist
| ozr wrote:
| Sounds awful.
| withinboredom wrote:
| This 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.
| steezeburger wrote:
| How do prisoners have access to Spotify?
| quectophoton wrote:
| > imagine allowing invalid values to exist
|
| Pretty common, for example when using databases as a
| mostly dumb store with all the logic in application code,
| and then a second application (or big refactor) appears
| and they introduce a subtle bug that results in an
| invalid `INSERT` (or whatever), and the database happily
| accepts it instead of rejecting it.
| agency wrote:
| imagine enforcing invariants as part of the design of a
| software system
| nickpeterson wrote:
| The nerve, taking good jobs away from young qa testers.
| Wait till the IT Union hears of this!
| zimpenfish wrote:
| That would make sense but this was one piece of data being
| validated against a lookup based off that data. The
| previous devs just had a, uh, Unique(tm) style of
| development. I swear they must have been on some kind of
| "editor tab count" bonus scheme.
| layer8 wrote:
| This can happen when some of the interface operations have
| added logic, but others (like Validate here) don't, so just
| get delegated as-is.
|
| One typical example is a tower of wrapped streams, where each
| layer applies additional transformations to the streamed
| data, but the Close operation is just passed across all
| layers to the lowest one (which closes the underlying file or
| whatever).
| hyperhello wrote:
| I enjoy reading about new languages on HN. It's weird because
| the grammar is weird. It's like seeing start var ! oper
| addition sub var !! mltplctn var % close close or something and
| eventually you realize it means a + b*c. Why invent a new
| language? Why not just write list<int> if you want a list of
| integers or write Channel<string> or one of the idioms that
| everyone knows? I don't know, but it can be fun to play with
| communication and meaning and grammar, and maybe even more fun
| if you get to be part of a group that gets paid for it.
| recursive wrote:
| What "everyone knows" depends on who everyone is. There was
| syntax prior to templates with angle brackets.
| eddd-ddde wrote:
| The coder spectrum, scientists on one end, software engineers
| on the other. Only balance can save us.
|
| I have read code used in research papers. The theoretical math
| usually goes beyond my comprehension, so I always dive into the
| code to better understand the logic, only to find... it's way
| worse... unintelligible.
|
| At the end of the day we are used to what we do, and anything
| different will be foreign to us.
| dlisboa wrote:
| A lot of the code written in math, physics or data analysis
| settings is really written for the authors alone to
| understand. They benefit from tens of pages of documentation
| (papers) plus decades of previous experience from the
| readers. None of which commercial software systems have.
| mbivert wrote:
| > I can't for the life of me make sense of why you'd do it.
|
| Over-engineering is a common cause: simple solutions can be
| deceitfully difficult to find. That being said, additional
| indirection layers are usually justified by the overall
| architecture, and -- assuming they're reasonable -- can't
| always be appreciated locally. << I'm always
| delighted by the light touch and stillness of early programming
| languages. Not much text; a lot gets done. Old programs read
| like quiet conversations between a well-spoken research worker
| and a well-studied mechanical colleague, not as a debate with a
| compiler. Who'd have guessed sophistication bought such noise?
| >> (Dick Gabriel)
| prisenco wrote:
| _" I apologize for such a long letter - I didn't have time to
| write a short one."_ - Mark Twain
| kubanczyk wrote:
| Twain? https://news.ycombinator.com/item?id=769624
| prisenco wrote:
| [?]
| Misdicorl wrote:
| Ignoring inexperience/incompetence as a reason (which,
| admittedly, is a likely root cause) domain fuzziness is often a
| good explanation here. If you aren't extremely familiar with a
| domain and know the shape of solution you need a-priori all
| those levels of indirection allow you to keep lots of work
| "online" while (replacing, refactoring, experimenting) with a
| particular layer. The intent should be to "find" the right
| shape with all the indirection in place and then rewrite with a
| single correct shape without all the indirection. Of course,
| the rewrite never actually happens =)
| withinboredom wrote:
| As a software engineer, this is something I get onto with my
| team. There is a such thing as too much abstraction and
| indirection. Abstraction should serve a purpose; don't create
| an interface until you have more than one concrete
| implementation (or plan to within a PR or two). Premature
| abstraction is a type of premature optimization, just in code
| structure instead of execution.
| tomcam wrote:
| 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.
|
| Trigger warning next time. I was trying to eat lunch
| jdc0589 wrote:
| this is the classic over abstraction problem so that you _can_
| change things behind an interface at some point down the line
| if you ever need to while being totally opaque to any consuming
| code.
|
| A lot of languages force you to start with this from day one,
| unless you want to go refactor everything to use an interface
| later on, so people just do it even when there will literally
| never be a reason to (and for testability, _sometimes_ ).
|
| The cool thing about Go is the interface system is inverted
| kind of like duck-typing, so if you write purely idiomatic Go,
| then the thing receiving an arg in a function call is the one
| specifying the interface it must meet, rather than the
| implementing code having to declare every interface that some
| implementation meets.
|
| People screw this up a lot though, especially if they came from
| Java/C# backgrounds.
| diarrhea wrote:
| Do you have a concrete example by any chance?
| devjab wrote:
| Most C# education will teach you to always make an
| interface for everything for some reason. Even in academia
| they'll teach CS students to do this and well... it means
| there is an entire industry of people who think that over-
| engineering everything with needless abstractions is best
| practice.
|
| It is what it is though. At least it's fairly contained
| within the C# community in my part of the world.
| weitendorf wrote:
| This is actually really bad practice and a very "over eager
| junior engineer" way of writing software. You're not off base
| at all that it seems excessive and confusing. It's the kind of
| thing that seems technically complex and maybe even "elegant"
| (in isolation, when you first write the "interesting" code) at
| first but becomes a technical nightmare when used in real
| software that has to grow around and with it. You're actually
| more on point in worrying about the understandability and
| debuggability this introduces.
|
| I spent the better part of two years unfucking some Go software
| that (among other things) misused channels. The problem with
| channels is that you rarely actually need them, but _can_ use
| them for a lot of different things without too much initial
| difficulty.
|
| I think a good litmus test for proper use of channels is if you
| answer no to "could this be done with a direct function call
| instead?" and "can I use a wait group or mutex instead", and
| yes to (zooming out a bit to think about what kind of decisions
| you previously made that led you to think about using channels)
| "am I really benefitting from concurrency/parallelism enough to
| justify the technical complexity of debugging concurrent code".
| zachmu wrote:
| when I was learning Go, I read a guide that told you to fire
| off a goroutine to walk a tree and send the values back to
| the main goroutine via a channel. I think about that "just an
| example" guide a lot when I see bad channel code.
|
| For me the biggest red flag is somebody using a channel as
| part of an exported library function signature, either as a
| param or a return value. Almost never the right call.
| lanstin wrote:
| I've used that pattern to write tools to e.g. re-encrypt
| all whatever millions of objects in an S3 bucket, and
| examine 400m files for jars that are or contain the log4j
| vulnerable code. I had a large machine near the bucket/NFS
| filer in question, and wanted to use all the CPUs. It
| worked well for that purpose. The API is you provide
| callbacks for each depth of the tree, and that callback was
| given an array of channels and some current object to
| examine; your CB would figure out if that object (could be
| S3 path, object, version, directory, file, jar inside a
| jar, whatever) met the criteria for whatever action at
| hand, or if it generated more objects for the tree. I was
| able to do stuff in like 8 hours when AWS support was
| promising 10 days. And deleted the bad log4j jar few times
| a day while we tracked down the repos/code still putting it
| back on the NFS filer.
|
| The library is called "go-treewalk" :) The data of course
| never ends back in main, it's for doing things or maybe
| printing out data, not doing more calcualation across the
| tree.
| SomeCallMeTim wrote:
| I saw some code in a job I was just starting where they had
| added several abstractions that I found...confusing.
|
| After taking an extra long time to understand what the code
| actually did, I realized that some junior engineer had been
| using some design pattern they didn't really understand, and
| that added zero actual value to the routine.
|
| After deleting all of that code and refactoring it to use
| completely different abstractions, everything was suddenly
| much easier to read and to extend.
|
| Design is a hard skill to learn, and junior developers
| profoundly haven't learned that skill yet. But that's what we
| need to teach them as senior engineers, right?
|
| Not that I could teach the author of the code I changed,
| since I think it was written by an intern that no longer
| worked for the company. But you do what you can.
| RandomWorker wrote:
| There is also the fact it's much easier to write something
| when you know where you are going. When you start you often
| just make lots of items general in nature to improve later
| on.
| tejtm wrote:
| I chalk it up to "complexity envy" If they have real problems
| why can't we?
|
| source me: CS by edu, science geek by choice.
| yarg wrote:
| It gets worse (this isn't too much of an exaggeration): https:/
| /github.com/EnterpriseQualityCoding/FizzBuzzEnterpris....
|
| Sometimes there's a scary lack of understanding and competency
| where you'd expect to find it.
|
| As an undergrad, I once spent about half an hour peer
| programming with a computer science PhD - it was enlightening.
|
| He didn't have the slightest understanding of software -
| calling me out for things like not checking that the size of a
| (standard library) data structure wasn't negative.
|
| But other times these things are done for a reason; sometimes
| it's actually sane and sometimes it's just a way to deal with
| the lunacy of a codebase forged by the madmen who came before
| you.
| Dansvidania wrote:
| the fizzbuzz repo hurt. It is oh so true though.
|
| I often wonder if, unbeknownst to me, I am writing similarly
| over-complicated software. People seem to be unable to tell,
| so I doubt I can either. It makes me second-guess my code a
| lot.
|
| Is there any reliable/objective/quantitative way to evaluate
| such a thing? The repo is a great example of what not to do,
| but it's so extreme it's hardly useful in practice.. (nor it
| should care to be..as a joke)
| nickpeterson wrote:
| Just delete some code and see if it still works. Bonus
| points if you can delete abstractions.
| devjab wrote:
| If you have abstractions that you weren't forced to make
| after exhausting every other option then you can improve
| your code. If you begrudgingly add abstractions when you
| can no longer convince yourself that there must be away to
| avoid it, then you're likely doing well.
| yarg wrote:
| I think it's circumstantial - do your abstractions make it
| easier or harder to implement and integrate the sort of
| features that the application generally requires?
|
| There's sometimes significant use in having some powerful
| abstractions in place to allow for code reuse and
| application customisation - but all too often people build
| fortresses of functionality with no idea how or if it's
| ever going to be used.
|
| Foresight is useful here; if you can look at new features
| and break them up into feature specific business logic and
| generalisable application logic, then similar features can
| be cleanly integrated with less work in the future.
|
| Sometimes however, the level of customisability far exceeds
| what is strictly necessary; the complexities involved no
| longer help, but rather hinder - not only understanding,
| but feature implementation and integration as well.
| lowbloodsugar wrote:
| I mean we are in "midwit meme" territory here. 4 levels of
| indirection look fine to an idiot. A "midwit" hates them and
| cleans them up. But a very seasoned engineer, thinking about
| the entire system, will happily have 4 layers of indirection.
|
| Like anyone using a HashSet in Rust is already doing four
| layers of indirection: rusts HashSet is actually wrapping a
| hashbrown::HashSet. That set is wrapping a HashTable and the
| HashTable is wrapping an inner Table.
|
| If you're having trouble comprehending such code then a good
| IDE (or vim) that can navigate the code on a keypress should
| help.
| jayd16 wrote:
| ITT people who have no context passing judgement on code
| they've never seen described by someone who doesn't know what
| they're describing.
| ljm wrote:
| On the contrary, and I do agree that software engineers take
| the abstraction too far when they don't know better, I don't
| hold the code produced by people who aren't software engineers
| by profession in particularly high esteem either.
|
| You're looking at two extremes: the codebase that is spread out
| too much with too much abstraction, and the codebase with zero
| abstraction that is basically a means to an end. In both cases
| they are difficult to work with.
|
| I've certainly dealt with enough python, JS and PHP scripts
| that are basically written with the mindset of 'fuck this, just
| give me what I want', whereas people working in the code day to
| day need the abstractions to facilitate collaboration and
| resilience.
| computerdork wrote:
| Agree with this. Abstraction and design patterns _when used
| in a well-thought out manner_ should make _large or complex_
| codebases easier to work with.
|
| And like you, have experienced code bases that tried to throw
| every design pattern in the book at you, even for a
| relatively simple application, and made it a pain to work
| with.
|
| But have also seen them used carefully, in a standard
| company-wide usage that made all the code easier to
| understand - worked on a high-volume website with a large
| codebase, where they had micro-services that all used common
| 3-tier architecture, security-services, tooling... Really-
| well thought-out and you could work on any one of their ~100
| microservices and already have a good understanding of its
| design, how to build and debug it, how its security worked,
| it's caching...
|
| Yeah, agreed, its how these techniques are used that
| determine if they are useful or just add complexity.
| LudwigNagasena wrote:
| It happens so that we don't have "a single 15,000 line file
| that had been worked on for a decade". We don't have the luxury
| of asking the GitHub team and John Carmack to fix our code when
| we are forced to show it to the stakeholders.
| stroupwaffle wrote:
| I think something people forget is that computer programming is
| a craft which must be honed.
|
| A lot of people are introduced to it because computers are such
| an important part of every discipline, but unfortunately the
| wealth of mistakes maintaining many a code base occur from
| those who, quite honestly, simply lack experience.
|
| In the authors case, they don't explain the simple
| understanding that every pointer is simply adding a dimension
| to the data.
|
| int *data; // one dimensional vector
|
| int **data; // two dimensional matrix
|
| int ***data; // three dimensional matrix
|
| Which is one way to interpret things. The problem is that when
| folks learn computer programming using Python (or another high
| level language), it's like using power tools bought from the
| hardware store compared to some ancient, Japanese wood working
| technique. The latter takes time to understand and perfect.
|
| Ten thousand hours, so I've heard ;)
| THBC wrote:
| "every pointer is simply adding a dimension to the data."
|
| No, it's not. The concept of having a pointer to a pointer
| has nothing to do with the concept of dimensionality.
|
| You must be thinking of lists of lists (of lists...), which
| can be implemented using pointers. The dimensionality,
| however, comes from the structure of the list, not from the
| pointers.
| 7thaccount wrote:
| This perfectly summarizes a lot of production code I've seen.
| We once replaced like 20 Java files with like a single page of
| easy to understand and "to the point" code.
| astrange wrote:
| This is a popular pattern in apps or other "frameworky" code,
| especially in C++.
|
| I can think of at least two open source C++ apps I used where
| every time I checked in the IRC channel, the main dev wasn't
| talking about new app features, but about how to adopt even
| newer cooler C++ abstractions in the existing code. With the
| general result that if you want to know how some algorithm
| backing a feature works, you can't find it.
| sweeter wrote:
| A lot of people suffer from the tendency to do a lot of pre-
| abstraction. They also split everything up way to much without
| needing to
| stonemetal12 wrote:
| It is the Law of Demeter. It sounds like a good idea, until you
| get the chain of wrappers.
|
| https://en.wikipedia.org/wiki/Law_of_Demeter
| devjab wrote:
| It's called Clean Architecture, Clean Code or SOLID and it's
| extremely stupid. It's widely used because the man behind it,
| and a lot of other grifters, are extremely good at selling
| their bullshit. You also have crazy things like the Agile
| Manifesto to thank "Uncle Bob" for.
|
| What is the most hilarious, however, is that these things are
| sold by people who sometimes haven't coded professionally since
| 15-20 years before Python was even invented.
|
| Anyway, if you want to fuck with them ask them how they avoid
| L1/L2/L3 chance misses with all that code separation. They
| obviously don't but you're very likely to get a puzzled look as
| nobody ever taught them how a computer actually works.
| spookie wrote:
| > 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 wrote:
| Fun stuff
| PhilipRoman wrote:
| The 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 wrote:
| Would 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 wrote:
| Here'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...
| jhoechtl wrote:
| For those wondering, Limbo is a predecessor of Go hand used in
| the Inferno operating system, a descendant of plan9.
| malkosta wrote:
| It reminds me of Joe Armstrong's "My favorite Erlang Program":
| https://joearms.github.io/published/2013-11-21-My-favorite-e...
| zemo wrote:
| channel 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 wrote:
| Somewhat a reference to https://wiki.c2.com/?ThreeStarProgrammer
| twp wrote:
| Sending 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
___________________________________________________________________
(page generated 2024-08-28 23:00 UTC)