1. 9

    I think this section from the GNU Coding Standards explains why the guts of so much GNU software is so damn weird:

    If you have a vague recollection of the internals of a Unix program, this does not absolutely mean you can’t write an imitation of it, but do try to organize the imitation internally along different lines, because this is likely to make the details of the Unix version irrelevant and dissimilar to your results.

    For example, Unix utilities were generally optimized to minimize memory use; if you go for speed instead, your program will be very different. You could keep the entire input file in memory and scan it there instead of using stdio. Use a smarter algorithm discovered more recently than the Unix program. Eliminate use of temporary files. Do it in one pass instead of two (we did this in the assembler).

    Or, on the contrary, emphasize simplicity instead of speed. For some applications, the speed of today’s computers makes simpler algorithms adequate.

    Or go for generality. For example, Unix programs often have static tables or fixed-size strings, which make for arbitrary limits; use dynamic allocation instead. Make sure your program handles NULs and other funny characters in the input files. Add a programming language for extensibility and write part of the program in that language.

    1. 3

      None of this sounds particularly weird, especially:

      Make sure your program handles NULs and other funny characters in the input files.

      1. 3

        The point is that the programs were intentionally somewhat contorted to avoid being suspected of copyright infringement.

        Remember, back in the late 80s~early 90s when GNU was getting its start, the only reason anybody used linux over BSD is that the latter were getting (baselessly) sued by AT&T.

        1. 3

          The GNU tools and utilities predates the Linux kernel by a significant margin.

          It’s possible that the legal situation kept BSD back. It’s also possible that it simply wasn’t as fun and rewarding to contribute to BSD as it was to the Linux kernel.

        2. 1

          I think the bit about going all-out for speed is why you get these weird lookup tables and a grep that tries to munch multiple bytes at a time.

      1. 6

        Funnily enough, I always thought that the because VS Code gained so many users, so quickly, was the reason it was destined not to last long. My understanding is that everything is supposed to work, with minimal configuration, just install this or that “plug in” and it’s ready. That means, that users don’t have an invested interest in, so it’s easier to switch away.

        If anything, I think 1. VSC’s state as the reference implementation for LSP, 2. propitiatory Microsoft extentions will keep it alive. But not for decades, the technology it is based on would make that unprobable, and corporate doesn’t guarantee extended support when it’s free – just think of how many Google services were killed.

        1. 4

          People writing plugins for it is the investment they are making. And the plugin ecosystem is huge in VS Code, so the collective investment made from users is also huge. I think that VS Code is here to stay for a while.

          1. 9

            Probably the best thing VSCode has give all of us is LSP; this makes it a lot easier to build language-specific plugins for $any editor (and the entire model of having an external process taking care of all of that stuff is much nicer too, IMO). This alone ties people less to VSCode (or any other editor/IDE) than a few years ago, since “intelllisense”-like features will work equally well anywhere (given good support for LSP, of course).

            1. 7

              Is this really the case? We used to have a supremely customizable browser that was leapfrogged by a way less customizable but faster browser within a few years to the point where the more customizable browser threw out its customizability and adopted the plugin model of its competitor.

              1. 5

                You do give a good point. But at the time Firefox ditched XUL it already had problems with the extension community fizzling out. Also to note, the initial Chrome market growth was mainly people switching to it from IE. Anecdotally, Firefox users mainly stuck with what they used until the XUL removal.

                1. 3

                  That’s true. While not a plugin developer even as a user I remember the frequent issues that plugins would not work in new Firefox versions for a while until they got updated, at which point it again only worked until the next release. I can see as a plugin developer you’d do that once, twice, but if you need to keep up every 6 weeks that’s gonna take a toll.

                  Yet somehow I think that just throwing out extensibility out the window has made it a worse browser (“Mozilla Chromium”) and if not for my contrarian attitude there is very little to keep me in Firefox-land. Recent firings at Mozilla also have not cast a good light on the long-term viability of the browser.

              2. 5

                It’s an investment for the plugin-creators, but not for the people using them. My understanding is that it’s just click-to-install and go. That level of investment can easily be replaced by whatever other editor, that provides the same effort.

                1. 5

                  You can invest just as much time configuring VS Code and the extensions you install as you can in vim if you really want to. That you can just click a button to install instead of manually downloading a plugin or cloning a repo to your .vim folder or adding some lines to your .vimrc or whatever the process is for installing a package on emacs is nothing but a welcome improvement.

                  I agree that it’s good to be wary of Microsoft, but for now this is probably the best open source editor available out there and I’m using it as long as it lasts.

                  1. 3

                    Sure, it’s configurable and programmable, but is that usually done? My point was just that, for since most people just install a plug-in, and let it be, these will be the same that could easily move back and forth between other editors with similar capabilities.

                    1. 2

                      The fact that there are specific plugins is important. Companies are starting to create plugins specific for their product for VS Code, e.g. Bitbucket integration or AWS Toolkit. These extensions aren’t that replaceable, so the move between editors gets more complicated. In other words, you could say that some users invest into VS Code by getting used to using specific extensions.

                      1. 1

                        But these extensions will get recreated for the next editor/IDE du jour since Amazon would of course go where people are, there is no inherent connection why AWS Toolkit would require VS Code. I remember the hype of creating IDEs based on Eclipse, but most of them had their lunch eaten by Jetbrains IDEs and VS Code these days.

                        1. 1

                          If those editors are able to get popular without all the big extensions that everybody relied on having been ported yet.

                          1. 1

                            These big extensions only got created after VS Code got popular; it would make little sense for companies to invest into a tiny niche editor and its extensions.

                            1. 1

                              Right, exactly. I think you’re missing my point - you’re saying that the big extensions will move to the next popular editor, but I’m saying that (what would be) the next popular editor might not have a chance to get popular in the first place because it was missing the big extensions people expect. It’s a chicken-and-egg problem.

                        2. 1

                          So it’s not only propitiatory Microsoft extensions, that are not allowed to be ported to other editors, but also other platforms as well, but third-party service providers as well.

                          So in other words: Microsoft VS Code is on it’s way to become the Windows of text editors. Should anyone be surprised?

                      2. 1

                        The process for installing a package in Emacs is clicking a button. In this screenshot it looks like a hyperlink that says [Install]

                  2. 1

                    But not for decades, the technology it is based on would make that unprobable

                    I’d love to hear more elaboration on this. I’m assuming it means because it’s built on the web languages, but from my perspective, if any language is indicating longevity at this time it would be the web languages?

                    1. 1

                      Yes, from my experience, web technologies and preferred practices change a lot, and quickly. I don’t see that as a reliable foundation. Other than that, Electron is based on Google’s Chromium, and with their increasing monopoly over the web, what they will be doing is also a great uncertainty. I’m not guaranteeing anything, just stating my guesses.

                      1. 1

                        Got it, yeah saying preferred practices change a lot in the web world is an understatement. Web technologies have elements of stability (old websites from the 1990s still just work), and elements of instability (how you’re supposed to make a website changes almost everyday). Point blank my view on this is it doesn’t really matter, technology choices are really a problem that only affects resource-strapped projects. E.g., platform momentum can be leveraged to solve technology problems, but no amount of technology improvements will ever create platform momentum. In other words, if the shifting sands of web technology are ever a problem for VS Code, it will be because VS Code is already dead for some other reason.

                        (Which also means if you want to still be able to use VS Code, even if it’s no longer popular, then technology choice is important. Personally I think VS Code’s value is inextricable tied to its momentum, so that’s not me.)

                  1. 8

                    I find it weird of the author to pick “the most popular” as criterion for the longevity of software while at the same time showing how time and time again the popularity of once popular editors has waned. And the reasons why this will not repeat with VS Code are partly debunked by the extensibility of Emacs and partly by showing how many IDEs used to be based on Eclipse back in the day, which also has been, erm, eclipsed, since its heyday.

                    And at the same time, noted the existence of Emacs and Vim (both essentially originating in 1976) and dismissed their continued use as not relevant to longevity. I don’t know about VS Code, but I am pretty sure that even in 10 years people will write Emacs extensions and I am pretty sure that some form of (Neo)Vi(m) will continue to exist.

                    It is actually even more baffling how the author mentions Ctrl-C/Ctrl-V as important, while completely dismissing that just about every extensible editor (or browser) has a plugin that more or less well emulates Vi, including VS Code.

                    1. 4

                      If you even accept the premise.

                      It’s crossed a popularity threshold that’s eluded other text editors by being used by over 50% of developers.

                      Based on what? One SO survey?

                      1. 5

                        If you’ve got more I’m all ears!

                        Here are a couple more:

                        VS Code 56.6% https://2019.stateofjs.com/other-tools/#text_editors

                        VS Code 76.4% https://2019.stateofcss.com/other-tools/#text-editors

                        Personally I think Stack Overflow is the fairest poll I’ve found, because it has the least obvious bias, but I would love to ear any and all data available on this subject.

                        1. 3

                          Anecdata, but I’ve never participated in any SO poll as far as I can remember, and neither has one of my team mates (most don’t even have an account, only use it when googling for stuff).

                          While it ranks very highly and is frequently read, the amount of people actively participating is -imho- not representative of developers at large. Also the VS user base is still a bit skewed towards front end and web development, as far as I know - for example the non-Windows C/C++-developers I know mostly don’t.

                          I simply refuse any online poll to be an authoritative “that’s what developers want”, the participation is usually just too low. Not really worth fighting over though.

                          1. 2

                            I don’t have a better one. But I remember that SO survey, and I can tell you for certain that the way that’s worded, if I answered today, I’d check VSCode, Emacs, IntelliJ and Notepad++.

                            I use VSCode for PlatformIO because that’s the least painful way I’ve found to build/load/debug code for things adjacent to Arduino, which I use for fun regularly. I use Emacs (with evil mode) for most of my regular editing for which I don’t need tight debugger integration. Todo lists, editing README files, quick and dirty C++ tools, small scripts, etc. I have a subscription to the JetBrains toolbox because I find that its refactoring tools and good build/debug integration make me more productive on things that pay my bills. And I use Notepad++ whenever I’m stuck on a windows box and need to do some nontrivial config editing.

                            If the question asked what was important to me for productivity, both Code and Notepad++ would fall off. IMO the wording of the question makes it likely that VS Code gets over-reported given the current environment.

                            Also, I don’t think the notion that backing of a large corporation prevents stagnation holds any water. This is the same large corporation that backed Internet Explorer, which stagnated for years before being replaced with Edge, which stagnated for fewer years before being replaced with a chromium wrapper. Office also had a long period of stagnation (I’d say 1997 - 2013, but others might argue that window down). As did Windows.

                            I think I could argue that stagnation is not fatal to some software packages, but not that corporate backing prevents stagnation.

                            But I also think that editing text is not a very “sticky” activity compared to editing vector graphics or spreadsheets. I can switch to any editor that can handle large files and I can come around to like any editor that provides half-assed decent vi emulation. Even emacs, as I mentioned. If MS puts VS Code into IE 6 mode, the ecosystem can move on rapidly.

                            1. 1

                              But I also think that editing text is not a very “sticky” activity compared to editing vector graphics or spreadsheets.

                              I think you’re onto something here. For the vast majority of people who edit text, Notepad.exe suffices - in that they simply don’t bump up against its limitations. VS Code covers everyone else, up to the tiny minority who obsess over latency, licensing, and ligatures, and who won’t settle for anything lesser.

                              1. 1

                                I agree with all of your points about corporate management, but the problem is there isn’t much point in evaluating corporate management in a vacuum. Essentially there are three models for managing a text editor:

                                1. Corporate (Atom/VS Code)

                                2. Indie/Solo Developer (Sublime Text/TextMate)

                                3. Open Source (Emacs/Vim)

                                I work on a Mac, so I can’t comment on other platforms, but for the Mac, the problem with #3 is that it hasn’t produced a post-1985 (when many of our current UI conventions were solidified) mainstream text editor. So #3 gets cut off for a separate reason than stagnation, it just hasn’t produce a text editor that works within modern expectations.

                                We’ve tried #2, and that’s resulted in stagnation (as perceived by the community), with it being a major complaint about both Sublime Text and TextMate.

                                So #1 is the only option left.

                                In other words, the question isn’t whether corporate management is reliable in a vacuum, it’s just whether it’s more reliable than #2. It the end of the day, this is all just guesswork, but given the experience myself, and many others in the developer community, have had so far with #2, I’d place my bet today on #1.

                                (I’ll skip the polling stuff because it essentially boils down to whether you think that polling data is useful or not. I think it’s flawed but useful, but wouldn’t fault anyone for coming to a different conclusion.)

                        1. 5

                          Ever since ponying up for PragmataPro, I find it very difficult to switch back to wider fonts. The extra information I get per line without sacrificing readability is wonderful.

                          1. 3

                            I’m the opposite, I recently switched to a wider font (IBM Plex Mono) and I noticed I can reduce the font size by a couple of points (11 to 9), increasing the number of lines of code I can display compared to Iosevka. I’m still able to display 2 buffers side by side.

                            1. 1

                              +1 for wider fonts, Source Code Pro is king here.

                            2. 1

                              I had the same, though I went from Iosevka which is nice to see whether you enjoy these kinds of fonts. PragmataPro is just very slightly nicer, but the incredible configurability and free license of Iosevka is definitely cool.

                            1. 5

                              It makes sense that Rust has better libraries and IDE support, it has more funding and a larger community.

                              in OCaml you have opam, esy, dune, and OCaml itself all doing part of the job. They sorta kinda mostly work well together, until they don’t. And as you wire them together, you really need to understand what each tool does, where it starts and stops, and how they integrate.

                              I think this is only true if you’re trying to use Reason, now ReScript?. If you stay firmly within OCaml-land you only need to know two tools: Opam and Dune. Opam is a package manager and Dune is a build system. It’s nice that Cargo can combine these — and Cargo is excellent — but so is Dune. Dune has been introduced widely in the last two years and IMHO has totally transformed the OCaml build story. It’s true that Reason has introduced a lot of segmentation, but if you stay away from npm things are generally good.

                              Re macros: I totally agree that PPX’s fall short sometimes and a hygenic macro system for OCaml would be awesome. That said, the blog post supposedly showing trouble in the PPX ecosystem is from four years ago and I believe the proposed solution has been adopted. Overall the PPX infrastructure has improved a ton in the last four years.

                              I’ll also say OCaml has a great module system with “functors” (function-like things that take modules as arguments and produce new modules) that subsumes much of the use-case for macros.

                              Re aesthetics:

                              OCaml is so ugly that the community came up with a whole other syntax for it

                              Well, the JavaScript community came up with a JavaScript syntax for it. Of course beauty is in the eye of the beholder, but I happen to prefer the OCaml syntax.

                              1. 8

                                That’s one point I disagree with. Merlin (in OCaml) is just amazing, and much better than rust-analyzer. It’s fast, it’s very, very robust to bad syntax, and I only have good things to say about it. RA is not too bad but it’s not as mature yet.

                                The ppx story keeps unfolding with, imho, still no viable solution in sight (soon there’ll be a ppxlib 2.0 or something, that doesn’t improve ppx stability, and we still don’t have a stable AST to write these parsers again.) I would love macros and deriving in OCaml, but I don’t see any way of getting them in a stable, future-proof way.

                                1. 3

                                  I would love macros and deriving in OCaml, but I don’t see any way of getting them in a stable, future-proof way.

                                  Both Haskell and Rust wisely have deriving so OCamls stubborn insistence to defer it to a combination of ppx_deriving/type_conv and support from the build system is just death by a thousand cuts.

                                  1. 2

                                    I couldn’t agree more. Sadly it’s difficult to contribute this kind of functionality and get it accepted.

                                2. 4

                                  This is exactly what I was thinking about reading his criticisms of OCaml. Sure, the language has some warts (I think mostly due to adding features over the years) but I find the core syntax very nice. Reason to me is incredibly ugly. Along similar lines, OCaml + Merlin + Emacs is one of the best toolchains for writing code that I’ve ever used.

                                  The authors comments about bad error messages are valid, they could definitely use some work. But I think a lot of the complaints about dealing with the typechecker is something you get comfortable over time, or if you sit down and read up on the type inference/checking algorithm.

                                  1. 1

                                    I think this is only true if you’re trying to use Reason, now ReScript?

                                    No, you can also use Esy as a replacement for opam on pure OCaml projects, without having to use Reason. ReScript (formerly BuckleScript) is a separate toolchain that leverages npm and a different build tool (ninja).

                                    To the wider point, though: I was going to take issue with the way Paul mentioned confusion with so many build tools, but I’m forced to admit that’s partly on the OCaml community. If the community-recommended tools don’t solve real developer needs like cross-project dependency sandboxing and build caching, then eventually alternative tools (like Esy and Nix) will enter the picture and compete for market share.

                                  1. 31

                                    This is interesting ’cause I came to Rust from (mainly) OCaml back when Rust was new, and my complaints were pretty different. My main memory is that I had lots of issues with closures, which now seem to be mostly smoothed over by impl Trait, better type inference, and better lifetime inference.

                                    In retrospect, my experience was that writing Rust like it is OCaml is not much better than writing OCaml. Rust is good at lots of things when doing this, but they’re all things that OCaml is good at, and you run into bunches of inconveniences because OCaml is designed to be written in that style and Rust is not. To make Rust better than OCaml, you have to write it like Rust, and that’s only worth it if you want the things Rust is good at which OCaml isn’t: mainly, IMO, control of memory via borrow checking. Without that you just get a weird-looking, very inconvenient OCaml. Looking at the “things to like about Rust” it’s community, libraries, tooling, more tooling, aesthetics… oh yeah, and macros. Only one actual language feature. Not that the ecosystem around the language isn’t important and worth being a priority, but the actual language feature is pretty far down the list.

                                    Mutability in Rust is also one of those things you have to embrace, coming from an immutable language. im is an extremely nice tool, but in my experience mainly useful for only certain things like sharing data between Erlang-like actors. The thing to realize is that the Problem with mutability comes from shared (aliased), mutable data. Immutable-by-default languages deal with that Problem by keeping mutability to a minimum. The whole point of Rust is that it deals with the same Problem by keeping sharing to a minimum, and it works just as well.

                                    So yeah, you CAN write Rust with Rc’s everywhere and lots of immutability. But apart from the Rust ecosystem itself, IMO there isn’t much to gain by doing that vs just writing OCaml, and OCaml is better at writing OCaml than Rust is.

                                    That said, a lot of the things they label as sucking about Rust are definitely problems:

                                    • I was bitten by the pattern matching one myself recently, and apparently there’s a feature in development to make it suck less, but it’s been in development hell for years. You can pattern match “through” references just fine, but Rust is replete with smart pointer types that can look like references but aren’t, and pattern matching through them Just Doesn’t Work.
                                    • Related to that is “too many ways to do things”. It’s something you get used to as you learn the languages, but it still causes occasional Feels Bad moments when you remember that to write an iterator you need to implement iter(), iter_mut(), and into_iter() which are all exactly the same thing except for returning &T, &mut T and T. Threading vs. non-threading variants (Rc vs Arc) are less common but similarly Feel Bad. In my experience trying to fix this rapidly leads into a grim hellscape of overly-generic generics and undecidable type theory. Maybe someday we’ll figure it out, but it will probably be in a different language than Rust.
                                    • Over-use of macros is definitely a code smell, one I suspect comes from the author wanting to write OCaml. Rust very much embraces “make it explicit and clear” over “make it concise and clever”, which IMO is a good thing but dang do you do a lot of typing sometimes.
                                    1. 7

                                      Looking at the “things to like about Rust” it’s community, libraries, tooling, more tooling, aesthetics… oh yeah, and macros. Only one actual language feature.

                                      You kind of undersold the “other” two language features: zero-cost abstraction and “fearless concurrency” (which has taken OCaml… some time to get to). You mentioned “control of memory”, but it’s only a little bit about “control” and often more about the zero-cost aspect.

                                      I agree with the rest of your point, though. I feel like OCaml is a really great language and arguably more productive and expressive than Rust for domains where the (pretty small) raw performance difference and concurrency issues don’t matter much.

                                      1. 2

                                        Zero-cost abstraction is nice but IIRC OCaml’s abstractions are generally pretty cheap. I mainly do indie gamedev stuff, so avoiding GC pauses is way more important than having absolute performance. Others’ priorities may vary.

                                        Fearless concurrency is absolutely awesome though. I don’t often need to use it, but when I do… right now the only languages that do concurrency Right are Erlang and Rust.

                                        1. 3

                                          Yes, OCaml is very performant for sure. And Rust isn’t immune from “GC pauses” in a way: https://abramov.io/rust-dropping-things-in-another-thread

                                          Fearless concurrency is absolutely awesome though. I don’t often need to use it, but when I do… right now the only languages that do concurrency Right are Erlang and Rust.

                                          Agreed. Any opinion on Clojure with respect to concurrency?

                                          1. 5

                                            And Rust isn’t immune from “GC pauses” in a way…

                                            Memory management always costs something, somewhere. The thing is just that real-time programs like games or music/video players must have control over where the cost happens, or sooner or later it will catch up with you at an unexpected time and you will drop frames. How bad this is depends on the program and how picky you/your users are.

                                            Any opinion on Clojure with respect to concurrency?

                                            Unfortunately no, I haven’t used Clojure enough. Clojure is one of the coolest languages I will probably never use; I tend to write either command line tools or video games, and either way having long start-up times are painful at best. Though now that I check again it appears a “hello world” program starts and runs in only two seconds on my desktop now, so maybe it’s worth checking up on again.

                                            1. 2

                                              Clojure is one of the coolest languages I will probably never use

                                              Yeah, same here. I used it once for a small app. It’s a really nicely designed Lispy language. I like that it enforces immutability and feels more FP than your typical LISP or scheme. But, the startup time was rough, memory consumption was high, and I still just can’t see myself choosing an untyped language when I have a say. Definitely just my bias. But I still admire it and really like how clean the code looks.

                                              1. 4

                                                But, the startup time was rough, memory consumption was high, and I still just can’t see myself choosing an untyped language when I have a say.

                                                Nitpicky, but: it’s dynamically typed. It’s not untyped, which is another beast entirely.

                                                1. 1

                                                  Yes, fair enough.

                                              2. 2

                                                GraalVM is supposed to help with clojure startup times. Might be worth considering.

                                                This claims 100x improvements:

                                                https://github.com/io-tupelo-demo/graalvm

                                                1. 1

                                                  Two seconds!? I can’t even…

                                            2. 1

                                              I’d be curious on your thoughts of how Rust and OCaml are handling concurrency today. I’m somewhat out of the loop on each, though familiar with both languages. If I were picking between the two for a project that had some amount of inherent concurrency, what would the tradeoffs be?

                                              1. 2

                                                I’m definitely not an OCaml expert. But I’d say it depends on what kind of concurrency you need. If you want to be able to have some amount of multi-threaded-ness, I’d say you are pretty much forced to use Rust today, unless you’re okay with the almost-there-but-still-evolving multicore branch of OCaml.

                                                If you need single-threaded async, there are libraries for OCaml to do that.

                                                Otherwise, it’s just up to whichever language you like more.

                                                1. 2

                                                  Makes sense, thanks for the insight!

                                            3. 4

                                              But they’re implementing the Dark Language interpreter. Dark is an immutable functional programming language that uses pure functional data structures.

                                              I like writing mutable code in Rust, but that’s not going to be an option here.

                                              1. 3

                                                I like writing mutable code in Rust, but that’s not going to be an option here.

                                                But the implementation language and the destination language don’t have to match. OCaml itself is written in C and OCaml and C is quite different from OCaml.

                                                1. 1

                                                  But the implementation language and the destination language don’t have to match

                                                  But it sure helps.

                                              2. 2

                                                I’ve been spending some more time with Haskell lately, using it to learn how first-order unification and Hindley-Milner type checking works; and I implemented some of the same code in Rust. Based on that experience, I think it’s better to think of Rust as a C++ competitor that borrows concepts from the ML-family of languages. Compared to Haskell, Rust code is extremely verbose and finicky in the details (I.e. owned vs borrowed vs mutably borrowed). The trade-off is that, if you’re fimiliar enough with Rust, you have a pretty good idea what the resulting assembly code will be. In comparison, Haskell compilation is a black box due to lazy evaluation and GC.

                                                I think there’s definitely a place for a language that is closer to the ML-family but that fully embraces linear types to acheive static memory management. I’m trying to learn enough to build a prototype, though it might take years to get there. I’m planning to use concatenative syntax and semantics to minimize the syntactic overhead of pervasive linear types. And I’m also playing with the concept of implicit parameters (similar to shell environment variables) as a substitute for globals.

                                                1. 2

                                                  I love ocaml, and have so far dabbled in but not really done much with rust. however, I have an ocaml library that I’m seriously considering porting to rust, despite the fact that it will undoubtedly be a lot more inconvenient to implement, because of one other major rust feature - rust libraries can be seamlessly consumed as though they were c libraries by any other language with a c ffi. I know very few languages with this property - rust, d if you are willing to use a restricted subset, c++, zig and ats are the main ones that have crossed my radar, though there are probably a few even more niche ones that I have seen and forgotten.

                                                  1. 4

                                                    It’s not 100% seamless, you have to mark C-consumable functions as such and there are certain Rust types those functions can’t consume or produce (enums, generics, etc). But it’s probably like 80% seamless.

                                                1. 7

                                                  Most people familiar with mechanical keyboards are familiar with QMK. It’s really a great project, and it’s not very hard to make your own super advanced keyboard layout with macros and multiple keyboard layers.

                                                  Their web based keyboard configurator is really slick too!

                                                  1. 2

                                                    It is cool indeed.

                                                    Oddly enough, nobody is making standard, full sized mechanical keyboards with qmk as stock. That’s a gold mine waiting to happen, for the first to market.

                                                    I fear some company will accidentally make a keyboard using a compatible arm or avr microcontroller, the community will pick up on it and it’ll thus get undeservedly popular, instead.

                                                    1. 4

                                                      An inline controller can be used to turn a non-programmable (full-size) keyboard into a programmable one. E.g. https://www.1upkeyboards.com/shop/controllers/usb-to-usb-converter/ this one runs tmk, which is what QMK was originally forked from.

                                                      1. 1

                                                        I like how, with qmk, it is possible to reduce latency. An inline controller will only add latency.

                                                        1. 2

                                                          Sure it’s definitely a work around. Figured it was one way to get access to more programmable full size keyboards. Another option is to replace the controller in an existing board. I did this with my Filco TKL — it now runs TMK: http://bathroomepiphanies.com/controllers/

                                                          1. 2

                                                            But does it in reality? According to Dan Luu he measured the Planck which is presumably running QMK to have 40ms latency compared to 30ms latency that e.g. my Pok3r has with its proprietary firmware or 15ms that Apple’s keyboard seems to have. Overall a pretty weak showing for QMK which is presumably due to its myriad of awesome features but that were not written with latency in mind.

                                                            1. 1

                                                              I’m not sure about Dan Luu’s tested keyboard actually running qmk, or how to tweak qmk for low latency. It might have some silly slow debounce enabled by default. Or it might be qmk is hopelessly sluggish. But, at least, with source code and a friendly keyboard, it is possible to work on improving latency. If a keyboard won’t even run qmk, that’s not anywhere as good as a starting point.

                                                              None of my keyboards run it, but I plan to eventually (once it’s easier?) get a supported full size keyboard. Then I might personally take a look at it. Worst case scenario, qmk turns out to be shit but I do learn how it works, then write my own.

                                                        2. 2

                                                          Or even just a standard full sized keyboard with QMK.

                                                          1. 1

                                                            If it isn’t mechanical, it won’t appeal to the sort of people who care about what keyboard they use.

                                                            1. 5

                                                              Reprogrammable, high-quality, low-volume. mechanical keyboards cost more to produce than conventional keyboards do. When cost is a consideration in this way, you’re more likely to think critically about each piece of the design and question whether the value you get from it will justify the additional cost, rather than saying “let’s just do an exact copy of the 101-key design that IBM standardized in the 1980s”.

                                                              Given this dynamic, it’s very unsurprising to me that no one builds “full size” designs this way.

                                                              1. 1

                                                                There’s plenty of full size mechanical keyboards in the market. They also sell well, to gamers.

                                                                Picking a qmk friendly microcontroller and preparing a friendly firmware update method can’t be that hard, much less increase the cost in any significant way.

                                                                It just hasn’t been done.

                                                              2. 1

                                                                I disagree. I use the Microsoft Ergonomic keyboard as my preferred daily driver but would enjoy having QMK onboard. I’ve used a mechanical keyboard before but buying/building a shaped, tilted, split design mechanical is really expensive. The Ergodox EZ looks great, but it’s a month’s rent, ~5-6x the Microsoft keyboard.

                                                                1. 2

                                                                  There’s full sized keyboards with mx brown switches in the $100 to $200 range.

                                                              1. 1

                                                                That one definitely isn’t full sized!

                                                          1. 13

                                                            One of the Factor devs here. Feel free to ask any questions you might have, and I’ll do my best to answer.

                                                            1. 6

                                                              What do you think is the over arching goal for factor? Were you trying to solve a particular problem which was not solved well by other languages?

                                                              1. 9

                                                                I’m not the inventor, so I wasn’t trying to solve any particular problem as such. I just really liked what I saw.

                                                                For me, Factor provides a sweet spot of easy and very powerful metaprogramming, a simple runtime, and incredible introspectability. The feel of doing development in it is somewhere near a Lisp Machine, since all objects have graphical representations, you have a full REPL with compiler the entire time, and yet you still compile down to fully native code at all times. This makes doing incremental development extremely easy, and I think the amazingly rich standard library and GUI toolkit (for a language with its user base, at least!) speaks to its productivity. And unlike, say, Pharo/Squeak Smalltalk (an old haunt of mine), it uses normal text files for code, so I can use traditional tooling like Git and GitHub to manage development.

                                                                These days, I write a lot less in Factor. Most of that’s due to real life taking precedence as I get older, some of that’s due to other languages closing the gap quite a bit. (Things like Jupyter, or Swift Playgrounds, did not exist when Factor was created, for example.) But Factor remains my preferred environment when I’m trying to learn a new concept or explore a domain I’m not terribly familiar with. Most recently, I had a blast using it for Cryptopals, for example.

                                                                1. 2

                                                                  These days, I write a lot less in Factor. Most of that’s due to real life taking precedence as I get older, some of that’s due to other languages closing the gap quite a bit. (Things like Jupyter, or Swift Playgrounds, did not exist when Factor was created, for example.) But Factor remains my preferred environment when I’m trying to learn a new concept or explore a domain I’m not terribly familiar with. Most recently, I had a blast using it for Cryptopals, for example.

                                                                  Not to make light of anyone’s excellent work, but whenever I’ve explored Factor this matches what I’ve seen. The community was incredibly vibrant and active a few years back, but recently activity seems to have really fallen off across the board.

                                                                  Am I mis-perceiving things or are you not alone in having moved on in some respects to other languages and environments?

                                                                  1. 5

                                                                    Eh, it’s mixed. Yes, things have slowed down: Slava left, the company using Factor commercially folded, and a lot of us (such as me) have kids and stuff that make coding for fun harder.

                                                                    On the other hand, there’s actually a lot of work going on (largely driven by @erg) to make the syntax and runtime a bit more standardized, there have been a number of library improvements from Alexander Iljin, when people have needed more libraries they’ve added them, and so on. If you look at the core repo, you’ll see there honestly been a tremendous amount of activity since the last release. Sure, the pace may be slower, but it’s steady.

                                                                    In other words: I don’t think development has stalled, but I think it’s stabilized. For me, that’s great: Factor’s library grows and its stability improves, but things are generally where I left them. I know that same kind of things draws a lot of people to the Common Lisps as well, which also have a slower churn rate.

                                                                    1. 2

                                                                      I guess I was referring less to the language itself than to the ecosystem around it - blog posts and the like. But you’re right, none of this is an actual measure of a project or community.

                                                                      1. 2

                                                                        Blog posts are definitely down, but the mailing list is still quite active. I think it depends a bit where you look. But yes, I hear your point.

                                                              2. 3

                                                                Factor is a dynamic language and that makes sense to me: You have values on the stack that know their own types. When you are going to use a value, you check it’s type.

                                                                How do you think a statically typed concatenative language would look? My intuition is that it would be like a reduce operation over all of words in the program. I think I’m a bit hung up due to the lack of formal parameters.

                                                                1. 4

                                                                  How do you think a statically typed concatenative language would look?

                                                                  Probably exactly like our (optional) static typing. Do note that the compiler could do a lot more than it currently does for optimizations, but you can play with optimized. to see what it’s able to pull off. For even relatively complex cases that involve fixnums, it can basically do ops on straight machine words.

                                                                  1. 1

                                                                    That is neat! Is it used a lot in practice?

                                                                    How is the data stack implemented in Factor? Is it just an array of pointers? Or does it use the top bits to determine the types? Tried looking at the repo, but wasn’t sure what file it might be in…

                                                                  2. 3

                                                                    There was also cat from Diggins and Kleffners thesis

                                                                  3. 2

                                                                    On the “See some example programs” page, the example program links all give me a 502 error.

                                                                    1. 1

                                                                      How related is concatenative programming related to array programming (APL)? I feel that some of the concatenative combinators seem familiar to transformers in Nial

                                                                      1. 1

                                                                        Can the REPL run in a console? I installed the factor package from AUR, and factor-vm seems to only be able to open its own graphical window.

                                                                        1. 3

                                                                          I can’t speak to whatever Arch is doing, but I just double-checked that plain factor on my box kicks off a command-line REPL, so I’m not sure what’s going on with their package.

                                                                          That said, I would strongly discourage using the vanilla command-line REPL; you’re missing so much awesomeness. If you really badly want to use a terminal for whatever reason, I would strongly suggest at least using FUEL (our SLIME workalike) in Emacs.

                                                                          1. 1

                                                                            ArchLinux doesn’t install a plain factor (perhaps because the name collides with the factor binary from the coreutils package). There is /usr/bin/factor-vm which is a symlink to /usr/lib/factor/factor. The latter also opens a GUI thing, but I just found out I could factor-vm -run=listener to get a TTY thing, so that’s cool.

                                                                            Thanks.

                                                                            1. 3

                                                                              That’s exactly the reason why the factor binary is renamed in the package. Source: Arch Linux packager here.

                                                                              It is kinda sad that there hasn’t been any new releases in years, so if you want to have the new features you’re better off with factor-git.

                                                                              1. 1

                                                                                Thanks, I’ll switch to factor-git.

                                                                                1. 1

                                                                                  I’m not thrilled with that, but there’s an (in my opinion, wrong) attitude that our next release needs to be Perfect™, which means it’s forever delayed. The good news is, as you said and @kas is now doing, that Git is honestly quite stable, so that’s probably the route most people should go for now.

                                                                                  1. 1

                                                                                    I think cutting a release would also send out a signal that Factor is not dead. I can confirm that git is quite stable so maybe there could be a point where tagging a new release might make sense.

                                                                                    In any case, thanks for maintaining Factor. I’m not actively using it any more but honestly due to the whole environment and community this was the most fun learning a programming language I’ve ever had, especially a completely different paradigm.

                                                                        1. 2

                                                                          Mhh, I fear the requirement of ALPS-style switches makes this a rather niche endevour given how common MX-switches and their clones are. The clear advantage over an Atreus is the configurability of the angle between the halves.

                                                                          1. 3

                                                                            they feel so good tho

                                                                            Anyway, it’s 36 keys. There’s no way this project would ever be anything but niche.

                                                                            1. 3

                                                                              Says the inventor of a keyboard with just 6 keys more that seemingly has found its niche in… Clojure programmers.

                                                                              I’ve finished my Atreus this week (thanks!) with Kailh Box Jades. Never typed on ALPS, I just always hear about their superiority.

                                                                              1. 2

                                                                                Honestly for clicky switches you can find pretty close equivalents in MX, (I’m using Kailh light greens right this moment and ISTR box jades are close to that) but for non-clicky switches I still haven’t found anything quite the same. Personally I’ve never seen the appeal of 3rd-party keycap sets since … why would you want anything other than blanks? (Edit: unless it’s those incredible historic beige 1990s Apple caps in the pics for the OP of course) But I understand the rest of the market disagrees.

                                                                          1. 14

                                                                            I am amazed at how Canonical always tries to make up their own technology instead of embracing existing open-source projects… and it NEVER works, and they keep trying it anyway. Let’s look at the list:

                                                                            • Upstart vs. systemd
                                                                            • Mir vs. Wayland
                                                                            • Snap vs. Flatpak
                                                                            • Unity vs. GNOME

                                                                            Am I missing any? I feel like there’s more. Does anyone know why the hell they do this? Is it them and Red Hat having a technological pissing match that Red Hat usually wins (systemd and Flatpak come out of Red Hat after all)? Or do they just dream of making a de-facto standard that gives them lots of power, which this article seems to imply?

                                                                            Either way, good on Mint for pushing back against this nonsense.

                                                                            1. 26

                                                                              Note that Upstart considerably predates systemd.

                                                                              1. 18

                                                                                Snaps predate Flatpak.

                                                                                IMHO, what happens is that Canonical validates the existence of alternatives by beginning work, causing alternative efforts to start up or for existing alternative efforts to gain momentum. Then a certain vocal faction publicly trash Canonical’s efforts and try to swing the community towards one of the alternatives.

                                                                                None of this is intended to diminish the value of the alternatives. Alternatives are good. They’ve always existed in the our ecosystem (eg. sendmail, qmail, postfix, exim; apache, nginx; etc). But in the case of a Canonical-led effort, a specific vocal crowd makes it political.

                                                                                An exception is Unity vs. GNOME. That happened after GNOME didn’t want to follow Canonical’s design opinions on how the desktop should work (even though they represented the majority of GNOME desktop users!), and refused patches. But then, as above, politics happened anyway.

                                                                                1. 16

                                                                                  Author’s note: I use RedHat as a stand-in for the entire RedHat/CentOS/Fedora ecosystem a lot in this.

                                                                                  tl;dr Redhat is trying to address pain points for existing users, Ubuntu is going after new markets. Both are laudable goals, but Ubuntu’s strategy is riskier.

                                                                                  I think a lot of this comes down to market demand. With both the “Mir v Wayland” and “Unity v GNOME” RedHat and Canonical were both trying to address a market need.

                                                                                  With Wayland and GNOME, Redhat wanted a more modern display server and desktop environment so that it’s existing customers didn’t have to deal with the big ol’ security hole that is X. (Don’t get me wrong, I love X11 and still think it’s valuable, but I think RedHat’s market disagrees).

                                                                                  With Mir and Unity, Ubuntu wanted a display server and DE that would scale from a phone to a multi-monitor workstation. This is a laudable goal, and it did see a market need to address.

                                                                                  The difference is, Ubuntu was trying to address a market that it wanted while Redhat was trying to address the needs of a market that it actually had. Redhat has tons of customers actively using Wayland and GNOME for their intended purpose, and that gives a project momentum. Ubuntu also had loads of customers using Mir and Unity, but for only one of the multiple purposes that it was intended to be used for. Engineering always has trade-offs, designing a display server and DE for such a wide array of purposes is bound to have rough edges for any single one of those purposes. Ubuntu was asking it’s primary market, desktops and laptops, to suffer those rough edges for the greater Canonical purpose.

                                                                                  Even with snap v flatpak, again Ubuntu’s goals are much wider with snap than Redhat’s are with Flatpak, judging from what I’ve seen. Flatpak is a way for Redhat to distribute software to Linux/systemd in a way that’s more robust than the current RPM method, and Fedora is actively using flatpaks as a base to their Silverblue variant. whereas with snap, I think that Ubuntu wants to be the one stop shop for distributing software on Linux. Again: engineering, trade-offs, rough edges, etc.

                                                                                  The Redhat method of integrating the new package format seems to be coming up with an entirely different distribution to leverage flatpak functionality to it’s fullest while kinks are worked out . Canonical’s method seems to be: “Let’s shove it into our flagship product, and work out the kinks there”. This comes with a lot of inherent risks.

                                                                                  1. 5

                                                                                    You could also mention bzr vs git/hg and like the other software you mention Bazaar(-NG) is essentially dead.

                                                                                    1. 7

                                                                                      On Bazaar, there’s a very interesting retrospective here: https://www.jelmer.uk/pages/bzr-a-retrospective.html

                                                                                      One quote I think is quite relevant to the current discussion:

                                                                                      “Some people claimed Bazaar did not have many community contributions, and was entirely developed inside of Canonical’s walled garden. The irony of that was that while it is true that a large part of Bazaar was written by Canonical employees, that was mostly because Canonical had been hiring people who were contributing to Bazaar - most of which would then ended up working on other code inside of Canonical.”

                                                                                    2. 5

                                                                                      Upstart predates sytemd, and pretty successful, even Redhat adopts it for RHEL.

                                                                                      1. 4

                                                                                        Note: Apparently Canonical’s solutions often are the forerunners and other people the copycats… I didn’t actually know that, thanks for the corrections. Frankly it makes the fact that their solutions tend to come out on the losing side even more interesting…

                                                                                      1. 40

                                                                                        If you absolutely have to use reddit, for the love of all that’s good, use old.reddit.com.

                                                                                        1. 14

                                                                                          I also recommend the the old reddit redirect addon: https://addons.mozilla.org/en-US/firefox/addon/old-reddit-redirect/

                                                                                          IDK if it still happens, but there was a time when you would suddenly be dumped onto the new reddit while browsing old reddit, and this solves that issue.

                                                                                          1. 1

                                                                                            it happens when clicking a link from modmail on mobile. very annoying since I can’t even see how to moderate a post in the new UI; I have to manually edit the URL first and add back the ‘old’.

                                                                                            1. 2

                                                                                              if you phone is anything but iOS i think you can install the addon

                                                                                            2. -1

                                                                                              Thanks, but I don’t use Firefox anymore, nor install it, except for occasional testing.

                                                                                              The only time I get dumped back into new reddit, at this time, is when I’m on mobile, and then “Request Desktop site” fixes it.

                                                                                              However, this happens despite me requesting that it doesn’t in the preferences, so I think it’s going to keep happening more and more often.

                                                                                              1. 12

                                                                                                Thanks, but I don’t use Firefox anymore, nor install it, except for occasional testing.

                                                                                                Why?

                                                                                                1. 10

                                                                                                  You should probably create an “Ask Lobsters: Why don’t you use Firefox anymore?” thread. Otherwise we’ll just end up hijacking this thread with off topic information.

                                                                                                  1. 4

                                                                                                    In my opinion, outside of internal technologcal advances, which are many, Firefox has become Google’s lackey, and most of their UI design decisions are led by Chrome. I don’t like what they’re doing with the UI, and I don’t like how there is a shitton of “telemetry”; I am neither skilled enough, nor dedicated enough to disable all of it.

                                                                                                    Also, I think it’s not the best idea to use it from a security standpoint, due to it being in the top 3 most popular browsers.

                                                                                                    1. 1

                                                                                                      The same can be said for any flavor of chromium, what are you using where you feel comfortable in the amount of data collection without changing default settings regarding telemetry and error collection?

                                                                                                      1. 1

                                                                                                        I’m using qutebrowser, which, out of the box, by default, collects and sends no personal data to anyone.

                                                                                                        Nor does it even have features to do this.

                                                                                                        The only “telemetry” is the optional crash report, which I am given the option to send on a crash.

                                                                                                        I think it is illustrative of the sad state of affairs in the browser world that you expect a browser to send personal data to the developer by default.

                                                                                                        And let’s not beat around the bush here. “Telemetry” is just a deceptive way of saying “personal data”.

                                                                                                        Anonymization is bullshit, and all kinds of personal details can be recovered from “anonymized” “telemetry” data sent to Google, Mozilla, Apple, Brave, and whoever else.

                                                                                                        Also, I don’t think that Ungoogled collects personal data, so what you said is not entirely true.

                                                                                                        1. 2

                                                                                                          Fair, I use Firefox mainly because I could not find a browser that could handle my day-today development needs outside of a Chromium branch and Firefox. I have really come to enjoy Firefox containers because I do have several identities that are cookie based and using the containers to divide sessions has worked very well. I’ll take a look at the couple browsers you mentioned. Thanks

                                                                                                  2. 2

                                                                                                    There are greasemonkey scripts if you’re using a browser that supports using those (I use one with the browser, luakit, I use fulltime)

                                                                                                    1. 1

                                                                                                      Thank you for telling me that.

                                                                                                      My browser does support GreaseMonkey scripts, from what I know.

                                                                                                      I am not motivated enough to figure out how to install them, nor am I going to try to fix broken websites. I’d rather just avoid them, because I think they are a waste of my time.

                                                                                                    2. 2

                                                                                                      there’s also a chrome extention if you’ve heard of that browser

                                                                                                      1. 3

                                                                                                        I have heard quite a bit about Chrome, and that’s why it’s not allowed to be installed on my computers.

                                                                                                        1. 3

                                                                                                          holding out for edge on linux i see

                                                                                                          1. 3

                                                                                                            haha!

                                                                                                            Yes, I heard through the grapevine Microsoft will be releasing a completely FOSS Edge for Linux with no telemetry any day now.

                                                                                                  3. 7

                                                                                                    I am always fascinated by these antiquated interfaces, such as youtube’s &disable_polymer=1, m.facebook.com, text.npr.org, lite.cnn.io. They limit spying opportunities greatly and often allow being free of javascript and still have a worthwhile experience. Interestingly, none of these services offer official RSS despite making sites much more scrapable.

                                                                                                    Probably shouldn’t talk about it, cause they start taking them down for reasons mentioned above :).

                                                                                                    1. 14

                                                                                                      I always have a suspicion that these kind of “antiquated” interfaces as you call them might actually exist when the developers of the system like to use the system but dislike all the crap that marketing and sales makes them put into the UI. You know, to have a usable system for themselves.

                                                                                                      1. 3

                                                                                                        I agree. I think mbasic is kept around because otherwise a significant portion of users would quit Facebook.

                                                                                                      2. 7

                                                                                                        I’m surprised there is a m.facebook.com, because Facebook has had a mbasic.facebook.com that’s even more stripped down.

                                                                                                        1. 5

                                                                                                          When you have a database, you can build many different interfaces for the same data.

                                                                                                          For example, have you heard of Facebook API?

                                                                                                          Also, there’s 0.facebook.com. Facebook pays ISPs in poor areas to provide free access to it.

                                                                                                        2. 4

                                                                                                          such as youtube’s &disable_polymer=1,

                                                                                                          Do you just append this to the URL? It doesn’t seem to change anything on my system.

                                                                                                          1. 7

                                                                                                            I think that has been removed now. It used to do something and I even have a Firefox plugin that automatically adds it, since the Polymer version used to work really badly in Firefox.

                                                                                                            1. 5

                                                                                                              It sadly seems to be true, a shame.

                                                                                                              You can try out idiotbox.codemadness.org and invidio.us for unofficial solutions.

                                                                                                            2. 4

                                                                                                              I don’t think they’re antiquated, I think accessibility is a modern concept, and they work very hard to keep them accessible.

                                                                                                              I think mbasic is way more impressive than www.facebook.com.

                                                                                                              1. 3

                                                                                                                It is a joke from perspective of the ‘modern’ design. I think they are the most impressive and probs the best solutions for people who care from usable and accessible sites.

                                                                                                            3. 1

                                                                                                              I do, and I do.

                                                                                                              There’s a setting in the global preferences that works almost always, except on unmasked mobile.

                                                                                                            1. 6

                                                                                                              Go’s treating of errors as first-class citizens is, by far, my favorite way of handling errors. Most of my other work is done in TypeScript, and it’s absolutely bonkers that we’re spending all this time typing functions and return types and the like, but when it comes to errors, we basically throw everything about type checking out the window. Obviously TypeScript can only handle so much given the choices in JavaScript, but it still feels like a huge missing opportunity for the language.

                                                                                                              It’s actually gotten to the point where I’m considering making all my functions return tuple types, with a nullable error component just like Go:

                                                                                                              type Err = Error | null;
                                                                                                              
                                                                                                              async function getUser(): Promise<[string, Err]> {
                                                                                                                  try {
                                                                                                                      const res = await fetch('/me');
                                                                                                                      const data = await res.json();
                                                                                                                      return [data, null];
                                                                                                                  } catch (err) {
                                                                                                                      return ["", err];
                                                                                                                  }
                                                                                                              }
                                                                                                              
                                                                                                              (async () => {
                                                                                                                  const [user, err] = await getUser();
                                                                                                                  if (err !== null) {
                                                                                                                      console.error(err);
                                                                                                                      return;
                                                                                                                  }
                                                                                                              
                                                                                                                  console.log(user);
                                                                                                              })();
                                                                                                              
                                                                                                              1. 3

                                                                                                                Someone once said to me they thought java style checked exceptions would have been ok, if they were inferred via type inference.

                                                                                                                1. 7

                                                                                                                  The problem with Java isn’t checked exceptions, it’s that exceptions are used for stuff that’s not exceptional, like not finding a result. If exceptions weren’t being used where they shouldn’t, it’d annoy people far less than it does.

                                                                                                                  1. 1

                                                                                                                    I’m not aware of anything that Java-style checked exceptions can do that you can’t do with result types in a language with ML-style types.

                                                                                                                    1. 2

                                                                                                                      The only thing I can think of is that Java kinda let’s you combine unrelated exception types, while at least in Rust you would have to define an enum for it.

                                                                                                                      Plus, Rust is kinda bad with enums, because the individual enum members are not real types on their own, so you can’t say “only this specific error case of this enum can happen”.

                                                                                                                      But on the other hand, Java’s throws clause is basically a union, with exception supertypes mentioned in the throws clause subsuming subtypes, so they can accidentality be thrown away while coding.

                                                                                                                      1. 3

                                                                                                                        The only thing I can think of is that Java kinda let’s you combine unrelated exception types, while at least in Rust you would have to define an enum for it.

                                                                                                                        In OCaml you can combine result types with polymorphic variants which work like a set of constructors and are combined and type-inferred by the compiler. Here’s an explanation and so far it seems to work pretty well. Basically, composable checked exceptions in ML.

                                                                                                                        1. 3

                                                                                                                          Really cool, thanks for letting me know this!

                                                                                                                  2. 3

                                                                                                                    I totally feel your frustration with errors, but I think the tuple approach puts too much faith in the developer not making a mistake. Do you know if Go prevents returning both an error and value?

                                                                                                                    I would probably go with something like this:

                                                                                                                    type Result<T> = ({success: true} & T) | {success: false, err: Error}
                                                                                                                    
                                                                                                                    async function getUser(): Promise<Result<{user: string}>> {
                                                                                                                        try {
                                                                                                                            const res = await fetch('/me');
                                                                                                                            const user = await res.json() as string;
                                                                                                                            return {success: true, user};
                                                                                                                        } catch (err) {
                                                                                                                            return {success: false, err}
                                                                                                                        }
                                                                                                                    }
                                                                                                                    
                                                                                                                    (async () => {
                                                                                                                        const result = await getUser();
                                                                                                                    
                                                                                                                        // You can only access result.success here.
                                                                                                                    
                                                                                                                        if (!result.success) {
                                                                                                                            // Since success === false in this block, err can be accessed
                                                                                                                            console.error(result.err);
                                                                                                                            return;
                                                                                                                        }
                                                                                                                    
                                                                                                                    
                                                                                                                        // Because we returned when success === false, TypeScript knows that this
                                                                                                                        // has {success: true; user: string}.
                                                                                                                    
                                                                                                                        console.log(result.user);
                                                                                                                    })();
                                                                                                                    
                                                                                                                    
                                                                                                                    1. 4

                                                                                                                      Do you know if Go prevents returning both an error and value?

                                                                                                                      Of course not! It’s not a bug, it’s a feature.

                                                                                                                      1. 1

                                                                                                                        Do you know if Go prevents returning both an error and value?

                                                                                                                        No, and that’s not really a bad thing IMHO. For example I wrote an email address parsing library which can return an error but also returns the list of email addresses it was able to parse, which is useful in various situations (e.g. “sent email to X, but wasn’t able to send to Y”).

                                                                                                                        In general, you probably shouldn’t return a value and an error in most cases though.

                                                                                                                        1. 7

                                                                                                                          That sounds like a problem waiting to happen. Like a C function that processes half your data and admittedly returns -1 but if you forget to check you might just go ahead and use the half-processed data thinking you’re done (e.g. who will ever notice that only 999 addresses were processed, not 1000 until 3 months later you’re wondering why there is a large discrepancy in what you excepted and what you got at which point it will be too late).

                                                                                                                          1. 1

                                                                                                                            Well the solution to that is to not forget to check it 🙃 But yeah, I get your point. Perhaps a better way would be to return an error with the mail addresses on the error struct, so you need to do something with the error, and if you’re sure you want to get the email addresses anyway you can still get them from the error 🤔 This way it’s at least explicit.

                                                                                                                            1. 2

                                                                                                                              Alternatively you can make that particular failure case (a partial success) not an error and return some kind of state struct where for each address it would state whether it was able to send it or not individually. Since there was some kind of success presumably. This is a discussion of API design, I am not particularly familiar with how that’s preferred to be done in Go, I just try to design APIs that prevent the user from shooting themselves in the foot.

                                                                                                                              1. 1

                                                                                                                                I just try to design APIs that prevent the user from shooting themselves in the foot.

                                                                                                                                Yeah, that’s a good attitude. In this particular case you really needed to ignore errors on multiple levels for things to really go wrong. I don’t remember the exact details off-hand as I wrote this over 4 years ago (and has been running pretty much bug-free in production ever since processing millions of emails daily, which is actually not a bad accomplishment considering there were many issues before) but I would perhaps do it different if I were to write it today, I was fairly new to Go at the time 😅

                                                                                                                                Generally speaking though the mantra of “check your errors!” in Go is strong enough that you can somewhat rely on it; you can run in to problems if you ignore errors and return empty data too.

                                                                                                                            2. 1

                                                                                                                              There are linters that tell you if you forgot to check.

                                                                                                                        2. 1

                                                                                                                          Not sure if you like it but you can use the io-ts-promise library to chain async operations together like this:

                                                                                                                          fetch('http://example.com/api/not-a-person')
                                                                                                                            .then(response => response.json())
                                                                                                                            .then(tPromise.decode(Person))
                                                                                                                            .then(typeSafeData =>
                                                                                                                              console.log(`${typeSafeData.name} is ${typeSafeData.age} years old`),
                                                                                                                            )
                                                                                                                            .catch(error => {
                                                                                                                              if (tPromise.isDecodeError(error)) {
                                                                                                                                console.error('Request failed due to invalid data.');
                                                                                                                              } else {
                                                                                                                                console.error('Request failed due to network issues.');
                                                                                                                              }
                                                                                                                            });
                                                                                                                          
                                                                                                                        1. 10

                                                                                                                          A couple thousand people got screwed over several decades ago by the British Government for geopolitical reasons therefore you shouldn’t buy the .io TLD? I don’t think that’s reasonable. .io is a useful de-facto gTLD with explicit tech connotations unlike .xyz, and as stated in the articles linked through by the OP a proportion of its profits are reinvested in internet infrastructure. Some things are worthy of boycotting, but this is not one of them.

                                                                                                                          1. 44

                                                                                                                            There’s a difference between “I urge people to reconsider” and “You’re a moron if you do this”. The author did the former, not the latter.

                                                                                                                            Everyone is free to boycott what they want.

                                                                                                                            1. 15

                                                                                                                              I agree, the OP is perfectly free to boycott .io if they so choose. And I am perfectly free to say that I think it’s hand-wringing and an over-reaction. On a site like lobste.rs, which is rarely overtly political, a skimming reader might think that there is a good technical reason to not choose .io, which as far as I know is not the case.

                                                                                                                              1. 20

                                                                                                                                And I am perfectly free to say that I think it’s hand-wringing and an over-reaction

                                                                                                                                I won’t try and convince you otherwise :)

                                                                                                                                might think that there is a good technical reason to not choose .io, which as far as I know is not the case

                                                                                                                                Well….. [1]

                                                                                                                                Also, the future of the TLD being uncertain due to these geopolitical issues is quite a technical reason not to choose .io [2] (I added an update about this to the post)

                                                                                                                                [1] https://thehackerblog.com/the-io-error-taking-control-of-all-io-domains-with-a-targeted-registration/ [2] https://www.prolificlondon.co.uk/marketing-tech-news/tech-news/2019/05/future-popular-io-domains-question-over-british-empire-row

                                                                                                                                1. 5

                                                                                                                                  That first link was an interesting read, thanks! Good thing I’m too tight to buy .io domains anyway :p

                                                                                                                                2. 4

                                                                                                                                  There are technical reasons too: it’s a poorly run registry with bad record of nameserver uptime. The whole thing is held together with chewing gum and gaffer tape. I know this because of the “fun” I had going through ICB’s registrar accreditation process, where I spent most of the time getting them to fix bugs on their side.

                                                                                                                                  1. 3

                                                                                                                                    Wait, I’ve thought this whole time that lobste.rs was a political wing in Kosovo/Serbia! /s

                                                                                                                                3. 42

                                                                                                                                  A couple thousand people got screwed over several decades ago

                                                                                                                                  Those people are still being screwed over, today. Denied their homeland, they are forced to remain stateless. Their buried dead lie in graves untended, their lands appropriated for CIA Black Sites and USAF weapons of mass destruction.

                                                                                                                                  .io is a useful de-facto gTLD

                                                                                                                                  It isn’t a gTLD, its a cock-a-mamey “ccTLD” run for the benefit of the same people who have stolen the Chagossians homeland.

                                                                                                                                  Some things are worthy of boycotting, but this is not one of them.

                                                                                                                                  What would be worthy of boycotting, O Sage of the Internet?

                                                                                                                                  1. 5

                                                                                                                                    I’m pretty sure tech firms have been treating it as essentially a gTLD for quite a while now.

                                                                                                                                    What would be worthy of boycotting

                                                                                                                                    China for atrocities against Uyghur Muslims, or Saudi Arabia for atrocities in Yemen. Nations and entities which have murdered people en-masse instead of just displacing them.

                                                                                                                                  2. 29

                                                                                                                                    A couple thousand people got screwed over several decades ago by the British Government for geopolitical reasons therefore you shouldn’t buy the .io TLD? I don’t think that’s reasonable.

                                                                                                                                    I’m surprised (perhaps disappointed) you don’t think that’s reasonable. I can’t think of many better reasons to boycott something. You say “a couple thousand people” as though that’s a number that should be treated as insignificant.

                                                                                                                                    1. 4

                                                                                                                                      You say “a couple thousand people” as though that’s a number that should be treated as insignificant.

                                                                                                                                      The suffering of one person is a tragedy, the suffering of “a couple thousand people” is a statistic, right? Considering how shady this whole thing is I think it is completely fair to just vote with your wallet and chose a different TLD at no inconvenience to yourself.

                                                                                                                                    2. 14

                                                                                                                                      A couple thousand people got screwed over several decades ago by the British Government for geopolitical reasons therefore you shouldn’t buy the .io TLD? I don’t think that’s reasonable

                                                                                                                                      You probably would revise your position if you were one of those couple thousand who had a foreign colonial government stealing your wealth.

                                                                                                                                      Beyond the problematic colonist mindset here, there are actual technical reasons, well documented at this point, and with a little google fu you can find quite a few horror stories.

                                                                                                                                      1. 12

                                                                                                                                        I don’t like using .io simply because it’s supposed to be a geography-specific domain. It’s abusing the spec to use it as a trendy tech domain, IMO.

                                                                                                                                        1. 10

                                                                                                                                          Well, there’s .tech, .computer, .systems. So why .io? Just because it means “input/output”? If I make a techy website, why do I need my domain to be associated with “input/output”? Also, few people outside tech will get the .io reference. To others, it’s just confusing.

                                                                                                                                          There’s also the “future of io” issue: https://www.prolificlondon.co.uk/marketing-tech-news/tech-news/2019/05/future-popular-io-domains-question-over-british-empire-row Who knows what will happen if the Mauritian government gets ownership of the TLD.

                                                                                                                                          1. 4

                                                                                                                                            The problem with long, unusual TLDs like those ones is that they don’t immediately parse as URLs when one reads them - in fact, less technical people may not even realize they are URLs at all (I accept this may be a feature not a bug :p). They also break a lot of field validators. You are to some extent right about non-technical people not associating IO with input/output - I think many consider the domain synonymous with those simple online multiplayer games in the vein of agar.io.

                                                                                                                                            Is there a better source for the claim that .io would pass to the Mauritians if the UK ceded the Chagos islands (which I doubt will happen, but that’s beside the point)? Cynically, I am inclined to think that this will not happen.

                                                                                                                                            1. 6

                                                                                                                                              Cynically, I am inclined to think that this will not happen.

                                                                                                                                              On your side of the fence as well. I’d like to see it happen, but… UK giving up a source of money like that?

                                                                                                                                              In the end, it’s risk assessment. If you feel your brand perception will improve by using .io and the chances of .io going “territory only” mode are slim, no one is stopping you.

                                                                                                                                              It’s difficult, but I’m trying to not to judge. What other people do with their domains is their business. I’m really only asking to take these issues into consideration when buying a domain.

                                                                                                                                          2. 3

                                                                                                                                            This is exactly the kind of libertarian nonsense that seems to plague lobsters these days.

                                                                                                                                            1. 5

                                                                                                                                              Nothing about his comment in any way pertains to libertarianism or libertarian philosophy.

                                                                                                                                          1. -1

                                                                                                                                            Can anyone recommend some material describing concrete motivations for adding generics to Go? I’m aware of the abstract idea that you need generics in order to build data structures that work with different types, but is there a real-world setting where this is actually better than just copying code and doing s/type1/type2/g? My sense is that projects that use complex data structures almost always customize the data structures in a way that depends on the data type being stored.

                                                                                                                                            1. 19

                                                                                                                                              I hope it’s not that hard to imagine wanting different data structures than hash maps; maybe your problem fits better into a binary search tree for example.

                                                                                                                                              Well, I for one usually don’t feel like implementing my own red-black tree, so I would like to just grab a library. That library will be much nicer to use if I can just make an RBTree<string, MyFoo>. I certainly wouldn’t want to copy/paste an int->string red-black tree into some file(s) and judiciously search/replace until I have a string->MyFoo tree (and then do the same when I need an int->float tree).

                                                                                                                                              1. 0

                                                                                                                                                That makes sense, but I am still looking for some grounding in actual programming practice. Is there a use of a red-black tree that would not warrant customizing it for the storage type? Or one where it would make sense to add a library dependency rather than copying the RB tree code?

                                                                                                                                                1. 7

                                                                                                                                                  How do you write a library that provides a Red-Black tree that can in principle work with many different client types without generics? This isn’t a rhetorical question, I don’t know Go and I genuinely don’t know how you would implement this kind of library in Go without generics.

                                                                                                                                                  1. 6

                                                                                                                                                    Go’s sync.Map (concurrent hashmap) is an actual real world example of this, and it uses interface{}, akin to Java’s Object.

                                                                                                                                                    1. 24

                                                                                                                                                      Right, that’s a great example. Because it uses interface{}, it:

                                                                                                                                                      • Requires all keys and values to be heap allocated, leading to worse performance, worse memory usage, and worse memory fragmentation. Requiring two heap-allocated ints to store one value in an int->int concurrent hash map is unacceptable for many uses.
                                                                                                                                                      • Is less ergonomic, requiring a cast every time you want to use a value.
                                                                                                                                                      • Provides no type safety. (I imagine this one will be the least convincing to Go programmers, since Go generally expects the programmer to just not make mistakes)
                                                                                                                                                      1. 3

                                                                                                                                                        This brings me back to C++Builder 3 back in the 90s. To use a list, you had to create a class derived from a kind of TItem class to be able to store things. Why anyone would want to go back to that in productive code boggles my mind.

                                                                                                                                                        1. 1

                                                                                                                                                          I’m using a sync.Map (for its concurrency support - I have many goroutines writing map entries, and another goroutine periodically ranging over the entire map to dump it to a json file).

                                                                                                                                                          However I know the types I write to the map, I have no need for interface{}.

                                                                                                                                                          Am I better off with a real typed map + using sync.RWLock/mutex/etc. directly (in a custom struct)? Performance-wise.

                                                                                                                                                          1. 1

                                                                                                                                                            I don’t know, you would have to benchmark or measure CPU or memory usage. The sync.Map documentation suggests that using a regular map + mutexes could be better though: https://golang.org/pkg/sync/#Map

                                                                                                                                                            The Map type is specialized. Most code should use a plain Go map instead, with separate locking or coordination, for better type safety and to make it easier to maintain other invariants along with the map content.

                                                                                                                                                            The Map type is optimized for two common use cases: (1) when the entry for a given key is only ever written once but read many times, as in caches that only grow, or (2) when multiple goroutines read, write, and overwrite entries for disjoint sets of keys. In these two cases, use of a Map may significantly reduce lock contention compared to a Go map paired with a separate Mutex or RWMutex.

                                                                                                                                                            If your usage falls outside of the two use cases which sync.Map is optimized for, it would absolutely be worth looking into replacing your sync.Map with a regular map and a mutex.

                                                                                                                                                            I suppose it becomes a question of which has the biggest performance penalty for you, heap allocation + indirection with sync.Map or lock contention with regular map + mutex?

                                                                                                                                                            (Also, in most cases, this probably doesn’t matter; make sure you’re not spending a long time improving performance in a part of your code which isn’t actually a performance issue :p)

                                                                                                                                                            1. 1

                                                                                                                                                              Right - the code “just works(TM)” and it takes around 0.5 seconds to render the JSON file every minute (which I track with metrics just to be safe) so it should be fine to keep as is. This is just a for-fun conversation.

                                                                                                                                                              or (2) when multiple goroutines read, write, and overwrite entries for disjoint sets of keys. In these two cases, use of a Map may significantly reduce lock contention compared to a Go map paired with a separate Mutex or RWMutex.

                                                                                                                                                              I definitely remember reading this sentence and it made me choose sync.Map because it sounds like my usecase. But like you say if I don’t measure it’ll be hard to tell.

                                                                                                                                                      2. -1

                                                                                                                                                        I don’t know and I didn’t think you could. I’m asking for an example use of an RB tree where using a library would make sense.

                                                                                                                                                        1. 6

                                                                                                                                                          Here is a popular Go RB tree implementation https://github.com/emirpasic/gods/ that uses Interface{} for the key and value types. Just search github for uses of it… With generics, users of this library would get greater typesafety.

                                                                                                                                                          https://github.com/search?q=%22github.com%2Femirpasic%2Fgods%2Ftrees%2Fredblacktree%22&type=Code

                                                                                                                                                          1. -2

                                                                                                                                                            okay. except i don’t know how to search github for uses of it and your search link brings me to a login page :(

                                                                                                                                                            1. 4

                                                                                                                                                              To short-circuit this:

                                                                                                                                                              At a previous job, I worked on a tool that started various services. The services had different dependencies, each of which needed to be started before the service. We wanted to be able to bring them up with as much parallelism as possible, or have a flag to launch them serially.

                                                                                                                                                              A simple approach to doing this correctly is modeling the dependencies as an acyclic graph (if it’s a cyclic graph, you’ve got a problem — you can never bring the services up, because they all depend on each other). To launch them in parallel, launch each one that has its dependencies met. To launch them serially, topologically sort the graph into an array/list/whatever and launch them one by one.

                                                                                                                                                              A generic graph implementation would be very useful, as would a topological sort that worked on generic graphs. With Go, you can’t have one that’s type-safe.

                                                                                                                                                              Another great use case for graphs: visualizing dependency graphs! You can have an open source graph visualization library, build a graph of whatever it is you’re trying to visualize, and pass it to the library and get a nice visualization of the data.

                                                                                                                                                              Graph data structures can be quite useful. Supporting generics makes them type-safe, so you catch errors at compile time instead of runtime. Some other examples of the usefulness of graphs:

                                                                                                                                                              • Graphs of friends at a social network (I currently work at one, and we use generic graph data structures all over the place — graphs of people to people, graphs connecting people and photos they’re tagged in, etc)
                                                                                                                                                              • Network topology graphs
                                                                                                                                                              • Graphs of links between documents

                                                                                                                                                              etc.

                                                                                                                                                              And it’s not just graphs. How do you write a type-safe function that takes in a list of possibly-null items, and returns a new list with the nulls stripped out? How about a function that takes a map and returns the list of its keys? In Golang, the answer is always copy-paste or give up type safety. In languages with generics, you can trivially write these functions yourself if they’re not in the standard library.

                                                                                                                                                              1. 1

                                                                                                                                                                thanks, this is a good motivating example.

                                                                                                                                                                1. 1

                                                                                                                                                                  Huh. It had not occurred to me that github search would require a login.

                                                                                                                                                      3. 11

                                                                                                                                                        To turn the question around, why would you want to manually copy/paste code all over the place when the compiler can do it for you? And while I personally think “DRY” can be over done, not having the same (or very similar) code copy/pasted all over the place seems like a big practical win.

                                                                                                                                                        As far as customizing specific data structure and type combinations, most languages with generics have a way to do that, and I’d bet the Go designers thought of it.

                                                                                                                                                        1. 2

                                                                                                                                                          Copy / paste has got it’s own problems, but it lets you avoid a ton of complexity in the toolchain.

                                                                                                                                                          Toolchain development is all about tradeoffs. For instance, I use Typescript; the reference implementation is featureful, but slow to boot, so it keeps a background process alive to cache the heavy lifting, which accumulates state and introduces subtle confusions (eg type errors that don’t exist) until it’s restarted.

                                                                                                                                                          For some problem spaces, the problems introduced by copy/paste pale in comparison to the problems introduced by slow, stateful compilers.

                                                                                                                                                          1. 7

                                                                                                                                                            Copy/paste vs generics is unrelated to compiler bugginess.

                                                                                                                                                            If you carefully pick TypeScript as the comparison point, you can make the case that a buggy toolchain is bad (not that most users care, they just restart the compile process when it starts to go bad).

                                                                                                                                                            But if you were to pick say ReasonML for comparison, you could say that it’s possible to have a solid generics implementation (much less copy-pasting) and a super-fast, accurate compiler.

                                                                                                                                                            I.e. you can have both buggy and non-buggy compilers supporting generics. Hence, unrelated.

                                                                                                                                                            1. 2

                                                                                                                                                              ReasonML is great!

                                                                                                                                                              That said, while the relationship is indirect, it’s there. Adding complexity is never free. It didn’t cost ReasonML speed or reliability, but it costs maintainers time and makes every other feature more difficult to add in an orthogonal way.

                                                                                                                                                              1. 2

                                                                                                                                                                In the scheme of things, is it more important to have a super-simple compiler codebase, or is it more important to put more power and expressiveness in the hands of users? Note that every mainstream language that started without generics, has now added it.

                                                                                                                                                                1. 1

                                                                                                                                                                  IMO, there’s such a thing as a right time to do it.

                                                                                                                                                                  In the early years it’s more important to keep the simplicity - there aren’t that many users and you’re still figuring out what you want the language to be (not every feature is compatible with every approach to generics).

                                                                                                                                                                  Once you’re ready to start generics you need to answer questions like - do you want monomorphisation or lookup tables? Is boxing an acceptable overhead for the improved debugging ergonomics?

                                                                                                                                                                  1. 1

                                                                                                                                                                    It seems like Go has been going through exactly the process you’re describing.

                                                                                                                                                                2. 2

                                                                                                                                                                  I think these comparisons are a bit unfair: isn’t Typescript self hosted, whereas ReasonML is written in OCaml? It seems like Typescript would have a very hard time competing.

                                                                                                                                                                  1. 3

                                                                                                                                                                    Being able to use lots of existing OCaml bits is a huge advantage.

                                                                                                                                                                    Typescript has been able to compete due to the sheer number of contributors - MS pays quite a large team to work on it (and related stuff like the excellent Language Server Protocol, VScode integration).

                                                                                                                                                                    However, large teams tend to produce more complex software (IMO due to the added communications overhead - it becomes easier to add a new thing than find out what existing thing solves the same problem).

                                                                                                                                                                    1. 1

                                                                                                                                                                      I should clarify my comment was more about comparing performance of the two languages.

                                                                                                                                                                      OCaml is a well optimized language that targets native machine code so tooling built in OCaml should be more performant than tooling built in Typescript. As a result, it’s hard to compare the complexity of either tool by the performance of the tool. It’s apples and oranges.

                                                                                                                                                                    2. 2

                                                                                                                                                                      isn’t Typescript self hosted, whereas ReasonML is written in OCaml? It seems like Typescript would have a very hard time competing.

                                                                                                                                                                      That’s a strange argument. If it were very hard for them to compete why would they not use OCaml as well, especially since its contemporary alternative, Flow, was written in OCaml too? Or why would they not make TypeScript as good as a language for writing TypeScript in as OCaml is?

                                                                                                                                                                      1. 1

                                                                                                                                                                        My comment was more about performance, but it wasn’t very clear. It’s hard for Typescript, which is compiled to Javascript and then interpreted/JITed, to create tooling that’s as fast as a language that builds optimized native code.

                                                                                                                                                                        Given that Typescript is self hosted it has the advantage that community involvement is more seamless and I don’t want to downplay the power that brings.

                                                                                                                                                                  2. 0

                                                                                                                                                                    But compilers that support generics are more likely to be buggy. That’s a relation.

                                                                                                                                                                    1. 2

                                                                                                                                                                      Any source for this rather surprising assertion?

                                                                                                                                                                      1. 0

                                                                                                                                                                        generics are feature that requires code to implement; code can contain bugs.

                                                                                                                                                                        1. 1

                                                                                                                                                                          But a self-hosting compiler with generics is likely to be less verbose (because generics) than one without, so it should be less buggy.

                                                                                                                                                                          1. 1

                                                                                                                                                                            i guess you can’t prove it either way but IME the complexity of algorithms is more likely to cause bugs than verbosity.

                                                                                                                                                                  3. 5

                                                                                                                                                                    I think Typescript is a straw man. Does this Go implementation of generics slow down the compiler a noticeable amount? There’s nothing inherent to generics that would make compiling them slow.

                                                                                                                                                                    On the other hand, copy/pasted code is an ever increasing burden on developer and compile time.

                                                                                                                                                                  4. -2

                                                                                                                                                                    You are imagining a code base where the same complex data structure is instantiated with two different types. Is that realistic?

                                                                                                                                                                    1. 5

                                                                                                                                                                      You are imagining a code base where the same complex data structure is instantiated with two different types. Is that realistic?

                                                                                                                                                                      Realistic enough that the Linux kernel developers went through the hassle of developing generic associative arrays, circular buffers, and other generic data structures using void*.

                                                                                                                                                                      And so did Gnome with GLib, which provides generic lists, hash tables, and trees, along with several others structures, also using void*.

                                                                                                                                                                      And the standard libraries of most modern languages include reusable and generic sequence and associative data types, and some times significantly more than that.

                                                                                                                                                                      For most data structures, though, focusing on a single code base gives too narrow of a view. Generics allow libraries of data structures to be created, so even though a single code base only use one R* tree (or whatever), that R* tree library can be used as-is by any number of projects.

                                                                                                                                                                  5. 8

                                                                                                                                                                    The Abstract and Background sections of the draft design doc touch on the motivations. Additionally, each section describing a dimension of the design usually mentions, at least briefly, the motivation for that feature.

                                                                                                                                                                    1. 8

                                                                                                                                                                      Here is an example that I’ve wanted for ever, and can finally do. Higher order combinators that you can leverage first class functions with!

                                                                                                                                                                      Generic map, in go

                                                                                                                                                                      1. 1

                                                                                                                                                                        That’s the type of thing I have seen as a justification, but I don’t get why that’s so important. Can’t you just use a for loop?

                                                                                                                                                                        1. 22

                                                                                                                                                                          “Can’t you just …” goes forever. “Can’t you just write your for loop with labels and jumps in assembly?”^^

                                                                                                                                                                          For me, it’s all about abstraction. Having low level combinators, like this, that I can compose to build higher level abstractions in a generic way is wonderful.

                                                                                                                                                                          ^^: See also whataboutism.

                                                                                                                                                                          1. 3

                                                                                                                                                                            I’m not sure that composing from higher level abstractions is always such a good idea. I like both Go (hobby projects) and Rust (work!) but I still fell that most of the time I prefer this level of abstraction:

                                                                                                                                                                            type Server struct {
                                                                                                                                                                            ...
                                                                                                                                                                                Handler Handler // handler to invoke, http.DefaultServeMux if nil
                                                                                                                                                                            ...
                                                                                                                                                                            }
                                                                                                                                                                            type Handler interface {
                                                                                                                                                                                ServeHTTP(ResponseWriter, *Request)
                                                                                                                                                                            }
                                                                                                                                                                            

                                                                                                                                                                            from this:

                                                                                                                                                                             pub fn serve<S, B>(self, new_service: S) -> Server<I, S, E>
                                                                                                                                                                                where
                                                                                                                                                                                    I: Accept,
                                                                                                                                                                                    I::Error: Into<Box<dyn StdError + Send + Sync>>,
                                                                                                                                                                                    I::Conn: AsyncRead + AsyncWrite + Unpin + Send + 'static,
                                                                                                                                                                                    S: MakeServiceRef<I::Conn, Body, ResBody = B>,
                                                                                                                                                                                    S::Error: Into<Box<dyn StdError + Send + Sync>>,
                                                                                                                                                                                    B: HttpBody + 'static,
                                                                                                                                                                                    B::Error: Into<Box<dyn StdError + Send + Sync>>,
                                                                                                                                                                                    E: NewSvcExec<I::Conn, S::Future, S::Service, E, NoopWatcher>,
                                                                                                                                                                                    E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
                                                                                                                                                                                {
                                                                                                                                                                                ...
                                                                                                                                                                            

                                                                                                                                                                            Don’t get me wrong, i like type level guarantees and I can see flexibility here, but in my experience with c++, rust and haskell is that generic programming often ends up complicating things to a degree that I personally don’t like.

                                                                                                                                                                            1. 1

                                                                                                                                                                              I think this is going to be a balance that the community has to find. I don’t regularly program in rust, but I’d be quite surprised if it wasn’t possible to get something close to the Go http API in it. The example you pasted seems complicated for the sake of being complicated. In theory, the Go community has been drilled into thinking in terms of the littlest abstraction that’ll work, which maybe makes it possible to generally avoid generic APIs that don’t actually need to be?

                                                                                                                                                                            2. 3

                                                                                                                                                                              “Can’t you just” does not go forever. It is a simpler way to say that the alternative is not significantly harder than what’s proposed. Is there some type of task that would be doable using a generic map but unreasonably hard using for loops?

                                                                                                                                                                              I feel like Go was designed from the ground up to be written in an imperative style, and composing first order functions is more of a functional style of coding. If I understand, without generics you would be nesting for loops rather than composing map functions, which is no more difficult to understand or write.

                                                                                                                                                                              I don’t follow the connection to whataboutism.

                                                                                                                                                                              1. 2

                                                                                                                                                                                I think it’s fine for your style of writing code to be to use loops and conditionals instead of map and filter. I think it’s a fine way to code that makes more sense in an imperative language. Straight for loops and while loops with if statements inside them is just better, more easily understandable code in an imperative language, in my opinion, than .map(...).filter(...).map(...) etc.

                                                                                                                                                                                1. -1

                                                                                                                                                                                  Incidentally there is a repo wherein Rob Pike expresses his attitude towards this style of coding:

                                                                                                                                                                                  https://github.com/robpike/filter/

                                                                                                                                                                                  I wanted to see how hard it was to implement this sort of thing in Go, with as nice an API as I could manage. It wasn’t hard.

                                                                                                                                                                                  Having written it a couple of years ago, I haven’t had occasion to use it once. Instead, I just use “for” loops.

                                                                                                                                                                                  You shouldn’t use it either.

                                                                                                                                                                                  1. 2

                                                                                                                                                                                    I mean… that’s like … one man’s opinion… man. See also.

                                                                                                                                                                                    Generics are going to create a divide in the Go community, and it’s going to be popcorn worthy. There’s no point of adding Generics to the language if this filter thing “shouldn’t be used,” and the community rejects the abstractions that Generics provide.

                                                                                                                                                                                    This divide is easily already seen in the community as it relates to test helpers. On the one hand, there’s a set of developers that say “stdlib testing is more than enough.” On the other hand, there are people who want the full testing facilities of junit, with matchers, lots of assert style helpers, etc. Who is right? They all are, because those things work for their respective teams and projects.

                                                                                                                                                                                    This general dogmatic approach to language idioms is why I call it “idiotmatic” Go.

                                                                                                                                                                                    1. -1

                                                                                                                                                                                      I suppose if Ken and Rob wanted generics they would’ve put them in the original language, and there wouldn’t be this controversy. Time to go back to learning Erlang which seems old and dusty enough to not have big language changes and drama.

                                                                                                                                                                                2. 16

                                                                                                                                                                                  You can’t pass a for loop to anything, you can only write it where you need it. Sure, toy examples look like toy examples, but the fact remains that Go has first-class functions, which should be a nice thing, but it doesn’t actually have a type system rich enough to express 90% of the things that make first-class functions worth having.

                                                                                                                                                                                  1. -1

                                                                                                                                                                                    You can’t pass a for loop to anything, you can only write it where you need it.

                                                                                                                                                                                    right, so the example code could be done with a for loop no problem. is there a more motivating example?

                                                                                                                                                                                    it doesn’t actually have a type system rich enough to express 90% of the things that make first-class functions worth having.

                                                                                                                                                                                    how do you mean?

                                                                                                                                                                                  2. 3

                                                                                                                                                                                    Consider composing multiple transformations and filters together. With multiple for loops you have to iterate over the array each time, while by composing maps you only need to iterate once.

                                                                                                                                                                                    1. 3

                                                                                                                                                                                      Just compose the operations inside the loop.

                                                                                                                                                                                      for x in y:
                                                                                                                                                                                          ...f(g(x))...
                                                                                                                                                                                      
                                                                                                                                                                                      1. 2

                                                                                                                                                                                        That works in some cases, but it’s pretty easy to find a counter example, too.

                                                                                                                                                                                3. 7

                                                                                                                                                                                  In terms of collections, the truth is most of the time a map/slice is a good option. Here’s my top two favorite use cases for generics in go:

                                                                                                                                                                                  1. Result<T> and functions that compose over them.
                                                                                                                                                                                  2. Typesafe versions of sync.Map, sync.Pool, atomic.Value, even a rust like Mutex
                                                                                                                                                                                  1. 5

                                                                                                                                                                                    Oh man. I hadn’t even considered a better way to do error handling, eg. a Result type. People are gonna get so mad.

                                                                                                                                                                                    1. 5

                                                                                                                                                                                      Generics isn’t enough to do what people want to do with error handling 99.99% of the time, which is to return early. For that, you either need a macro, such as the aborted try proposal, or syntactic sugar for chaining such “functions that compose over them” (like Haskell’s do notation).

                                                                                                                                                                                      Otherwise you end up with callback hell à la JavaScript, and I think nobody wants that in Go.

                                                                                                                                                                                      1. 4

                                                                                                                                                                                        I was more thinking of something where the if err pattern is enforced via the type system. You’re still not getting there 100%, you could get reasonably close, with a generic Result type that panics when the wrong thing is accessed, forcing you to check always or risk a panic.

                                                                                                                                                                                        r := thing()
                                                                                                                                                                                        if r.HasError() { handleError(r.Err()) }
                                                                                                                                                                                        else v := r.Val() { handleSuccess(v) }
                                                                                                                                                                                        

                                                                                                                                                                                        And of course it’s easy to question why this is interesting until you do chaining of things, and get a full on, type safe Result monad.

                                                                                                                                                                                        r := thing().andThen(func(i int) { ... }).andThen(func(i int) { ... })
                                                                                                                                                                                        if r.IsErr() {
                                                                                                                                                                                           handleErrForWholeComputation(r.Err())
                                                                                                                                                                                        } else {
                                                                                                                                                                                           handleSuccessForWholeComputation(r.Val())
                                                                                                                                                                                        }
                                                                                                                                                                                        

                                                                                                                                                                                        The alternative can be seen in things like this where you skirt around the fact that you can’t generically accept a value in one of those called functions. This is also why I said people are going to get so mad. These things are confusing to people who haven’t dealt with them before, and will make Go much more expressive, but less easy to grok without effort.

                                                                                                                                                                                  2. 5

                                                                                                                                                                                    but is there a real-world setting where this is actually better than just copying code and doing s/type1/type2/g

                                                                                                                                                                                    All of them. Copying code manually is one of the worst things you can do in software development. If it weren’t, why even bother writing functions, ever?

                                                                                                                                                                                    My sense is that projects that use complex data structures almost always customize the data structures in a way that depends on the data type being stored.

                                                                                                                                                                                    The fact that libraries exist that don’t customise in such a way in languages with generics would disprove that notion.

                                                                                                                                                                                    1. 6

                                                                                                                                                                                      one of the worst things you can do in software development

                                                                                                                                                                                      For me that’s “making things unreadable for whoever comes after you”. And sometimes copying a bit of code is the optimal solution for avoid that.

                                                                                                                                                                                      1. 0

                                                                                                                                                                                        but is there a real-world setting where this is actually better than just copying code and doing s/type1/type2/g

                                                                                                                                                                                        All of them. Copying code manually is one of the worst things you can do in software development. If it weren’t, why even bother writing functions, ever?

                                                                                                                                                                                        I disagree with your implication that the use of functions means code should never be copied. For example if you want to use strlcpy() in a portable C program, it makes more sense to put a copy in your source tree rather than relying on an external library. An extra dependency would add more headache than just copying the code.

                                                                                                                                                                                        My sense is that projects that use complex data structures almost always customize the data structures in a way that depends on the data type being stored.

                                                                                                                                                                                        The fact that libraries exist that don’t customise in such a way in languages with generics would disprove that notion.

                                                                                                                                                                                        That’s why I said “almost always.” And remember that the existence of a library doesn’t mean it is used with any frequency.

                                                                                                                                                                                      2. 3

                                                                                                                                                                                        Suppose you have a structure parametrizable by types T1, T2. You’re writing it in Go, so you assume that it’s ok if T1=string, T2=int. Also, in some of the places, you were using int for purpose unrelated to T2 (ie. if T2=foo, then there are still ints left in the source). Another programmer wants to copy-paste the code and change some types. How does he do it?

                                                                                                                                                                                        1. 2

                                                                                                                                                                                          I think “this would make copy-pasting code harder” is not so compelling an argument. One of the major points of introducing generics is that it would eliminate much of the present need for copy/paste in Go.

                                                                                                                                                                                          1. 2

                                                                                                                                                                                            Yes it would be harder than a search-and-replace, but that is still abstract and unrelated to any real-world use case.

                                                                                                                                                                                            Yes, I’m just counterpointing the parent commenter’s argument. I know the value of generic structures.

                                                                                                                                                                                          2. -1

                                                                                                                                                                                            Yes it would be harder than a search-and-replace, but that is still abstract and unrelated to any real-world use case.

                                                                                                                                                                                        1. 6

                                                                                                                                                                                          I was under the impression that a lot of left-handed people just use a right-handed mouse; both my left-handed mother and brother do anyway. It’s just easier, especially if you have to use someone else’s computer and such.

                                                                                                                                                                                          At any rate, I really don’t like the whole “must hate left handed people” and “do left-handed people not matter to you?” angle. Trackballs are already a niche market, and left-handed trackballs are a niche within a niche. The economics of these kind of things are such that it’s just not worth making products for such a small market. I appreciate that this kinda sucks, but … it’s not “hating left-handed people”.

                                                                                                                                                                                          I once had a discussion with someone on the value of an alt tag for a certain image. I felt it had no value in this particular case and was better left blank. His reply? “You must hate blind people!” It wasn’t the kind of reasoning that made me more sympathetic to their argument, to put it mildly.

                                                                                                                                                                                          1. 4

                                                                                                                                                                                            I really don’t like the whole “must hate left handed people” and “do left-handed people not matter to you?” angle

                                                                                                                                                                                            Even worse since the blog author does not actually have an answer. The post does not provide any additional information to answer that claim beyond the headline.

                                                                                                                                                                                            1. 2

                                                                                                                                                                                              I’ve added some context to the post, as you’re right I wasn’t very clear. My issue is that it’s impossible to get a left handed ergo mouse from Logitech - trackerball or not. Right handed people get so much choice, but we’re left with the billy basic symmetrical mice for most of the time.

                                                                                                                                                                                              The title is also supposed to be facetious; I’m well aware that Logitech don’t hate left handed people. I thought that would have been obvious, clearly not though, so again I’ve added some context at the end of the post.

                                                                                                                                                                                              Thanks for the feedback. :-)

                                                                                                                                                                                              1. 2

                                                                                                                                                                                                Right handed people get so much choice, but we’re left with the billy basic symmetrical mice for most of the time.

                                                                                                                                                                                                Yeah, but that’s just an economical issue. For better or worse, the Free Market is not always good at catering to niche markets, leaving some groups consistently out in the cold (something I could write at length about, but that would get rather off-topic).

                                                                                                                                                                                                The title is also supposed to be facetious; I’m well aware that Logitech don’t hate left handed people. I thought that would have been obvious, clearly not though, so again I’ve added some context at the end of the post.

                                                                                                                                                                                                Even with the context, it just comes across as bitter and angry to me. I appreciate that this is perhaps not how you actually feel about it, but it is the message it gives.

                                                                                                                                                                                            1. 2

                                                                                                                                                                                              PostScript allows that and according to the creators this is deliberate to be able to patch buggy PostScript words in printers by allowing documents to redefine them with fixed versions.

                                                                                                                                                                                              1. 26

                                                                                                                                                                                                inheritance is something that makes your code hard to understand. Unlike function, which you can read just line by line, code with inheritance can play “go see another file” golf with you for a long time.

                                                                                                                                                                                                This isn’t an argument against inheritance, it’s an argument against modularity: Any time you move code out of inline you have the exact same “problem” (to the extent it is a problem) and you can only solve it the same way, with improved tooling of one form or another. ctags, for example, or etags in Emacs.

                                                                                                                                                                                                1. 31

                                                                                                                                                                                                  Inheritance has this problem to a much larger degree because of class hierarchies. Tracing a method call on a class at the bottom of the tree, requires checking every parent class to see if its overridden anywhere. Plain function calls don’t have that problem. Theres only a single definition.

                                                                                                                                                                                                  1. 7

                                                                                                                                                                                                    Plain function calls don’t have that problem. Theres only a single definition.

                                                                                                                                                                                                    Unless we start using higher order functions when the function is passed around as a value. Such abstraction creates the exact same problem, only now it’s called “where does this value originate from”.

                                                                                                                                                                                                    1. 5

                                                                                                                                                                                                      Yes, which is why higher order functions are another tool best used sparingly. The best code is the most boring code. The most debuggable code is the code that has the fewest extension points to track down.

                                                                                                                                                                                                      This is, of course, something to balance against debugging complicated algorithms once and reusing them, but it feels like the pendulum has swung too far in the direction of unwise extensibility.

                                                                                                                                                                                                      1. 4

                                                                                                                                                                                                        For extra fun, use higher-order functions with class hierarchies!

                                                                                                                                                                                                      2. 4

                                                                                                                                                                                                        The best is python code where the parent class can refer to attributes only created in child classes. There are equivalents, but less confusing, in languages like Java.

                                                                                                                                                                                                        1. 1

                                                                                                                                                                                                          Isn’t the example in the linked article doing exactly that?

                                                                                                                                                                                                          1. 2

                                                                                                                                                                                                            Okaaaay… what’s self.connect doing? Ah, it raises NotImplementedError. Makes sense, back to SAEngine:

                                                                                                                                                                                                            Not exactly. :)

                                                                                                                                                                                                            1. 1

                                                                                                                                                                                                              Check out Lib/_pyio.py (the Python io implementation) in CPython for lots of this.

                                                                                                                                                                                                          2. 1

                                                                                                                                                                                                            The overrides is mostly for modularity and reduce code duplication. Without classes, you might either end up with functions with tons of duplicated code, or tons of functions having a call path to simulate the “class hierarchies”. And yes, it’s going to make the code harder to read in some cases, but it also makes the code much shorter to read.

                                                                                                                                                                                                            1. 6

                                                                                                                                                                                                              Without classes, you might either end up with functions with tons of duplicated code

                                                                                                                                                                                                              Why? There is literally no difference in code re-using between loading code through inheritance vs function calls, apart from possibly needing to pass a state to a function, that could otherwise be held in class instances (aka objects). this is certainly less than class definition boilerplates.

                                                                                                                                                                                                              or tons of functions having a call path to simulate the “class hierarchies”

                                                                                                                                                                                                              The call chain is there in both cases. It’s just that in the class-based approach it is hidden and quickly becomes a nightmare to follow. Each time you call a method statically or access a class atribute, you are basically pointing to a point in your code that could be hooked to different points in the hierarchy. This is a problem. People don’t think it is a big deal when they write a simple class and know it well, because the complexity is sneaky. Add another class and all of the sudden you brought in a whole other hierarchy into the picture. Each time you read “this” or “instance.something”, you’re up for am hunt. And each other hierarchy you bring into the picture increases complexity geometrically. Before you know, the project is unmanageable, the ones writing it went on to some green field project, doing a similar mess again for some poor soul to struggle with after them.

                                                                                                                                                                                                              And yes, it’s going to make the code harder to read in some cases, but it also makes the code much shorter to read

                                                                                                                                                                                                              It doesn’t really. People fall for this because you can instantiate a class and get a bunch of hidden references that are all available to you at will without you need to explicitly pass them to each method, but you only get this through defining a class, which is way more verbose than just passing the references you need.

                                                                                                                                                                                                              All that said, what classes do offer in most languages is a scope that allows for fine grain control of data lifecycle. If we remove inheritance, then class members are akin to use global variables in non-OOP languages. But you can create as many scopes as you want. I which languages like python would do this as, for the same reason as OP, I suffer from working with OOP codebases.

                                                                                                                                                                                                              1. 4

                                                                                                                                                                                                                You make it sound like inheritance is the only way to reduce code duplication. In my experience that is simply not true, you can always use composition instead. E.g. Haskell doesn’t support inheritance or subtyping and you still get very compact programs without code duplication.

                                                                                                                                                                                                                1. 5

                                                                                                                                                                                                                  Without classes, you might either end up with functions with tons of duplicated code, or tons of functions having a call path to simulate the “class hierarchies”

                                                                                                                                                                                                                  This is only true in my experience if you’re trying a functional approach with an OO mindset. There are other ways to solve problems, and many of them are far more elegant in languages designed with functional programming as the primary goal.

                                                                                                                                                                                                              2. 5

                                                                                                                                                                                                                When you move a bit of code out of your file it’s not going to call back function from the first file. You going to even make sure this is the case, that there is no circular dependency, because it makes (in cases when a language allows to make you one) code harder to read. In case of inheritance, those games with calling everything around is just normal state of things.

                                                                                                                                                                                                                Of course, example in the article is small and limited, because pulling a monster from somewhere is not going to make it more approachable, but surely you’ve seen this stuff in the wild.

                                                                                                                                                                                                                1. 4

                                                                                                                                                                                                                  You might do that, in the same way that you might carefully document your invariants in a class that allows inheritance, mark methods private/final as needed, etc. But you also might not do that. It sounds a bit as if you’re comparing well-written code without inheritance to poorly written code with it.

                                                                                                                                                                                                                  Not that there isn’t lots of terrible inheritance based code. And I’d even say inheritance, on balance, makes code harder to reason about. However, I think that the overwhelming issue is your ability to find good abstractions or ways of dividing up functionality–the choice of inheritance vs. composition is secondary.

                                                                                                                                                                                                                  1. 2

                                                                                                                                                                                                                    It’s just that without inheritance it’s easier to make good abstractions. Inheritance affords you to do wrong thing easily, without any friction - just read a good article about that few weeks ago.

                                                                                                                                                                                                                2. 4

                                                                                                                                                                                                                  Interesting article from Carmack about inlining everything:

                                                                                                                                                                                                                  http://number-none.com/blow/blog/programming/2014/09/26/carmack-on-inlined-code.html

                                                                                                                                                                                                                  1. 4

                                                                                                                                                                                                                    This isn’t an argument against inheritance, it’s an argument against modularity: Any time you move code out of inline you have the exact same “problem” (to the extent it is a problem) and you can only solve it the same way, with improved tooling of one form or another. ctags, for example, or etags in Emacs.

                                                                                                                                                                                                                    Not really, including code via accessing a class or object member forces you to manually go figure out which implementation is used, or where the implementation in a web of nested namespaces. In the case of function, each symbol is non-ambiguous. This is a big deal. If you have types A and B, with A having an attribute of the type B, each of these types containing a 3 level hierarchy, and you call A.b.some_b_method(). That could be defined in 9 different places, and if it is, you need to figure out which that symbol resolves to. This is a real problem.

                                                                                                                                                                                                                    1. 2

                                                                                                                                                                                                                      This isn’t an argument against inheritance, it’s an argument against modularity:

                                                                                                                                                                                                                      Yeah, all code should be in a single file anyway. No more chasing of method definitions across multiple files. You just open the file and it’s all there…

                                                                                                                                                                                                                      1. 2

                                                                                                                                                                                                                        Any form of modularity should be there to raise the level of abstraction. ie. Become a building block, that is solid (pun intended) firm and utterly reliable, that you can use to understand the higher layers.

                                                                                                                                                                                                                        You can peer inside the building block if you need to, but all you need to understand about it, to understand the next level up, is what it does, not how it does it.

                                                                                                                                                                                                                        Inheritance is there to allow you to know that “all these things IS A that”. ie. I can think of them and treat all of them exactly as I would treat the parent class. (ie. The L in SOLID)

                                                                                                                                                                                                                        I can utterly rely on the fact that the class invariant for the superclass holds for all subclasses. ie. The subclasses may guarantee other things, but amongst the things they guarantee, is that the super class’s class invariant holds.

                                                                                                                                                                                                                        I usually write a class invariant check for every class I write.

                                                                                                                                                                                                                        I then invoke it at the end of the constructor, and the beginning of the destructor, and at the start and end of every public method.

                                                                                                                                                                                                                        As I become more convinced of the correctness of what I’m doing, I may remove some for efficiency reasons. As I become more paranoid, I will add some.

                                                                                                                                                                                                                        In subclasses, the class invariant check always invokes the parent classes invariant check!

                                                                                                                                                                                                                      1. 4

                                                                                                                                                                                                                        Interesting write-up.

                                                                                                                                                                                                                        I’m a big fan of Dune (hell, I have a Dune tattoo), but I always struggled playing the game. The UX and controls just feel too clunky for me, as someone who played the game for the first time a few years ago. It’s was clear to me that the game was pretty neat underneath all of that, but in the end I just wasn’t enjoying it because of that, and gave up quite soon and never really tried again.

                                                                                                                                                                                                                        I actually played a lot of Herzog back in the day on our MSX (the first one, not Herzog Zwei), which is also kind-of an RTS.

                                                                                                                                                                                                                        I suspect there will be more Dune games in the next few years, with the upcoming movie and all.

                                                                                                                                                                                                                        1. 2

                                                                                                                                                                                                                          with the upcoming movie and all.

                                                                                                                                                                                                                          I was glad to see it’s going to be in two parts (though I would’ve preferred three, to match up with the book divisions). Trying to cram Dune into two hours just isn’t feasible. (The Weirding Way, you see, is based on sound…)

                                                                                                                                                                                                                          I was disappointed with the SyFy miniseries too. I had to turn it off when I realized they had Baron Harkonnen speaking in rhyming couplets and what the hell was going on with Irulan….

                                                                                                                                                                                                                          So, I gotta ask: what’s the tattoo?

                                                                                                                                                                                                                          1. 3

                                                                                                                                                                                                                            I couldn’t bear to finish watching the Sci-Fi channel miniseries. I actually saw it before I saw the Lynch film or read the books and felt it was just boring. I also thought Paul Atreides was a spoiled annoying brat.

                                                                                                                                                                                                                            So, I gotta ask: what’s the tattoo?

                                                                                                                                                                                                                            A picture especially for you 😚: https://imgur.com/a/aSOzVIv

                                                                                                                                                                                                                            1. 1

                                                                                                                                                                                                                              That’s awesome.

                                                                                                                                                                                                                            2. 1

                                                                                                                                                                                                                              I never got the whole sound thing until I became a bigger fan of Lynch and realized his whole deal is sound design. Of course he’d add in a sound that kills!

                                                                                                                                                                                                                            3. 2

                                                                                                                                                                                                                              This is somewhat the issue with old games: their controls don’t keep up with semi-modern conventions. I would have loved to play Fallout 1 since I think the setting is absolutely fascinating but the second I have to fight with the damn door controls I feel like throwing the computer across the room.

                                                                                                                                                                                                                              A year ago I was playing Pillars of Eternity and was impressed how it managed to capture the essence of Baldurs Gate 2 but without reminding me how it might not have aged well. It felt like playing BG2 back in the day did for me, but I am sure there have been plenty of adjustments to make it palatable for modern audiences.

                                                                                                                                                                                                                              1. 2

                                                                                                                                                                                                                                Yeah, Pillars of Eternity did a really good job of polishing the UX of the 2000-era cRPG games, although BG2 is still pretty playable today, and there’s the enhanced edition which makes things a bit easier too. It’s also worth checking out Tyranny by the way, I thought it was even better than PoE.

                                                                                                                                                                                                                                Fallout 1 and 2 (and Arcanum, by the Fallout 1 team) are … special. They’re all absolutely fantastic games, but you almost need some xanax to be able to play them. There’s no real technical reason for it as such, and I’m surprised at how much patience gamers (including myself) had for clearly bad UX even just 20 years ago.