Threads for agent281

  1. 2

    Wait, if you can’t compare string values in Dhall what do you do instead?

    1. 3

      If you need to work with a string that can have a finite set of possible values then use an enum type. If the string can be an abritrary value, then you probably ought not compare it.

      1. 1

        presumably you have to convert them to the same string type first? (e.g. if one is raw bytes and the other is decoded, or one is list of 32-bit chars and the other is vector of utf-8 bytes)

      1. 14

        Another option is to use Tailscale to connect to your Raspberry Pi from anywhere. I use that plus Jellyfin as my music server.

        That said, I really think anyone old enough to have a developer job should either pay for/watch the ads for YouTube Music or not use YouTube. Using an ad blocker or whatever to get commercial free YouTube without paying is fine when you’re a broke kid, but doing it as an adult with a job is just entitled behavior. If you don’t think you should have to pay for music, then lobby Congress to repeal the bloated copyright laws, but you still live in a society and can’t just decide you don’t feel like using for a service but not paying for it.

        1. 9

          Wow. The copyright MPAA talking point propaganda has really had an impact, it seems. That’s absolutely insane to me that anyone would think it’s unethical to block advertisements.

          1. 4

            YouTube gives you two different ways to pay for it. Choosing to use the service (which directly contributes to its monopolization of short video!) and then neither watching the ads nor pay the cheap monthly fee is entitled behavior. Using P2P is better because at least you’re not leaching bandwidth.

            1. 3

              Yes, not only that it’s ethical to pay google but that it’s unethical to not pay google. Stick that to your boot and lick it.

              It’s the same logic that says shoplifting from Wal-Mart is wrong even though most theft is wage theft. [1]

              [1] https://inthesetimes.com/article/walmart-corporations-wage-theft-labor-settlements-firms

              1. 3

                Stealing from Wal-Mart if you’re not poor is wrong. It doesn’t make it good if they are also thieves.

            2. 5

              Technically they can decide to use a service and not pay for it, as shown by this post. Personally I am more concerned that the artists don’t get paid than YouTube. Their payment through YouTube is close to not getting paid anyway.

              People have financial situations and alternative philosophies that may provide a different justification besides “entitlement”. In the grand scheme of things, someone copying some music without paying for it is not really undermining our society.

              1. 4

                I’m not an absolutist about it. I just feel like if you’re an adult and you can afford it, it’s entitled to consume without even trying to pay.

                1. 3

                  YouTube literally saved the music industry. The fact that the industry doesn’t pay artists is a different, and older, problem.

                  1. 1

                    YouTube literally saved the music industry.

                    Could you elaborate on this?

                    1. 5

                      For sure. Back in the Napster then *tella etc “piracy” days the music industry was freaking out. Albums would often leak from the plant and be available online before any release had been made and the while radio single, then CD, model was in serious trouble. There was no way to profit from these systems because they were distributed and ever-changing and there was no business to partner with. They tried doing 360s and all that (which they still do today) but it was a serious problem for them to imagine what to do, and hence why they tried the “suing fans” model for awhile, desperate for anything that could work.

                      Then all of that started to get replaced by stuff like YouTube. A centralised repository run by a company that already monetizes the content! The right parts of the industry realised this was amazing for them and threw their support behind this, in exchange for a cut. Singles and other pre releases drop on YouTube before the album and they get paid. These days with contentid they get paid even when their catalog is used in someone else’s video.

                      As with all short summaries the above is missing detail and nuance. The music industry partnering with YouTube with their one hand did not prevent them suing YouTube with the other. Carrot and stick and internal disorganization etc. YouTube was not the only presence in this period but it’s the main one left, etc. But I think this gives a good glimpse into the very real way that YouTube and similar helped end the mass fan lawsuits and provide a very real out for the music industry to make enough money to keep going on their model and give space for new things to emerge.

                      1. 1

                        Interesting! I remember the pirating days and I remember the threat of being sued as a seeder, but it all kind of faded away and I didn’t know what changed. Thanks for explaining!

              1. 1

                It’s looking like some functional languages are being worked on that do opportunistic in place mutation (such as Roc). If they work out they might improve the feasibility of functional programs in the space.

                1. 2

                  I was thinking about this and I think I have a nice minimal example of this behavior:

                  Imagine you have a program where the user controls a point on a 2D grid. The user always starts at (0, 0). They can move left, right, up, or down. This is a very simple program, but after a long enough time line it could start to misbehave. For example, let’s say you represent the x and y values with signed 32 bit integers and overflow in your language is undefined. Most runs will never have any problems, but if someone races to an edge they could trigger undefined behavior on integer overflow. The user can’t tell what’s going on. Maybe it maps to their character in a video game and they teleport to a completely different part of the world. They restart the game, go back to origin and everything is fine.

                  1. 9

                    In Legend of Zelda: Breathe of the Wild, there’s a gameplay mechanic where occasionally the “blood moon” will rise and all your defeated enemies will come back to life. What’s actually happening is the game is resetting to a known good state, but they make it seem relevant in game.

                    1. 1

                      Is that really the intent? If so, that’s amazing! I love how video game developers are able to pull the wool over our eyes. Reminds me of classic Hollywood illusions. Trust nothing!

                      1. 5

                        Wing Commander for DOS would crash after exiting the game, before returning to the DOS prompt, with an error message displayed on the screen. The devs just patched the error string to say “Thanks for playing Wing Commander!”

                    1. 5

                      I wasn’t satisfied with the blog’s markup and JS requirement, so I went to look for the original article, because I hoped it was written in Markdown or something similar. It is not.

                      1. 3

                        ngl this is wild

                        1. 2

                          Oh dear…

                        1. 10

                          Excellent post, I strongly agree with this. One of the reasons I enjoy Elm is its radical simplicity (and, relatedly, its stability). I’ve been using Elixir more recently, and there’s a clear overhead to learning it and using it in comparison.

                          Some of its complexity seems particularly unnecessary - eg there shouldn’t really be special syntax for calling anonymous functions, it’s not great to have different ways to access fields in structs vs maps, and so on. Possibly this highlights the “vertical” aspect of complexity - everything sits on top of a deep stack of other stuff these days. Perhaps the design of Elixir was constrained by the peculiarities of Erlang and OTP.

                          Elixir still takes a commendably conservative approach to features in comparison to other languages like Rust, TypeScript and C++.

                          In general, I feel like there’s an extremely cavalier approach to complexity at all levels of our industry, whether it’s designing PLs or writing software using them. That’s probably why, no matter how much tools and technologies seem to improve (on paper), it still takes a long time to create even an ostensibly simple piece of software.

                          1. 14

                            I’m finding that the most complex part of learning yet another language isn’t the language, it’s the tooling and ecosystem.

                            TypeScript itself was fun and easy … but figuring out that config file the translator uses was annoying, and ditto for TSLint and Jester, and don’t get me started on the seventeen flavors of JS modules. Stuff like Grunt can die in a fire. (I now understand why some folks go nuts for radical simplicity and end up living in a cave running Gopher on their 6502-based retro computer: Node and the web ecosystem drove them to it. So much worse than C++, even with CMake.)

                            1. 3

                              Yes, that is true but I think there’s a compounding effect on complexity which means complexity at all levels is important to minimise. As an example, a more complex language requires more complex tools and more attempts at getting the tools right, which leads to the proliferation you mention. Some features can instead lead to proliferation of libraries (Smart pointers or regular pointers? Which of fifteen flavours of strings? Macros or regular functions? Etc.)

                              Putting languages aside, isn’t it the same kind of disregard for complexity in all other areas (protocols, APIs, libraries etc.) that ultimately results in unwieldy ecosystems?

                              We tend to underestimate how small bits of complexity compound and snowball, I think.

                              1. 3

                                I strongly agree with this. Coming from Clojure on the JVM, I found it much more difficult to work on a project written in ClojureScript (supposedly the “same” language on a different runtime) than it was to learn Mirah (completely different language on the JVM).

                                Of course there are other factors like completely different paradigms that come out of left field (logic programming, category theory, etc) to make things difficult, but other than that, once you’ve got thee or four languages under your belt, learning a new one is much easier than learning a new runtime.

                              2. 4

                                Some of its complexity seems particularly unnecessary - eg there shouldn’t really be special syntax for calling anonymous functions, it’s not great to have different ways to access fields in structs vs maps, and so on. Possibly this highlights the “vertical” aspect of complexity - everything sits on top of a deep stack of other stuff these days. Perhaps the design of Elixir was constrained by the peculiarities of Erlang and OTP.

                                Joe Armstrong discussed this in an article.

                                It’s because there is a bug in Erlang. Modules in Erlang are sequences of FORMS. The Erlang shell evaluates a sequence of EXPRESSIONS. In Erlang FORMS are not EXPRESSIONS.

                                The two are not the same. This bit of silliness has been Erlang forever but we didn’t notice it and we learned to live with it.

                                https://joearms.github.io/published/2013-05-31-a-week-with-elixir.html

                                1. 2

                                  My Erlang is super rusty so I’m a bit fuzzy on this, but it looks to me like “forms” in this context basically means what other languages call statements; is that accurate?

                                  It’s refreshing to see someone admitting their mistakes so openly.

                                2. 2

                                  In general, I feel like there’s an extremely cavalier approach to complexity at all levels of our industry, whether it’s designing PLs or writing software using them.

                                  This has been frustrating me to no end, especially in the last ~4 years. My pet theory is that the industry has been overtaken by a “tech consumerist” mindset, where the impact of incorporating new, shiny “tech products” into your codebase is greatly overestimated, while the cost of the added complexity is underestimated.. Curious to hear if you have a take on how this cavalier attitude became so prevalent.

                                  1. 2

                                    Very few major projects are considered minimalist. The knock on effect of this is that steers the imagination away from minimalism. From a maximalist viewpoint, PLs such as Lisp, Smalltalk, or ML look like toys because there seemingly isn’t enough stuff to learn.

                                    1. 2

                                      It’s often a question of choosing where to put complexity. In Verona, we’d really like to have Smalltalk-like user-defined flow control (though with static typing so that it can all be inlined in an ahead-of-time compiler with no overhead), where a match expression and a function call / lambda, and (checked) exception throwing / catching are the only things in the language and everything else is part of the standard library. This is a great thing for having a minimal language and also means that complex control flow constructs such as a range-based for loop for iterating over every other element of a collection in reverse order are as first-class as while loops.

                                      We pushed quite a long way on this, but in a language that has linear types they add a huge amount of complexity in the type system. For example, consider this trivial case:

                                      var x : iso; // Linear pointer to the entry point of a region.
                                      if (a)
                                      {
                                        doSomethingWith(x);
                                      }
                                      else
                                      {
                                        doSomethingDifferentWith(x);
                                      }
                                      

                                      The two lambdas (braces are used to define zero-argument lambdas in Verona) both need to capture x, but x is a linear type and so they can’t both capture it. There are some details here around the fact that else is actually an infix operator applied to the result of if here that I’m going to ignore for now but even without that, we need to be able to express in the type system that:

                                      • The if...else construct takes two lambda that take zero arguments.
                                      • Each lambda must be callable once.
                                      • We don’t need to be able to call both lambdas together and so it’s fine for calling one to implicitly invalidate the other.
                                      • The construct will definitely call one or the other, and so any properties that are true at the end of both (e.g. that x is still valid and was not consumed) can be assumed for type checking the surrounding block.

                                      This is incredibly complex to make work at all, making it work and be something that mere mortals can use is basically impossible, so we’ve punted on it for our MVP and will build some control-flow constructs into the language.

                                  1. 5

                                    This seems like it’s probably misleading, because it’s measuring the overhead of the high-level data structure and not accounting for how the size in memory will vary according to what you put in there. If you only ever store (Python) int and short strings containing code points from the latin-1 range, it’ll look very different than if you store, say, longer strings containing code points outside latin-1 (will at least double the storage for the string) or more complex types like lists or dicts.

                                    The string storage varies because internally Python always uses a fixed-width encoding, but chooses it per-string as the narrowest one capable of encoding the widest code point in the string — 1, 2, or 4 bytes.

                                    1. 2

                                      The string storage varies because internally Python always uses a fixed-width encoding, but chooses it per-string as the narrowest one capable of encoding the widest code point in the string — 1, 2, or 4 bytes.

                                      I thought that python used utf-8 internally. TIL, I guess.

                                      https://peps.python.org/pep-0393/

                                      1. 2

                                        As the PEP says, Python used to do either 2-byte (with surrogates) or 4-byte internal Unicode and which one was a compile-time flag baked into the interpreter. Then Python 3.3 switched to the dynamic per-string encoding choice used ever since.

                                        The advantages of this are that strings are always fixed-width storage — which is convenient for a lot of interactions with them, both at the Python and underlying C level — but without the inefficiency of UTF-32. There are cases where this makes better use of memory than “UTF-8 always” (since Python can get the first 256 code points in 1-byte encoding while UTF-8 can only get the first 128), and cases where it doesn’t (since a single code point past 256 switches the whole string to a wider encoding).

                                        1. 3

                                          What’s an example of an algorithm where fixed width code points is actually useful?

                                          1. 1

                                            Iterating is a lot easier with fixed width. Indexing is a lot easier with fixed width. Calculating length is a lot easier with fixed width.

                                            I know a lot of people really like to say that you shouldn’t be allowed to do those things to Unicode, but there are enough real-world use cases which require them that there’s no reason not to have them be that little bit nicer.

                                            Also it avoids the issue of how to expose variable-width storage to the programmer; in the old days before PEP 393, a “narrow” (2-byte Unicode) build of Python would leak lone surrogates up to the programmer. But if you’re going to normalize them to avoid that you have a complex API design problem of how to achieve that. Currently Python strings are iterables of code points, not of bytes or code units, which is a cleaner abstraction all around.

                                        2. 1

                                          Does any language use utf-8 internally? That seems like a very slow choice if you are doing a lot of string manipulation.

                                          1. 1

                                            Swift does for most purposes. If that is too slow, you are probably not dealing with proper strings.

                                            1. 1

                                              It doesn’t seem like it’s actually storing anything as UTF-8 in memory, but rather a collection of Character objects, which to me seems to be a subclass of an integer by reading the documentation. That is pretty much the OOP way of implementing strings as far as I can tell.

                                              1. 1

                                                Storage is UTF-8 according to https://www.swift.org/blog/utf8-string/

                                                A Character is a Unicode grapheme cluster and is realised on a much higher level than pure storage.

                                        3. 2

                                          Also, I don’t know if it’s still the case, but I know that it at least used to be true that Python dict aggressively resizes to keep the hash table sparse, so even if you use the lowest-memory-overhead container you can find, putting even a single dict inside that container is likely to wipe out all your careful micro-optimizing choices.

                                        1. 1

                                          I‘m amazed about the advertised performance and overall architecture, but a little afraid that they written roughly 20% of their stack in assembly. Is this maintainable?

                                          1. 1

                                            Or portable?

                                            1. 2

                                              We are planning on supporting more architectures, most notably ARM SVE2 (hopefully within months) and potentially RISC-V as well. Golang actually makes it easy to integrate multiple architectures in a single code base, so we’ll take advantage of that.

                                            2. 1

                                              The low level primitives (about 250 right now) are all tested very extensively individually as separate building blocks. There is a simple “calling convention” (all based on registers, both Z for data and K for control). Effectively we are stichting them together as the result of parsing the SQL statement and this then become the “bytecode” program that executes the query. We will have a blog out shortly describing this in more detail. (disclaimer: Sneller founder here).

                                            1. 1

                                              Personally, I found Reginald Braithwaite’s video on Javascript combinators very influential. I appreciate how you can build up a little DSL with tiny, orthogonal functions. I just wish more languages could optimize across function calls better. I occasionally go back to regain inspiration.

                                              https://youtu.be/3t75HPU2c44

                                              1. 5

                                                The combinators style of programming comes from functional programming. You’ll find that production-ready implementations of functional language implementations (Haskell, Scheme, OCaml) are typically better optimized for this style.

                                                1. 1

                                                  Yeah, it’s just unfortunate that the languages I use for work don’t really support the style that I enjoy.

                                              1. 12

                                                The lesson here sounds more like “bad protocols will make your client/server system slow and clumsy”, not “move all of your system’s code to the server.” The OP even acknowledges that GraphQL would have helped a lot. (Or alternatively something like CouchDB’s map/reduce query API.)

                                                I don’t really get the desire to avoid doing work on the client side. Your system includes a lot of generally-quite-fast CPUs provided for free by users, and the number of these scales 1::1 with the number of users. Why not offload work onto them from your limited and costly servers? Obviously you’re already using them for rendering, but you can move a lot of app logic there too.

                                                I’m guessing that the importance of network protocol/API design has been underappreciated by web devs. REST is great architecturally but if you use it as a cookie-cutter approach it’s non-optimal for app use. GraphQL seems a big improvement.

                                                1. 17

                                                  Your system includes a lot of generally-quite-fast CPUs provided for free by users

                                                  Yes, and if every site I’m visiting assumes that, then pretty quickly, I no longer have quite-fast CPUs to provide for free, as my laptop is slowly turning to slag due to the heat.

                                                  1. 8

                                                    Um, no. How many pages are you rendering simultaneously?

                                                    1. 3

                                                      I usually have over 100 tabs open at any one time, so a lot.

                                                      1. 5

                                                        If your browser actually keeps all those tabs live and running, and those pages are using CPU cycles while idling in the background and the browser doesn’t throttle them, I can’t help you… ¯\_(ツ)_/¯

                                                        (Me, I use Safari.)

                                                        1. 3

                                                          Yes, but assuming three monitors you likely have three, four windows open. That’s four active tabs, Chrome put the rest of them to sleep.

                                                          And even if you only use apps like the one from the article, and not the well-developed ones like the comment above suggests, it’s maybe five of them at the same time. And you’re probably not clicking frantically all over them at once.

                                                          1. 2

                                                            All I know is that when my computer slows to a crawl the fix that usually works is to go through and close a bunch of Firefox tabs and windows.

                                                            1. 4

                                                              There is often one specific tab which for some reason is doing background work and ends up eating a lot of resources. When I find that one tab and close it my system goes back to normal. Like @zladuric says, browsers these days don’t let inactive tabs munch resources.

                                                    2. 8

                                                      I don’t really get the desire to avoid doing work on the client side.

                                                      My understanding is that it’s the desire to avoid some work entirely. If you chop up the processing so that the client can do part of it, that carries its own overhead. How do you feel about this list?

                                                      Building a page server-side:

                                                      • Server: Receive page request
                                                      • Server: Query db
                                                      • Server: Render template
                                                      • Server: Send page
                                                      • Client: Receive page, render HTML

                                                      Building a page client-side:

                                                      • Server: Receive page request
                                                      • Server: Send page (assuming JS is in-page. If it isn’t, add ‘client requests & server sends the JS’ to this list.)
                                                      • Client: Receive page, render HTML (skeleton), interpret JS
                                                      • Client: Request data
                                                      • Server: Receive data request, query db
                                                      • Server: Serialize data (usu. to JSON)
                                                      • Server: Send data
                                                      • Client: Receive data, deserialize data
                                                      • Client: Build HTML
                                                      • Client: Render HTML (content)

                                                      Compare the paper Scalabiilty! But at what COST!, which found that the overhead of many parallel processing systems gave them a high “Configuration to Outperform Single Thread”.

                                                      1. 4

                                                        That’s an accurate list… for the first load! One attraction of doing a lot more client-side is that after the first load, the server had the same list of actions for everything you might want to do, while the client side looks more like:

                                                        • fetch some data
                                                        • deserialize it
                                                        • do an in-place rerender, often much smaller than a full page load

                                                        (Edit: on rereading your post your summary actually covers all requests, but missed how the request and response and client-side rerender can be much smaller this way. But credit where due!)

                                                        That’s not even getting at how much easier it is to do slick transitions or to maintain application state correctly across page transitions. Client side JS state management takes a lot of crap and people claim solutions like these are simpler but… in practice many of the sites which use them have very annoying client side state weirdness because it’s actually hard to keep things in sync unless you do the full page reload. (Looking at you, GitHub.)

                                                        1. 6

                                                          When I’m browsing on mobile devices I rarely spend enough time on any single site for the performance benefits of a heavy initial load to kick in.

                                                          Most of my visits are one page long - so I often end up loading heavy SPAs when a lighter, single page optimized to load fast from an un sched blank state would have served me much better.

                                                          1. 4

                                                            I would acknowledge that this is possible.

                                                            But that’s almost exactly what the top comment said. People use framework of the day for a blog. Not flattening it, or remixing it or whatever.

                                                            SPAs that I use are things like Twitter, the tab is likely always there. (And on desktop i have those CPU cores.)

                                                            It’s like saying, I only ride on trains to work, and they’re always crowded, so trains are bad. Don’t use trains if your work is 10 minutes away.

                                                            But as said, I acknowledge that people are building apps where they should be building sites. And we suffer as the result.

                                                            What still irks me the most are sites with a ton of JavaScript. So it’s server-rendered, it just has a bunch of client-side JavaScript that’s unused, or loading images or ads or something.

                                                        2. 4

                                                          You’re ignoring a bunch of constant factors. The amount of rendering to create a small change on the page is vastly smaller than that to render a whole new page. The most optimal approach is to send only the necessary data over the network to create an incremental change. That’s how native client/server apps work.

                                                          1. 5

                                                            In theory yes but if in practice if the “most optimal approach” requires megabytes of JS code to be transmitted, parsed, executed through 4 levels of interpreters culminating in JIT compiling the code to native machine code all the while performing millions of checks to make sure that this complex system doesn’t result in a weird machine that can take over your computer then maybe sending a “whole new page” consisting of 200 kb of static HTML upon submitting a form would be more optimal.

                                                            1. 4

                                                              In theory yes but if in practice if the “most optimal approach” requires megabytes of JS code to be transmitted, parsed, executed through 4 levels of interpreters culminating in JIT compiling the code to native machine code all the while performing millions of checks to make sure that this complex system doesn’t result in a weird machine that can take over your computer

                                                              This is hyperbole. Sending a ‘“whole new page” of 200 kb of static HTML’ has your userspace program block on the kernel as bytes are written into some socket buffer, NIC interrupts the OS to grab these bytes, the NIC generates packets containing the data, userspace control is then handed back to the app which waits until the OS notifies it that there’s data to read, and on and on. I can do this for anything on a non-embedded computer made in the last decade.

                                                              Going into detail for dramatic effect doesn’t engage with the original argument nor does it elucidate the situation. Client-side rendering makes you pay a one-time cost for consuming more CPU time and potentially more network bandwidth for less incremental CPU and bandwidth. That’s all. Making the tradeoff wisely is what matters. If I’m loading a huge Reddit or HN thread for example, it might make more sense to load some JS on the page and have it adaptively load comments as I scroll or request more content. I’ve fetched large threads on these sites from their APIs before and they can get as large as 3-4 MB when rendered as a static HTML page. Grab four of these threads and you’re looking at 12-16 MB. If I can pay a bit more on page load then I can end up transiting a lot less bandwidth through adaptive content fetching.

                                                              If, on the other hand, I’m viewing a small thread with a few comments, then there’s no point paying that cost. Weighing this tradeoff is key. On a mostly-text blog where you’re generating kB of content, client-side rendering is probably silly and adds more complexity, CPU, and bandwidth for little gain. If I’m viewing a Jupyter-style notebook with many plots, it probably makes more sense for me to be able to choose which pieces of content I fetch to not fetch multiple MB of content. Most cases will probably fit between these two.

                                                              Exploring the tradeoffs in this space (full React-style SPA, HTMX, full SSR) can help you come to a clean solution for your usecase.

                                                              1. 1

                                                                I was talking about the additional overhead required to achieve “sending only the necessary data over the network”.

                                                        3. 4

                                                          I don’t really get the desire to avoid doing work on the client side.

                                                          My impression is that it is largely (1) to avoid JavaScript ecosystem and/or* (2) avoid splitting app logic in half/duplicating app logic. Ultimately, your validation needs to exist on the server too because you can’t trust clients. As a rule of thumb, SSR then makes more sense when you have lower interactivity and not much more logic than validation. CSR makes sense when you have high interactivity and substantial app logic beyond validation.

                                                          But I’m a thoroughly backend guy so take everything that I say with a grain of salt.


                                                          Edit: added a /or. Thought about making the change right after I posted the comment, but was lazy.

                                                          1. 8

                                                            (2) avoid splitting app logic in half/duplicating app logic.

                                                            This is a really the core issue.

                                                            For a small team, a SPA increases the amount of work because you have a backend with whatever models and then the frontend has to connect to that backend and redo the models in a way that makes sense to it. GraphQL is an attempt to cut down on how much work this is, but it’s always going to be some amount of work compared to just creating a context dictionary in your controller that you pass to the HTML renderer.

                                                            However, for a team that is big enough to have separate frontend and backend teams, using a SPA decreases the amount of communication necessary between the frontend and backend teams (especially if using GraphQL), so even though there’s more work overall, it can be done at a higher throughput since there’s less stalling during cross team communication.

                                                            There’s a problem with MPAs that they end up duplicating logic if something can be done either on the frontend or the backend (say you’ve got some element that can either be loaded upfront or dynamically, and you need templates to cover both scenarios). If the site is mostly static (a “page”) then the duplication cost might be fairly low, but if the page is mostly dynamic (an “app”), the duplication cost can be huge. The next generation of MPAs try to solve the duplication problem by using websockets to send the rendered partials over the wire as HTML, but this has the problem that you have to talk to the server to do anything, and that round trip isn’t free.

                                                            The next generation of JS frameworks are trying to reduce the amount of duplication necessary to write code that works on either the backend or the frontend, but I’m not sure they’ve cracked the nut yet.

                                                            1. 4

                                                              For a small team, a SPA increases the amount of work because you have a backend with whatever models and then the frontend has to connect to that backend and redo the models in a way that makes sense to it

                                                              Whether this is true depends on whether the web app is a client for your service or the client for your service. The big advantage of the split architecture is that it gives you a UI-agnostic web service where your web app is a single front end for that service.

                                                              If you never anticipate needing to provide any non-web clients to your service then this abstraction has a cost but little benefit. If you are a small team with short timelines that doesn’t need other clients for the service yet then it is cost now for benefit later, where the cost may end up being larger than the cost of refactoring to add abstractions later once the design is more stable.

                                                              1. 1

                                                                If you have an app and a website as a small team, lol, why do you hate yourself?

                                                                1. 4

                                                                  The second client might not be an app, it may be some other service that is consuming your API.

                                                            2. 4

                                                              (2) avoid splitting app logic in half/duplicating app logic.

                                                              The other thing is to avoid duplicating application state. I’m also a thoroughly a backend guy, but I’m led to understand that the difficulty of maintaining client-side application state was what led to the huge proliferation of SPA frameworks. But maintaining server-side application state is easy, and if you’re doing a pure server-side app, you expose state to the client through hypertext (HATEOAS). What these low-JS frameworks do is let you keep that principle — that the server state is always delivered to the client as hypertext — while providing more interactivity than a traditional server-side app.

                                                              (I agree that there are use-cases where a more thoroughly client-side implementation is needed, like games or graphics editors, or what have you.)

                                                              1. 1

                                                                Well, there’s a difference between controller-level validation and model-level validation. One is about not fucking up by sending invalid data, the other is about not fucking up by receiving invalid data. Both are important.

                                                              2. 4

                                                                Spot on.

                                                                this turns out to be tens (sometimes hundreds!) of requests because the general API is very normalized (yes we were discussing GraphQL at this point)

                                                                There’s nothing about REST I’ve ever heard of that says that resources have to be represented as separate, highly normalized SQL records, just as GraphQL is not uniquely qualified to stitch together multiple database records into the same JSON objects. GraphQL is great at other things like allowing clients to cherry-pick a single query that returns a lot of data, but even that requires that the resolver be optimized so that it doesn’t have to join or query tables for data that wasn’t requested.

                                                                The conclusion, which can be summed up as, “Shell art is over,” is an overgeneralized aesthetic statement that doesn’t follow from the premises. Even if the trade-offs between design choices were weighed fully (which they weren’t), a fundamentally flawed implementation of one makes it a straw man argument.

                                                                1. 1

                                                                  The Twitter app used to lag like hell on my old Thinkpad T450. At the very least, it’d kick my fan into overdrive.

                                                                  1. 1

                                                                    Yay for badly written apps :-p

                                                                    Safari will notice when a page in the background is hogging the CPU, and either throttle or pause it after a while. It puts up a modal dialog on the tab telling you and letting you resume it. Hopefully it sends an email to the developer too (ha!)

                                                                    1. 1

                                                                      It wound spin up on load, not in the background, because loading all that JS and initializing the page is what would cause the CPU usage. And then after I closed the page, 20 seconds later someone else would send me another Twitter link and I’d get to hear the jet engine again.

                                                                1. 6

                                                                  Host here. Before interviewing Ron I would have never thought they would just leave a REPL running on a space craft!

                                                                  1. 3

                                                                    Looking forward to this episode!

                                                                    The space + homoiconic language combination makes me want an episode with Chuck Moore. That could be an interview for the ages!

                                                                    https://en.m.wikipedia.org/wiki/Charles_H._Moore

                                                                    Edit: actually I think the history of Forth goes back further than the Wikipedia article states.

                                                                    https://www.forth.com/resources/forth-programming-language/

                                                                    In 1965, he moved to New York City to become a free-lance programmer. Working in Fortran, Algol, Jovial, PL/I and various assemblers, he continued to use his interpreter as much as possible, literally carrying around his card deck and recoding it as necessary.

                                                                    Weird to think of carrying around your programming language in a briefcase!

                                                                    1. 1

                                                                      I have been wondering about how to do a forth episode. Is Chuck Moore still active in any capacity?

                                                                      1. 1

                                                                        Maybe? He has talked with some forth groups recently (https://m.youtube.com/watch?v=dI0soDMg28Q&t=357s).

                                                                        1. 1

                                                                          It looks like there are a few interviews in the past couple years, but he is getting up there in years. I think part of the reason why he wrote color forth to use text color semantically was because his eyes weren’t in great shape and that was the late 90s, early 00s.


                                                                          Edit:

                                                                          Great moment from an interview from a couple years ago:

                                                                          https://www.youtube.com/watch?v=Qq0d6k7oi_A&t=3356s

                                                                    1. 23

                                                                      For the uninitiated: Style insensitivity is a unique feature of the Nim programming language that allows a code base to follow a consistent naming style, even if its dependencies use a different style. It works by comparing identifiers in a case-insensitive way, except for the first character, and ignoring underscores.

                                                                      Another advantage of style insensitivity is that identifiers such as itemId, itemID or item_ID can never refer to different things, which prevents certain kinds of bad code. An exception is made for the first letter to allow the common convention of having a value foo of type Foo.

                                                                      There’s a common misconception that this feature causes Nim programmers to mix different styles in a single codebase (which, as mentioned, is precisely the opposite of what it does), and it gets brought up every time Nim is mentioned on Lobsters/HackerNews/etc, diverting the discussion from more valuable topics.

                                                                      1. 3

                                                                        There’s a common misconception that this feature causes Nim programmers to mix different styles in a single codebase (which, as mentioned, is precisely the opposite of what it does)

                                                                        But… isn’t it exactly what the feature does? If my coding habits would make me write itemId and a coworker’s code habits would make them write item_id, style insensitivity makes it likely that I would accidentally use a different style than my coworker for the same variable in the same codebase, right? While most languages would make this impossible by making item_id be a different name than itemId, right?

                                                                        How is this a misconception?

                                                                        To be clear, I’m not saying it’s a huge deal or that it warrants all the attention it’s getting (that’s a different discussion), but since you brought it up…

                                                                        1. 3

                                                                          Thanks for providing some context. Is this a thing that gets applied by default any time you use any library, or a feature you can specifically invoke at the point during which the library is imported?

                                                                          The former seems … real bad. The latter seems … kinda neat? but a bit silly.

                                                                          1. 6

                                                                            Currently it’s always on, and there’s an opt-in compile flag --styleCheck:error that makes it impossible to use an identifier inconsistently within the same file. The linked issue discusses if and how this behavior should be changed in Nim 2.

                                                                            Personally, I wouldn’t mind if it was removed, as long as:

                                                                            • --styleCheck:error was on by default
                                                                            • there was a mechanism to restyle identifiers when importing a library.
                                                                            1. 2

                                                                              I agree. People outside the Nim community can add real value to this discussion, since it is just speculation what they really think based on a few loud complainers.

                                                                              1. 5

                                                                                I’m someone who looked at Nim, really liked it, then saw the “style insensitivity” and thought “this isn’t for me”. (Not co-incidentally, I’ve been involved in a major, CEOs-gettting-involved, fiasco that was ultimately due to SQL case-insensitivity.)

                                                                                Nim occupies a nice space - compiled but relatively “high level” - with only really Go as a competitor (zig/rust/c++ all seem a little too low level.) I personally recoil at the idea of “style insensitivity”, but hopefully in a friendly, lobste.rs manner.

                                                                                1. 5

                                                                                  I’ve been involved in a major, CEOs-gettting-involved, fiasco that was ultimately due to SQL case-insensitivity.

                                                                                  You can’t just say this and leave us hanging 😆 tell us the story! How did that cause a fiasco?

                                                                                  1. 5

                                                                                    Our software wouldn’t start for one customer (a large bank). The problem was unreproducible and had been going on for weeks. The customer was understandably very unhappy.

                                                                                    The ultimate cause was a “does the database version match the code” check. The database had a typical version table that looked like:

                                                                                    CREATE TABLE DB_STATE (VERSION INT, ...);
                                                                                    

                                                                                    Which was checked at startup using something like select version from db_state. This was failing for the customer because in Turkish, the upper case of “version” is “VERSİON” (look closely). Case-insensitivity is language-specific and the customer had installed the Turkish version of SQL Server.

                                                                                    Some java to demonstrate:

                                                                                    public class Turkish {
                                                                                      public static void main(String[] args) {
                                                                                        System.out.println("version".toUpperCase(java.util.Locale.forLanguageTag("tr")));
                                                                                      }
                                                                                    }
                                                                                    

                                                                                    If you look at the java documentation for toUpper, they specifically mention Turkish - others have been bit by this same issue, I’m sure.

                                                                                    Which makes me wonder - how does Nim behave if compiled on a machine in Turkey, or Germany.

                                                                                    1. 6

                                                                                      This is making me wonder if anyone has ever used this as a stack-smashing attack. Find some C code that uppercases the input in place and send a bunch of “ß”s. Did the programmer ensure the buffer is big enough?

                                                                                      1. 3

                                                                                        I’m pretty sure Nim’s style insensitivity is not locale-specific. That would be very dumb.

                                                                                    2. 3

                                                                                      There’s also crystal, but ii is failing to reach critical mass in my opinion.

                                                                                      I think crystal did a great job providing things people usually want upfront. I want a quick and direct way to make an HTTP request. I want to extract na value from a JSON string with no fuss. I want to expose functionality via CLI or a web interface with minimal effort. Crystal got these right.

                                                                                      I agree that above-mentioned languages are too low level.

                                                                                      1. 1

                                                                                        Not sure about the others, but I think exposing functionality via CLI is pretty easy in Nim. cligen is not in the Nim stdlib, though.

                                                                                        1. 1

                                                                                          I was not comparing to Nim directly. Just giving examples of the kind of thing I believe are the strongest drives to success of a language.

                                                                                          But for an example of one thing that I found lacking in Nim was concurrency primitives. Crystal makes it relatively simple and direct with fairly simple and familiar fibre API.

                                                                                          A quick way to spin up an HTTP service was another one. It even had support for websockets.

                                                                                      2. 2

                                                                                        Seems plenty friendly to me. Programmers can get awfully passionate about style guides/identifier conventions.

                                                                                        I think it tracks individual, personal history a lot - what confusions someone had, what editor/tool support they had for what sort of “what meta-kind of token is this?” questions and so on. In extreme cases it can lead to, e.g. Hungarian notation like moves. It can even be influenced by font choices.

                                                                                        Vaguely related to case insensitivity, early on, Nim only used '\l' to denote a newline character..Not sure why it was not '\n' from the start, but because lower case “l” (elle) and the numeral “1” often look so similar, '\L' was the dominant suggestion and all special character literals were case insensitive. (Well, one could argue it was just tracking general insensitivity that made them insensitive…).

                                                                                        1. 2

                                                                                          Personally I’d say this compares unfavorably to go, where style arguments are solved by the language shipping a single blessed formatting style.

                                                                                          Confusions/disagreements over formatting style are - imo - a waste of the teams engineering time, so I see the go approach as inherently better.

                                                                                          1. 2

                                                                                            Go doesn’t enforce a style for identifiers. Try it online!

                                                                                            1. 3

                                                                                              Thanks, I hate it.

                                                                                              Fair point to nim though!

                                                                                  2. 4

                                                                                    It is always on - even for keywords like f_Or in a loop. I was trying to perhaps help guide the group towards a usage like you articulate.

                                                                                    EDIT: The main use case cited is always “use a library with an alien convention according to your own convention”. So, being more precise and explicit about this as an import-time construct would seem to be less feather ruffling (IMO).

                                                                                  3. 3

                                                                                    Just for the record - the Z shell (Zsh) had style insensitivity for its setopt builtin waaay back in the very early 90s. They did not make the first letter sensitive, though. :-)

                                                                                    As this seems to be a very divisive issue and part of what is divisive is knowing how those outside the community (who do not love/have not made peace with the feature) feel, it might be helpful if Lobster people could weigh in.

                                                                                    1. 2

                                                                                      As this seems to be a very divisive issue and part of what is divisive is knowing how those outside the community (who do not love/have not made peace with the feature) feel, it might be helpful if Lobster people could weigh in.

                                                                                      I looked into Nim and was at least partially dissuaded by style insensitivity. I don’t think it’s fatal persay, but it did hit me very early in my evaluation. I would liken it to the dot calls in Elixir: Something that feels wrong and makes you question other decisions in the language. That said, Elixir is a fabulous language and I powered through. I imagine others feel similarly.

                                                                                      1. 2

                                                                                        What specifically do you not like about style insensitivity?

                                                                                        1. 1

                                                                                          Here’s the thing: I haven’t used style insensitivity so I can’t really say I dislike it. However, it struck me as unnecessarily inconsistent. I don’t care about snake case or camel case. I just want code to be consistent. Of course, code I write can be consistent with style insensitivity, but code I read probably won’t be.

                                                                                          Additionally, I imagined that working in a team could have issues: repos use the original author’s preferred styling. Of course, having a clear style guide helps, but in small teams sometimes people are intractable and resistant to change. In a way, it triggers a feeling of exasperation: a memory of all the stupid little arguments you have with other developers.

                                                                                          So here I am kicking the tires on a new exciting language and I am already thinking about arguing with people. Kind of takes the wind out of your sails. It may be a great feature, but I imagine it’s a barrier to adoption for some neurotic types like myself. (Maybe that’s a blessing?)

                                                                                          1. 1

                                                                                            You have it the other way around. With style insensitivity, code is much more likely to follow a consistent style — because it can’t be forced into inconsistency by using libraries from different authors.

                                                                                            1. 1

                                                                                              I can see how code I write is consistent, but code I read is going to be more inconsistent. If it wasn’t then why would we need style insensitivity in the first place?

                                                                                              1. 1

                                                                                                I can see how code I write is consistent, but code I read is going to be more inconsistent.

                                                                                                Can you show me a serious Nim project that uses an inconsistent style?

                                                                                                If it wasn’t then why would we need style insensitivity in the first place?

                                                                                                Because libraries you’re using may be written in different styles.

                                                                                                1. 1

                                                                                                  I’m saying it’s inconsistent across projects. Not within projects. Sometimes you have to read other people’s code. Style insensitivity allows/encourages people to pick the nondefault style.

                                                                                                  Ultimately, I’m not in the nim ecosystem. I posted my comment about why style insensitivity made me less interested in nim. I can tell you that this is the exact sort of argument I was looking to avoid so you have proven my initial concerns correct.

                                                                                                  1. 1

                                                                                                    I don’t see what the problem is with reading code written in a different style, as long as it’s consistent. And in practice, most Nim projects follow NEP1.

                                                                                    2. 2

                                                                                      Doesn’t JRuby have something similar around Java native methods?

                                                                                      1. 2

                                                                                        I think it’s an interesting comparison, but it’s important to keep in mind that a language based around message passing is fundamentally different from what’s going on here where the compiler itself is collapsing a bunch of different identifiers during compile-time. When you call a method in Ruby, you’re not really supposed to care how it’s resolved, but when you make a call in a language like Nim, you expect it to be resolved at compile-time.

                                                                                        1. 3

                                                                                          I’d disagree there. The mechanism in JRuby is that the method is made available under multiple names to the application after it’s loaded. That’s not extremely different from what Nim does, except if we go down to the level to say we can’t compare languages with different runtime and module loading models.

                                                                                          https://github.com/jruby/jruby/wiki/CallingJavaFromJRuby

                                                                                          1. 2

                                                                                            I guess what I meant was that even if the implementation works the same way, Rubyists fundamentally have different expectations around what happens when you call foo.bar(); they’ve already given up on greppability for other reasons.

                                                                                    1. 9

                                                                                      I take this blog post as a response to personal criticism. He wrote something a couple years ago, it resurfaced, people say he doesn’t know what he’s talking about and he writes a new blog post to show he knows what he is talking about. It’s got some moments that go a little far (e.g. saying go was not designed) and it’s got some interesting criticism (e.g. the behavior of nil channels).

                                                                                      It’s important to remember that being in the public eye can be stressful and sometimes people lash out under stress.


                                                                                      Edit: Actually these tweets are pretty inflammatory.

                                                                                      1. 4

                                                                                        I’ve never been a fan of printf or f strings or any string substitution stuff like this. I can’t possibly be the only person who reads this:

                                                                                        print(f"{f'${number:.3f}':>10s}")
                                                                                        

                                                                                        And thinks it is completely, unfathombly unreadable, am I?

                                                                                        Is that really preferable to something less magical that takes more lines of code?

                                                                                        1. 9

                                                                                          I really like f-strings, but I would not write that. I’d rather break that out into a couple lines.

                                                                                          1. 3

                                                                                            I prefer format strings because it makes it easier to “see” what the string is going to look like, as opposed to a bunch of concatenation, probably spread across lines. That being said, you’ve got some weird nested stuff going on there that I probably wouldn’t use, so maybe I partially agree?

                                                                                            1. 2

                                                                                              You can use f-strings to write readable things as well.

                                                                                              logger.info(f"Folder for {show.name} is missing, creating...")
                                                                                              

                                                                                              Instead of

                                                                                              logger.info(f"Folder for " + show.name + "is missing, 
                                                                                              creating...)"
                                                                                              

                                                                                              The latter example is not valid Python but you get the point.

                                                                                              1. 4

                                                                                                Nitpick: You should probably not use fstrings for logging, since I believe they’re interpolated every time, even if your log level is below that.

                                                                                                1. 3

                                                                                                  Ah, thanks! I hadn’t thought of that.

                                                                                                  1. 2

                                                                                                    Wouldn’t the concatenation also happen every time, though? Logging functions are just regular functions, so their arguments are evaluated before the function is called, right?

                                                                                                    1. 1

                                                                                                      Yes. Here’s how to avoid unnecessary interpolation:

                                                                                                      logger.info("Folder for %s is missing, creating...", show.name)
                                                                                                      

                                                                                                      Most linters will warn about this.

                                                                                                      1. 2

                                                                                                        I’d forgotten all about that! Haven’t used Python logging in a bit :-) Thanks!

                                                                                              1. 11

                                                                                                Oh, I have quite a few!

                                                                                                For shell aliases and functions:

                                                                                                vg: Shell function to open grep results directly in Vim using the quickfix. A bit of expounding here, in a small blog post.

                                                                                                • rg foo (with ripgrep) to simply view results
                                                                                                • vg foo to use the results as a jumping point for editing/exploring in Vim.

                                                                                                A ton of aliases to shorten frequently used commands. Few examples:

                                                                                                • When I want to sync a Git repo, I run gf to fetch, glf to review the fetched commits, then gm to merge. git pull if I’m lazy and want to automatically merge without reviewing
                                                                                                • glp to review new local commits before pushing with gp. Both glf (“git log for fetch”) and glp (“git log for push”) are convenient because my shell prompt shows me when I’m ahead or behind a remote branch: https://files.emnace.org/Photos/git-prompt.png
                                                                                                • tl to list tmux sessions, then ta session to attach to one. I name tmux sessions with different first letters if I can help it, so instead of doing ta org or ta config, I can be as short as ta o and ta c

                                                                                                Also, “aliases” for Git operations that I relegate to Fugitive. Technically these are shell functions, but they exist mostly just to shorten frequently used commands.

                                                                                                • Instead of gs for git status, I do vs and open the interactive status screen from Fugitive (which after a recent-ish update a few years ago, is very Magit-like, if you’re more familiar).
                                                                                                • When I’m faced with a merge conflict, I do vm to immediately open Vim targeting all the merge conflicts. The quickfix is populated, and I jump across conflicts with [n and ]n thanks to a reduced version of vim-unimpaired.

                                                                                                For scripts:

                                                                                                First off, an easy way to manage my PATH scripts: binify my scripts so they go into PATH, binedit if I need to make a quick edit.

                                                                                                ez: Probably my favorite one. A script to run FZF, fuzzy-find file names, and open my editor for those files. Robust against whitespaces and other special characters. I also have a short blog post expounding on it.

                                                                                                watchrun: A convenience command to watch paths with inotifywait and run a command for each changed file. For example, watchrun src -- ctags -a to incrementally update a tags file.

                                                                                                notify-exit: Run a command and shoot off a libnotify notification if it finishes (whether with a successful exit code or not). I have it aliased to n for brevity (using a symlink). For example, n yarn build to kick off a long-running build and be notified when it’s done.

                                                                                                • Also, a remote counterpart rnotify-exit, which I have aliased to rn (using a symlink). For example, rn ian@hostname yarn build on a remote machine (within my LAN) to kick off a build, and have it still notify on my laptop.

                                                                                                And a slew of scripts that are a bit more integrated with tools I use, e.g.:

                                                                                                I normally keep these in fixed locations, so everything I’ve accrued naturally over the years should be here:

                                                                                                1. 4

                                                                                                  I used to have a ton of these git shortcuts but at some point I threw them out again because I kept forgetting them. The only thing I use daily is git up which is git pull --rebase.

                                                                                                  1. 1

                                                                                                    I had a similar problem and only kept branch-history which showed me the one line commit diff between my feature branch and dev.

                                                                                                  2. 2

                                                                                                    notify-exit: that’s one of those ideas that is so great you facepalm and ask yourself why you never thought of it before. I’m adding this to my config tomorrow.

                                                                                                    1. 1

                                                                                                      ez, is great, thanks for sharing!

                                                                                                    1. 2

                                                                                                      PEP 654 … introduces ExceptionGroups. With them, we’re able to raise multiple exceptions simultaneously

                                                                                                      I’m not familiar with the pattern of raising multiple exceptions. What other languages support this? I’m interested to see where and how they become useful.

                                                                                                      try:
                                                                                                          raise ExceptionGroup("Validation Errors", (
                                                                                                              ValueError("Input is not a valid user name."),
                                                                                                              TypeError("Input is not a valid date."),
                                                                                                              KeyError("Could not find associated project.")
                                                                                                          ))
                                                                                                      except* (ValueError, TypeError) as exception_group:
                                                                                                          ...
                                                                                                      except* KeyError as exception_group:
                                                                                                          ...
                                                                                                      
                                                                                                      1. 4

                                                                                                        The ExceptionGroups has primarily been introduced to support proper error handling in the asyncio TaskGroups

                                                                                                        1. 1

                                                                                                          That’s interesting. In Go, I have a multierror that I use to consolidate errors when running concurrent gorountines. Of course, errors are just a normal interface in Go, so it’s pretty easy to just add an Error method to a slice of errors. It doesn’t need any special language support.

                                                                                                          I wrote a gist to try to combine Python exceptions once, but it was sort of a kludge. It’s nice that it will be a first class thing now.

                                                                                                          1. 1

                                                                                                            That’s basically what asyncio.gather() already does, but PEP 654 exists to make the process less cumbersome.

                                                                                                        2. 3

                                                                                                          It’s not built-in, but if I’m understanding the feature correctly it wouldn’t be difficult to do with Common Lisp conditions.

                                                                                                          1. 1

                                                                                                            This reminds me a bit of pydantic: if you have errors in your model it should give you multiple validation errors instead of blowing up on the first one. Maybe it would also be useful in that context?

                                                                                                            1. 4

                                                                                                              I don’t think it’s for that use case. Within a single library, you can do:

                                                                                                              from typing import List
                                                                                                              
                                                                                                              class ValidationError:
                                                                                                                  line_number: int
                                                                                                                  reason: str
                                                                                                              
                                                                                                              class ValidationErrors(Exception):
                                                                                                                  errors : List[ValidationError]    
                                                                                                              

                                                                                                              So based on above discussion it sounds like it’s more about combinations of multiple different exceptions.

                                                                                                              1. 2

                                                                                                                Django’s forms library has something similar; instead of forms having a potential attached error, they have a a potential list of attached errors, produced by catching and collecting every django.forms.ValidationError raised during the validation process.

                                                                                                                1. 1

                                                                                                                  Seeing as we’re talking about new features in recent versions of Python, you don’t need from typing import List anymore, since 3.9 you can just write:

                                                                                                                  class ValidationErrors(Exception):
                                                                                                                      errors: list[ValidationError]
                                                                                                                  
                                                                                                                  1. 1

                                                                                                                    Good to know! And I guess I actually have one project where I can use Python 3.9 features, but everything else only just dropped 3.6. Going to be a while…

                                                                                                                    1. 1

                                                                                                                      TIL that you can do from __future__ import annotations as of Python 3.7 for this.

                                                                                                              1. 4

                                                                                                                This is a fine explanation of baby-free-monads and how they are analogous to Haskell IO. I do think it should be pointed out though that this is only one way to accomplish IO from a purely functional system. Albeit a very popular and useful one

                                                                                                                1. 4

                                                                                                                  A baby-free-monad sounds like a monad that doesn’t have any babies inside :)

                                                                                                                  1. 1

                                                                                                                    (avoid success) at all costs vs avoid (success at all costs) => (baby-free)-monad vs baby-(free-monad)

                                                                                                                  2. 3

                                                                                                                    What are some examples of other ways to accomplish IO in a purely functional system?

                                                                                                                    1. 4

                                                                                                                      I think the three big ones are that I’m familiar with are:

                                                                                                                      I don’t know of too many examples of the second two, but the first one has gained quite a bit of popularity in the last few years.

                                                                                                                  1. 2

                                                                                                                    Not langdev. Think I’m rather burned out. I heckin’ hate type checking so much. Anyone want to write a pragmatic and functional type checker for me, including generics, and let me get on with life to the actually interesting part of the project?

                                                                                                                    Maybe I’ll play with my OS project instead. It’ll never be useful and involves exactly zero theoretical math, which sounds exactly like what I need.

                                                                                                                    1. 1

                                                                                                                      Naive question: why is writing a type checking algorithm so difficult?

                                                                                                                      1. 2

                                                                                                                        It’s not. In theory. But nobody tells you how to do it. The only tutorials, examples, books etc come in one of three forms:

                                                                                                                        • Too concrete (no generics, no subtyping, basically just integers and bools and maybe strings)
                                                                                                                        • Too abstract (“here is how you implement simply typed lambda calculus, ok up to you to go from here”)
                                                                                                                        • Demonic math

                                                                                                                        I’m really sick of trying to deal with any/all of them, synthesize them into something small but sane and functional, and generally banging my head against a wall over and over.

                                                                                                                        1. 1

                                                                                                                          As a casual consumer of adjacent literature this rings true to me. Especially the demonic math. I feel somehow deficient that I can’t decipher the mad scribblings that I see in a lambda cube article. I get the impression that this would be very clear and really just self evident if I had the proper education, except I don’t know what that education is, where to get it (without paying 5 or 6 figures) or how long it would take to get. What’s more, some arrogant part of me feels like I should be able to create some amazing programming language because really I’m so smart (you know except the whole not knowing how thing).

                                                                                                                          Your language also has a linear component too, right? I imagine that only compounds the wonderfully under specified nature of the whole ordeal.

                                                                                                                          1. 2

                                                                                                                            You might be interested in my book, A Programmer’s Introduction to Mathematics (pimbook.org, ebook is free), which doesn’t focus specifically on type checking systems, but does cover math, notation, and proofs more generally. That said, I do sense that languages folks (type systems, functional programming, etc.) go a bit haywire on the notation as compared to a typical mathematician.

                                                                                                                            1. 2

                                                                                                                              …except I don’t know what that education is, where to get it (without paying 5 or 6 figures) or how long it would take to get…

                                                                                                                              It’s doable. You just start reading and talking to people about it. There are plenty of people on the r/ProgrammingLanguages discord server that will happily tell you way more than you ever wanted to know about any of it, and a couple guides that are widely cited such as http://siek.blogspot.com/2012/07/crash-course-on-notation-in-programming.html and https://blog.acolyer.org/2018/01/26/a-practitioners-guide-to-reading-programming-languages-papers/. As for “how long it would take to get”, well, same as learning to program itself I expect. You can get functionally familiar with it in a few months, and getting good enough to really understand all the nuances will take a lifetime. But unless you’re doing fancy research you probably don’t need all the nuances, just the ability to follow others’ nuances.

                                                                                                                              Of course, I haven’t done this. As you said, “some arrogant part of me feels like I should be able to create…” I keep thinking I know enough already and trying to take short cuts.

                                                                                                                              Your language also has a linear component too, right?

                                                                                                                              If you’re referring to my effect-system-ish ideas, I’m not even that far yet, and I don’t want it to be that complicated. All I really want out of it is to be able to annotate functions with useful constraints (“does not panic”, “does not allocate”, etc) and have the compiler able to check/enforce them for me. That seems like it should be pretty straightforward, since I don’t really intend much composition or metaprogramming or such. Then either that will be good enough for what I want, or I will need to learn more and elaborate it. Either way, not my main concern right now.

                                                                                                                      1. 6

                                                                                                                        In python, I tend to put some dummy constants at the top of module for readability. E.g.,

                                                                                                                        KB = 1024

                                                                                                                        MB = 1024 * KB

                                                                                                                        buffer = 128 * MB

                                                                                                                        1. 1

                                                                                                                          Heading towards the light at the end of the tunnel, the walls of which are made of Docker, Kubernetes, Terraform, DBT, Airflow, IAM, and python packaging.