I coded something dumb and I'm proud of it

(plbrault.com)

162 points | by drfreckles 72 days ago

19 comments

  • tleb_ 72 days ago
    Another way to describe what has been done: implement a pure function and avoid storing additional state. It sounds way less dumb that way. It is not really a pure function but the spirit is here.

    I've done the same during a refactoring of a side-project recently. It handles the input/output to a MIDI controller with many buttons, knobs and matching LEDs. Instead of computing what LED should change at regular interval, I am switching to recomputing the whole state each time. No more complex logic, no more mutable data. Only a pure function that outputs the desired LED state based on software internal state. Then a diff is computed and only changes lead to MIDI messages. Code is less efficient (for 100-ish LEDs) but much more straight forward.

    • pavlov 72 days ago
      This is how React works, or at least the illusion it presents to the developer.

      Where it goes awry and gets complicated is that web developers want to modify the input state directly within the same functions that produce the output state, and they also want to trigger side effects after the output state has been completed, requiring another pass.

      I’ve built a React variant for video compositing. Since it renders at a steady frame rate, there’s no reason to ever trigger re-renders. The useState() and useEffect() hooks are practically useless. To my personal taste it’s a sweet spot for React, and I wonder if some kinds of web apps might benefit from similar simplification to the state approach.

      • recursive 72 days ago
        I've also struggled with React's insistence on immutability. What if mutability was the only way to update state? I implemented a JSX-powered react-alike that explored the concept[1]. To my lack of surprise, I found the resulting environment easier to get stuff done in. I'm not subjecting my employer to this, but I would totally use this on a solo project that I had to support.

        [1] https://github.com/tomtheisen/mutraction

        • resonious 72 days ago
          One problem with this that I've found through similar endeavors is: the act of making the library results in you knowing how it works on a deep level. It's likely you don't know React on such a level. This inflates your perception of how easy to use it is. If you did know React that well, you'd probably be able to use it very effectively!

          Anyway I did take a look at Mutraction and it looks great actually. I've just made a lot of abstractions at work, and always been a little surprised by how hard it is for people to get used to them. Of course maybe I'm just bad at it. But ultimately it's made me kind of anti-abstraction all around. If everyone was as good with vanilla HTML, CSS, and JS (actually I'll approve TS) as they are with React, the web would be a better place </opinion>

          • recursive 72 days ago
            Before I built this, I spent a couple of weeks reading the react source code. Of course, it's huge, and I didn't touch most lines. Probably never saw most of them. But it was enough to understand dispatchers, work scheduling, and fibers.

            But you're right though. I still understand my own library better. However, I've really made an honest try to understand react (at least the client&DOM parts) as much as I practically could.

            I think I'm on-board with your anti-abstraction POV too.

        • eyelidlessness 72 days ago
          To be fair, this (like many applications of reactivity) is conceptually very different from embracing mutability. And at least at a glance, it looks conceptually much closer to React than that.

          Where React differs isn’t immutability, but where the mutation of state/effect boundary is (at the component/hooks-rules, versus something more fine grained).

          In every possible approach, a state change needs some orchestration to produce rendering updates. The approach taken here looks like a subset of a common reactive approach, not dissimilar to say Solid with its createMutable Proxy-based store. That’s much more palatable to me (and I expect it would be to even a lot of React devs) than a less disciplined free-for-all mutability take (which effectively devolves to “build your own state<->render abstraction, or just maintain state in the view itself, probably both”).

          • recursive 72 days ago
            Solid's proxy-based approach was indeed one of the major influences. It's also similar to reactive() in VueJS. There's one novel thing in mutraction that's not in either though, which is the undo/redo log. It might not be very useful in practice. I'm not under the impression that I really created anything fundamentally new here. I just scratched my own itch.

            Really, I think the main difference is that there's nothing in mutraction like a virtual DOM. Conceptually, it's dead simple. There are only real DOM nodes. This eliminates most of the use case for DOM refs as used in react, As you can just assign a JSX expression straight to a variable.

            I've seen the word "orchestration" used before with respect to UI framework architecture. I must confess, I don't understand what it means. By default, in mutraction, most mutations are immediately applied to the corresponding DOM elements. You can wrap blocks in transactions, but probably most of the time, you wouldn't. Is that orchestration?

            • eyelidlessness 71 days ago
              > There's one novel thing in mutraction that's not in either though, which is the undo/redo log. It might not be very useful in practice.

              On the contrary! That alone is cause for me to give it another look. Stuff like state history is sorely lacking in the industry in general, and can enable powerful things like time travel debugging. I’m super curious to look into how it works when I get a chance.

              > Really, I think the main difference is that there's nothing in mutraction like a virtual DOM.

              Clarification (as I presume you know this, but in case anyone else isn’t familiar): this is also how Solid works.

              > I've seen the word "orchestration" used before with respect to UI framework architecture. I must confess, I don't understand what it means. By default, in mutraction, most mutations are immediately applied to the corresponding DOM elements.

              That’s exactly what I meant in this context. Without something like reactive Proxy tracking and binding to the produced DOM nodes, you’ll have:

              1. Some mutable state, like objects and arrays and reassignable variable bindings.

              2. Some view DOM.

              3. Some code that manually assigns 1 to 2.

              4. Some code that manually handles events in 2 and applies mutations to 1.

              5. Recurse.

              This can be as “bare metal” as direct DOM interaction, but usually tends to look more like jQuery. As popular as that is in HN comments, it’s really hard to manage in applications beyond a certain level of complexity (interactivity, feature scope, etc).

        • p2edwards 72 days ago
          This is awesome. Thanks for making it.
      • _heimdall 72 days ago
        IMO react made way more sense when it was used only as the view layer. What react really needed was a separate paradigm for business logic. Having a state machine tied into react is really interesting to me though I haven't seen many try it, and I personally don't use react often enough to give it a go.

        React's render loop, and even JSX itself, makes plenty of sense when the data is just fed in and rendered. It falls apart really quickly when data is being changed from inside a component rather than must firing events, leaving us with a decade worth of duct tape trying to find a solution that works long term.

        • zeroonetwothree 72 days ago
          Many people use flux-variants like redux which is basically what you say.

          But they aren’t perfect either. And perhaps worse than the ’default’ way.

          The fundamental problem is a lot of state is local and doesn’t need to leak outside of the view (for example, is the mouse hovering on a button or not). Yet it can be hard to tell when that’s the case—imagine if hovering on a button now needs to call some logging code or update some status UI elsewhere on the page.

          If we were to store all that globally then it allows for pure rendering but it becomes unwieldy and hard to maintain. But if we don’t then you get the duct tape system.

          • _heimdall 72 days ago
            Yeah I did actually try a few flux-like libraries early on. I actually used redux for state management in an Angular 2 app. It worked better than I would have expected, but async was always a problem - at the time thunk and sagas were the solution and both were painful IMO.

            That's reall interesting though, I run into plenty of problems with shared state but don't actually remember having any real issues with local state. I haven't seen too much of a problem with components changing local state as long as nothing else can change it. Even if that local state is passed down to child components, changes would only happen in the one place and it should only cause a single re-render cascade.

            Where sate in react has really bitten me is when multiple components all try to read/write the same state, especially when some of it is async. Patterns can be used to hide or try to isolate it, but I've never seem it done in a way that feels cleaner or more fool proof than the idea of a state machine running entirely outside of react's component tree.

            • skydhash 71 days ago
              That’s what I try to do with every declarative UI i’ve worked on. Hoist all the logic that act on state outside of the view modules. Then you plug it in via functions. A list component may have only one hook (useItems) that take in a filter/search state and another (useSelection) that is dependent on the first. This ensures a clean relationship graph. Having everything in a smart component is where you got the spaghetti nightmare of relationship graph.
      • 1-more 72 days ago
    • rasz 72 days ago
      While this sounds elegant, its also computationally more expensive.
      • tleb_ 70 days ago
        In my MIDI controller case, the "compute state and send update" code takes about 8µs, up to a spike of 15µs. The worst case of the code in article is a sort on each frame on a 100ish array, it sounds safe to ignore. Profile before optimizing.
  • dontupvoteme 72 days ago
    this is an evil take but i think emojis are massively, massively underrated for use in signaling information (and massively overused in git readmes)

    A grimacing emoji when a process is thrashing, a fire emoji when it's eating CPU, a sweating smile emoji when the process is running longer than expected, etc etc etc.

    It sounds dystopian in a way but also useful - neat seeing them used here!

    • leononame 72 days ago
      Personally, I disagree. I'm probably in the minority, but emojis don't helot me much when conveying information and I see them more as visual clutter that makes it difficult to distinguish what's going on. This is especially the case when there are a lot of emojis (or other icons for that matter) instead of text, e.g. in menus. It makes it much harder for me to distill the information and it takes me longer to grok what's going on. Maybe I'm just a less visual type than others, but emojis actively make my experience worse.

      I like them in chat though.

      Edit: to clarify, e.g. the process list makes it harder for me, because there are emojis on every process. I'd find it a tad more helpful if there were only emojis on processes with events and healthy processes would just have nothing (like the hourglass only being present in some processes). Color coding the background also makes it much more difficult to distinguish the emojis for me.

      • Levitz 72 days ago
        Not to mention, they don't translate well to spoken word and are not easy to type on a keyboard either. "Why is svchost panting sweating red emoji CPU usage" is as stupid as it sounds.
      • necovek 72 days ago
        > I like them in chat though.

        I personally don't like them in chat too much either: I much prefer ":)" or ":(" or ";)" than actual visual it gets turned into — emojis being so colorful call the attention to them, whereas I simply want to signal the tone in a message — emoticon/emoji is not the core of the message unless that's the only thing I put out.

        But I am trying to go with the times (not that I had much choice as typing regular emoticons usually gets converted into emojis these days).

      • colechristensen 72 days ago
        The young will use and understand them much more.

        How many years will it be before the Oxford English Dictionary begins listing definitions for individual and groups of emoji? In 100 years they will just be an ordinary feature of language somewhere between a word and a punctuation mark.

        • acheron 72 days ago
          "What are letters?"

          "Kinda like mediaglyphs except they're all black, and they're tiny, they don't move, they're old and boring and really hard to read."

          -- The Diamond Age (Neal Stephenson)

          • ang_cire 72 days ago
            How would they have learned a contraction if everything is based on glyphs/ images?
            • colechristensen 72 days ago
              Plenty of words are contractions or otherwise combinations of other words that you don't know about unless you're particularly interested in language.

              "Goodbye" = "God be with ye" for example

              You don't think "will not" when you say "won't", "won't" is just a word you use with a meaning you understand long before you can write.

              • ang_cire 71 days ago
                See my other comment below, but all the things you're talking about work because English has a phonetic alphabet.

                You can break 'be with ye' into 'b', 'y', and 'e', and drop the rest. In languages like Chinese which do not have a phonetic writing system, you cannot drop individual sounds within a word/character in a rule-based way like contractions.

                Emojis are not a phonetic system, so unless you created an emoji for "they're", separate from the emojis for "they" and "are", you wouldn't have it as a word.

            • bee_rider 72 days ago
              Haven’t read it, but society is quite changed in The Diamond Age, I think. In the protagonist even speaking English? If not, we could assume it is sort of “translated” into English (in the sense that most fiction that doesn’t take place on modern day Earth is).
              • ang_cire 71 days ago
                This actually makes the most sense out of any of the replies, honestly.
            • thfuran 72 days ago
              You don't need to know how to write to speak.
              • ang_cire 71 days ago
                No, but languages that use glyphs that are monophonemic and non-phonetic (one-sound per-glyph/character) don't have contractions, which are a function of removing some component part of a word when combining them.

                For instance, both Chinese and Japanese use kanji/hanzi, but Chinese is monophonemic, and does not have a phonetic alphabet that characters can be broken into (radicals aside, which are not related to sound). Japanese does (kana).

                As a result, combining 2 characters in Chinese never changes the sound of some sub-portion of a character; it's either the whole sound that changes, or nothing. In Japanese, on the other hand, individual kana within a character can change (e.g. Rendaku), so for instance 'hito' (person) put twice in a row becomes hitobito instead of hitohito, because hito is comprised of 2 kana characters: 'hi' and 'to', and 'hi' becomes 'bi'.

                Emoji have no subcomponent characters, so you either would need an emoji for a contraction word in addition to the source words, or, more realistically, you just wouldn't have them at all.

                • thfuran 71 days ago
                  And you're saying that in Japanese and Chinese no word is ever informally pronounced in abbreviated fashion by eliding some phoneme(s)?
                  • ang_cire 71 days ago
                    In Japanese, yes, because it has a phonetic alphabet. For example, "konnichiwa" is often shortened as "kon'chiwa".

                    In Chinese, there is shorthand slang, but not shortening of words in writing based on spoken sounds (since it's not phonetic).

                    So even if "wo shi" (I am) might be spoken more quickly, there's no way to write that shortened version out in Chinese. You will actually see Chinese speakers use Latin characters and Arabic numerals for phonetic shorthand, (e.g. '88' for "bye bye", because 8 is pronounced 'ba'/'bai'.

            • DEADMINCE 72 days ago
              Because it wasn't always.
        • pessimizer 72 days ago
          I don't think they will. The problem with them isn't that people don't understand what they mean, it's that they require a lot of context to understand even when they are used naturally and freely. There's a reason why languages have grammar. They're really used for decoration, or to disambiguate between a short list of expected responses already established in the past by using words.

          i.e. a handful of emojis to explain the state of a machine is no more expressive than using a handful of colors to do the same thing. In that situation you'd react the same way to an emoji that I've thrown at you for the first time as with a color I've thrown at you for the first time. Suddenly the indicator is violet, or the indicator is smileyface emoji with big hearts for eyes: the question is what that meant to the programmer, and the emoji doesn't give any more indication than the color. "Are you trying to tell me that the server really loves my new blouse?"

      • thfuran 72 days ago
        There are also occasionally significant visual differences between the platforms. Like, a squirt gun and a handgun just aren't the same thing.
      • julianeon 72 days ago
        Incidentally in Slack you can easily set up a workflow where "emoji response -> text macro" (aka more information, a text supplement to your emoji). Very useful if you have a Slack channel that is deluged with questions.
        • fragmede 72 days ago
          reactj to create jira tickets, kick off PRs/builds/etc are the unsung workflow heros of slack
      • deredede 72 days ago
        Yeah, emoji on top of color is redundant and less legible. I find that people that use emojis for dashboards and the like tend to overuse them, but I agree with GP in that a single (well aligned and on an adequately colored background for contrast) emoji per item can convey a lot of info quite efficiently.
    • UI_at_80x24 72 days ago
      Yes and No.

      Many years ago I built an elaborate dashboard/status page that was a front-end for a dozen or so CLI processes that did the heavy lifting for our video->VR->CDN->website->SEO link farm.

      I used very simple "error codes" to flag when/where in the process errors would happen. 5 shapes, 5 colours, and 1-5 in numbers Square, Star, Circle, Triangle, Exclamation mark. Black, Blue, Grey, Yellow, Red 1,2,3,4,5

      Different people/departments would be check on different aspects of deployment. This prevented the glassy eyed blank stares when I would ask: What was the error code. Me and the other IT folks knew what each stage meant, along with the colour codes and severity number would allow us to pinpoint where in the process this happened. So this was a form of emojii, and it was VERY helpful. I would have preferred error codes/server number/step number but Bob in Marketing would just ignore that. He never could remember 'what it said'. But he always remembered "Red Square and #5"

      Symbols that are __EASY__ to identify (especially when attention spans are short) are tremendously helpful. [See Traffic signs as an example]

      Esoteric symbols that can change meaning and/or have no meaning in the context are HORRIBLE. I'm on the spectrum, I can't tell what any "face emojii" mean.

      • mr_mitm 72 days ago
        > I'm on the spectrum, I can't tell what any "face emojii" mean.

        Then again, about 3% of all people are color blind.

        • drfreckles 72 days ago
          That's precisely why I added emojis. In early versions of the game, the starvation levels were only represented by colors.
      • xmprt 72 days ago
        The problem is that symbols are also easy to misremembered or misidentified. It's easy to identify a red square but if Bob accidentally recalls a blue square or a red triangle, then all of a sudden you're looking for an error that didn't happen.
    • duxup 72 days ago
      When used thoughtfully, I find my clients love emojis on some things and they seem to work better than many web icons.

      Warning Emoji, a weight, phone emoji ... people see them every day and understand them immediately.

      https://emojipedia.org/warning

      Granted silly emojis, those that imply other things, eggplant. Not so much. And the burrito is just for developer type stuff.

    • nvartolomei 72 days ago
      You’ll love to learn more about Chernoff faces! https://en.wikipedia.org/wiki/Chernoff_face
    • scubbo 72 days ago
      https://en.wikipedia.org/wiki/Chernoff_face supports your take - humans are hard-wired to recognize and parse faces, why wouldn't we make use of that shortcut!?
    • deadbabe 72 days ago
      Emojis are incredible for variable names in code, it solves the hard problem of naming things.
      • thfuran 72 days ago
        Most importantly, they help make code easier to read than to write.
        • lmm 72 days ago
          Those who forget APL are doomed to reinvent it, poorly.
        • deadbabe 72 days ago
          omg yes
    • devmor 72 days ago
      Emojis are great for conveying the wide and varied levels of human emotion that differ from person to person that may type the same exact sentence with an entirely different meaning.

      This is especially true when you have repeated communications with someone and come to understand how and when they use certain emojis.

      For this same reason, I don't think they are great for technical information. They feel antithetical to the purpose of conveying exact information. You can use them as iconography, but purpose-made iconography is still superior, in my view.

    • ravenstine 72 days ago
      Emojis can be done properly. Infrequent use with good contextual positioning can break a person's preoccupation and direct their attention when it counts.

      Poor utilization of emojis, put simply, is using them all the time in ways that don't actually enhance the attention or meaning of the surrounding text.

      The problem with emojis is people like them too much, and I have little faith that they would be used wisely by most programmers if some influential figure like Uncle Bub Martin told everyone to start using emojis for all the things.

    • reidjs 72 days ago
      Agree, especially for conveying tone, eg, happy, frustration, danger, etc, especially in dialog.

      There is an art to using them to enhance a message instead of obscuring it, though.

    • bitwize 72 days ago
      cdparanoia used to use ASCII emoticons as status indicators -- :-) for when things were going well for instance.
    • AnimalMuppet 72 days ago
      More dystopian (and maybe more useful): A roll-eyes emoji when you're asking it to do something stupid.

      (It's more dystopian, because the computer has to know when you're asking it to do something stupid. Even more dystopian: It gives you the roll-eyes when you ask it to do something that it doesn't want to do.)

    • quectophoton 72 days ago
      My reaction: :skull_emoji: :skull_emoji: :skull_emoji:

      > a fire emoji when it's eating CPU

      Fire emoji obviously signals that everything is going nice and smooth tho.

      > etc etc etc.

      Or the bottom/submissive emoji when you're in root/privileged mode.

      Or the ok_hand emoji when there's something wrong (see ASL, and also The Expanse).

      /s

  • gumby 72 days ago
    > you are the operating system of a computer.

    "I am Jack's ALU"

  • AstroJetson 72 days ago
    Just spent 15 mins working as the OS, it's much harder than it looks.

    I just had a nice word with my browser with the 200+ tabs open, I now know how it feels.

    Only upgrade I'd make is how fast my "task switching" is, I'd like to think it was subsecond, but it's not.

    If you haven't played give it a shot. Then play the insane mode, with all the cores and memory it's pretty amazing to keep it all running.

    As to your code, pretty interesting idea. As noted (above?) I've also done control work, we only send a message to update the display when the value actually changes. Since we are on something like a CAN bus, we can't hog things making display changes at the expense of sensor readings.

  • Idiot211 72 days ago
    FYI: The link to "You're the OS" points to localhost OP :)
  • xg15 71 days ago
    I know this is exactly the opposite of the point OP wanted to make, but wouldn't this be the perfect use case for generators?

    You could modify the quicksort in python like this:

      def quicksort_with_steps(arr: [Process]):
          # Standard Quicksort operations
          if len(arr) <= 1:
              yield arr
              return
    
          pivot = arr[len(arr) // 2]
          left = [process for process in arr if process.sort_key < pivot.sort_key]
          middle = [process for process in arr if process.sort_key == pivot.sort_key]
          right = [process for process in arr if process.sort_key > pivot.sort_key]
    
          # the magic happens here:
          # sort each half and pass through the intermediate results of the "sub-sorters" to our own caller, 
          # after modifying them so they contain the entire array again:
    
          # left half:
          left_final = None
          for left_intermediate in quicksort_with_steps(left):  # we're iterating over the "yield" calls here, not the sorted array.
              yield left_intermediate + middle + right  # pass on the intermediate result to our own caller. 
              left_final = left_intermediate  # the last iteration has the "final" result for the left side, so store it.
        
          assert left_final is not None  # the generator always yields at least one result, so this shouldn't happen.
    
          # right half: (shorter because we don't have to store anything)
          for right_intermediate in quicksort_with_steps(right):
              yield left_final + middle + right_intermediate
    
          # the last "yield" returns the fully sorted array.
    
    Then if you call the function, it will return an iterator over all the steps of the algorithm. You could either put it in a for loop like in the recursive calls, or "manually" advance it one step using python's next() function, e.g. inside a frame callback.

    I'm pretty sure, if you're insane enough, you could also whip up something using async/await where the algorithm literally "awaits" the end of the animation...

  • kettro 72 days ago
    Well done!

    It is exactly the correct approach — especially with the dataset potentially changing at each iteration, treating it as a contiguous sequence of executions is wrong anyways. Plus, you should always strive to reduce state as much as possible.

  • dclowd9901 72 days ago
    It's honestly one of the hardest things for me, trying to explain to more junior developers that clever almost is never better. Does anyone have any good litmus or heuristic for figuring out when something is "too" clever?

    I was thinking of a way to quantify "complexity" in a process by a sort of "reference counting" style metric, where the moment you have to reference some other location to figure something out, you add 1 to a number and if that number gets above some figure, it's too complex.

    • tstrimple 72 days ago
      There's the cyclomatic complexity which I think gets close to your reference counting example.

      https://en.wikipedia.org/wiki/Cyclomatic_complexity

      But I don't think that captures most of the "too clever" stuff I typically see. That's usually some abomination of a one liner that does way too much. Those won't get picked up by cyclomatic complexity measurements. Furthermore, I find cyclomatic complexity tends to come from less experienced developers rather than experienced developers trying to be clever.

      If you're genuinely wondering if something is too clever, ask that junior dev to explain it to you. After all, they will probably be the ones to end up maintaining it later.

    • sgarland 72 days ago
      If I don't understand something I wrote a month later, it was too clever.

      Unfortunately, this is a massively lagging indicator.

      • Arrath 72 days ago
        Yeah that's the textbook example of closing a barn door after the horse gets out, eh?
  • fragmede 72 days ago
    Yay! This game didn't get much traction the first time it got posted so I'm really glad it's getting some more attention.
  • jrochkind1 72 days ago
    This is kind of fun.

    A random nit as we like:

    > I needed to somehow create a function that performs a single step of the algorithm, then gives back control to the main loop (which ironically sounds just like an OS' preemptive scheduling)

    That sounds more like cooperative scheduling, no?

    • drfreckles 72 days ago
      You're right! I rephrased that.
  • syndicatedjelly 72 days ago
    I played this game a few months ago and it was a lot of fun. Nice work :)
  • cratermoon 72 days ago
    If you didn't care about efficiency, why not use bubblesort?
    • drfreckles 72 days ago
      Because the animation needs to pretend to use something more efficient.
      • jandrese 72 days ago
        Ironically the particulars of this implementation make it much less efficient than a dumb bubble sort. Many Quicksort implementations have the worst performance on already sorted data, and in this case it will be run a lot on data that is already mostly sorted. It won't matter here because the data size is always trivially tiny, but it is something to consider in the real world.
        • drfreckles 72 days ago
          Again, the need was not to implement an efficient sort. It was to implement an animation that pretends to execute an efficient sort. Also, quicksort gives a more interesting result visually.
        • cratermoon 72 days ago
          There's also the overhead of quicksort compared to bubblesort, making it a poor choice for tiny sets of data.

          In this case I understand the author's point is to have something visually interesting to show the work being done.

  • leononame 72 days ago
    I don't think it's dumb. After all, the array can change in between renders, so you kind of have to start from scratch anyways. And for a list of <1000, who cares?
  • willhackett 72 days ago
    You know what, this is spot on! And, I'm happy you're proud of it.
  • passion__desire 71 days ago
    Does there exist something similar for human body?
  • mjmdavis 72 days ago
    Honestly this comes across as a smart solution, not a dumb one. You big smartie. Such a smart guy. Geez. You're so smart, you're very productive and are helping advance society. Thank you.
  • ww520 72 days ago
    Actually it’s clever not dumb. It partial evaluates to the first change then stops. It does the job and no more. Very insightful. I’m stealing this idea. :)