1. 4

    This reminds me a bit about using Pidgin as an IM client way back, it would basically never hang and have pretty instant feedback. Compare to Messenger or Line…. yikes

    OTOH it was a really bad IRC client and would get really slow with a bunch of data.

    I do wish that more apps would just use local stores (at least for caching). I bet most services I use have just a couple megs of data for me (especially if we’re ignoring stuff like images). Just use local stuff and a sync button or something!

    1. 4

      Honestly, I have to say: It depends. This technique is easily applied when the system is entirely written in one language, and you can write wrapper methods and classes in almost every case. But here’s an example of a situation where you can’t do that, or where doing it would probably introduce too much complexity relative to the value of the technique.

      • The old stack has a frontend written in $old_tech (e.g. Rails views + jQuery; or perhaps AngularJS), and a backend written as a REST API
      • The new stack is to have its frontend written in $new_tech (say, Vue, or React, or Angular), and is supposed to interact with the backend via GraphQL
      • The database gets to stay the same, serving both old and new stacks

      What can be done in such situations is replacement of pages (whether in classic web app or SPA) one by one, each self-contained, whose development doesn’t impact the other.

      1. 2

        One step in that process would be for you to “implement” your REST API through GraphQL, internally.

        For the frontend, you can have Angular or Vue side by side, so you can write nice helpers that make things easier to deal with, while just basically calling into jquery plugins or the like.

        the DB remaining the same seems like a decent strategy overall

        Now this is just from your data points and they might be harder or easier (I’m of the opinion that some old tech can stick around forever if it’s not busted), but you can get incremental value add with a bit of work here

        1. 1

          Not 100% the same, but when we did it we just served different /route to different backends. In our case the rewrite was PHP like the original application, but that was coincidence. It wasn’t very frontend-heavy in the sense that I read from your question, but I don’t see a huge difference. Also maybe you can just refactor the old system enough that it lends itself to interacting with the new, or deliberately is out of the way. E.g. put a very basic GraphQL mock interfaced on the old app.

        1. 9

          I would use Python, mostly because it’s what I know, and because I have seen many people get proficient in it very quickly.

          It’s not the fastest thing, or the safest, but being able to debug broken-ness is very valuable, and lots of stuff about the language makes it really nice to write “simple” code

          1. 5

            A bit of a counterpoint to the new language thing: Javascript from 10 years ago just works! I’m pretty sure this is the same for Haskell, Java, and probably PHP as well.

            There’s definitely languages that break a lot in their stuff, but it’s not impossible to keep stuff working out of the box.

            That being said, there is a certain magic to “ANSI C” stuff feeling like it will always compile in any environment until the end of time

            1. 3

              Not all JavaScript from 10 years ago runs. There is a fairly steady stream of browser APIs as well as base JS APIs that are deprecated. I just encountered this yesterday at work with Chrome 80 having removed a browser API that we use for debugging remote Chrome apps.

              1. 3

                The thought of trying to compile Haskell from last year fills me with existential horror, never mind Haskell from 10 years ago. Though, this is more a problem with the ecosystem and dependency management rather than the language itself.

                1. 1

                  It’s good to hear there’s some stability out there. I don’t think I uses anything Haskellish; I have definitely encountered some java code that can’t be run any more (Palm Pilot 2 ARM game called CounterSheep IIRC) but generally desktop stuff has been OK, and PHP’s practical culture seems to centre a lot around not rocking existing stuff/servers that work.

                  I’m not a JS dev, but what about dialects such as jscript?

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

                  I suspect there might be some survivorship bias here. What we consider canonical/main/normal javascript history are the parts that survived and that new dialects/features/etc were built on. Not the bits that died and many websites that only worked in IE.

                1. 7

                  Wow, the time parsing API is …. something. It’s pretty clever! But you really have to really not want to just use format params in order to end up writing such an API.

                  I wonder how much other fun APIs are in go, I kinda assumed that it would have some pretty boring APIs on account of its core language not providing much flexibility.

                  1. 1

                    Kinda related, I’ve gotten a bit worn out from pycharms perf problems that I started looking at emacs for my IDE, and it’s tough! Lots of stuff that I’m trying to reproduce

                    Notably I really want some sort of good symbol browser (though likely what I need is to make myself a good cheat sheet). Keeping track of major modes is a bit tough at times too. Will need to work a lot on this one

                    1. 3

                      Only tangentially related, but Salesforce is basically SAP for a lot of smaller companies, and if you haven’t had the opportunity to, I would highly recommend trying out Salesforce Trailhead (a learning program for using Salesforce), in particular for coding in the environment.

                      Lots of people while about SFDC but it’s basically the Smalltalk environment everyone talks about. Very strong linking between UIs and the code that displays them, class browsers, lots of debugging capabilities… it’s very powerful.

                      1. 2

                        There’s something that invariably annoys me with this genre of art, in that it’s … actually totally fake but it presents itself as real. If you look at the pictures of the phones in particular, it’s clearly printed (and not screens), or at least some composted image.

                        I’ve had this experience so many times in visiting certain galleries, where it’s like “ohhhh isn’t this concept interesting?” but it fails to hold up against surface-level scruitiny. Can’t artists at least try to get some technologist to dig around? It’s like listening to somebody brainstorming while high and saying stuff that just has no rhyme or reason when trying to connect back to reality. Extremely disappointing

                        Imagine if you were actually successful in doing this. You would lay all the phones nicely and show them all doing the thing. You would have video of the phones in action, You wouldn’t show yourself at 3 different angles…. walking down 3 different streets!

                        end of rant.

                        1. 1

                          I did notice the screens looking weird but anyway their “thing” is just to drive through that road, that’s it.

                          So, it’s not really anything crazy or a program, just pretending that 99 peoples with their phone are driving through that road (that’s why they are all in “driving” mode).

                          What else would it have to show to make you happy?

                          1. 1

                            Well my point is that they are saying “We did this and caused the street to report as a traffic jam”.

                            What they really did: film some footage of somebody walking down a street, and edited a screenshot of google maps.

                            The thing they claimed to do, they didn’t actually do! It’s just a thought experiment.

                            1. 2

                              Can you provide some evidence?

                              I mean, I understand being skeptic but I’d like to understand why you think it’s both unfeasible and obvious. To me it fits exactly what the Google algorithm would do in such a situation.

                          2. 1

                            You would lay all the phones along a road you haven’t rented? Have fun cleaning up little bits of Android….

                            1. 1

                              Sorry, I meant you would store them in the wagon upwards, so you could see all the screens in action

                          1. 12

                            Pretty much every release since Snow Leopard has (rightfully) received a fair amount of criticism. Things generally seem to get better at the end of the cycle after a few dot releases.

                            If anything, this shows that Apple cannot handle the yearly release. I think many Mac users (me included) would be very grateful if they moved to a 2-3 year release cycle as before. That doesn’t mean that there can’t be new features in the meanwhile, but just ship them when they are ready.

                            I understand that they want to show off features at some keynote every year. But this is killing macOS as a platform. Plus the keynotes are very crammed anyway, given the number of platforms that they have now (iOS, macOS, iPadOS, watchOS, tvOS).

                            1. 4

                              People say “go back to 2 year release cycles” but continuous releases would probably be much more effective at shipping less buggy software.

                              The idea of shipping a bazillion changes all together is anathema to modern software development, and making that window even longer isn’t going to make things nicer. I know why they do the yearly cycle (marketing pushes), but perhaps they should consider the damage it does to the brand to just have a broken OS all the time, and adopt strategies that make things up to date while not having a billion bugs`

                              1. 2

                                People say “go back to 2 year release cycles” but continuous releases would probably be much more effective at shipping less buggy software.

                                I don’t know, continuous releases make it harder to introduce large architectural changes and get them properly tested. Unless you introduce something similar to Windows fast and slow rings (and get enthusiasts to run them).

                                but perhaps they should consider the damage it does to the brand to just have a broken OS all the time, and adopt strategies that make things up to date while not having a billion bugs`

                                I fully agree! At this point it would even be better if they didn’t update macOS at all, besides reliability and security fixes, than the yearly mess that is dumped on users.

                                1. 2

                                  I don’t know, continuous releases make it harder to introduce large architectural changes and get them properly tested.

                                  My gut feeling is to just not do large architectural changes. Figure out how to make them small and incremental instead. Takes longer, except you’re not spending a bunch of time poking at a system randomly because it’s impossible to reason about the scope of change.

                                  This is an easy position for me to take from where I am sitting, though. Sometimes bandaids have to be taken off. But…. I dunno, I feel like software engineers tend to generate a lot of unforced errors that end up turning into “needed rewrites of this component”.

                              2. 1

                                Given (as this article points out) how many users can’t yet upgrade, maybe the pause year should be this year.

                                But more likely Apple says, “you get two pause years [of updates], so suck it up.”

                              1. 4

                                I am always a bit disappointed when these tiny gadgets don’t include a battery. I guess it makes sense, but I really wanted a thing kinda like the Pebble Core (https://www.techradar.com/news/wearables/pebble-core-everything-you-need-to-know-1322135), without it being “tape a RasPi and a battery together”.

                                That being said, the … tininess of this product is really amazing. I really love things like micropython being available for this too, where you’re not having to choose between “code in C” and “run all of V8”.

                                1. 1

                                  Had the same thoughts and did a little research. MeowBit and Odroid Go look interesting if the handheld form factor is ok. Haven’t seen something like a programmable fitness band though.

                                  1. 2

                                    If 7-segment LCDs are OK for you this watch[0] might be an option.

                                    I use it with a custom firmware (which you can flash using CLI tools). Without RF and sensors I had a battery life of >1 year.

                                    [0] https://processors.wiki.ti.com/index.php/EZ430-Chronos

                                1. 1

                                  I was on the xperia series for a while, they’re friendly about rooting/custom roms. But then I tried out iOS and it’s such a more usable experience… plus I trust the privacy story on iOS way more (another comment mentioned it but they crack down a lot on random location information collection). So now I have an iPhone 8 that works well (save for random crashing of some apps since iOS 13…)

                                  1. 16

                                    A lot of the problems in PHP are really fundamental without easy fixes, not without the “Python 3 of PHP”. Slapping on a few features and syntax sugar is nice and all, but it won’t fix any of that.

                                    I’ve seen countless bugs because of magic conversion between associative and indexed arrays, extremely basic error checking and functions are still broken, it’s still very easy to add critical security bugs, and so forth, and so forth.

                                    There are still countless “wtf?!“s left. There is no easy fix for this, but PHP’s stdlib is a complete mess and liability.

                                    I don’t strictly disagree you can write good software with it, but on the other hand a lot of things are also much harder than they need to be. A certain subset of problems is impossible to solve. For example, a function to generate a temporary name with a fixed file extension (fooXXXXXXX.html) is impossible to implement in a guaranteed race-free way as far as I know (solving this problem inspired my fopen() post above; but I’ll gladly be shown wrong on this).

                                    Of course, it’s “probably good enough”, but why settle for that when you can have “guaranteed correct” with literally every other mainstream language?

                                    The value of PHP was in that it ran really well on shared hosting, and I think it was a stroke of great insight back in the day. Things that are “wtf”s now – like the php.ini – made a lot of sense in that context, too, but now we’re stuck with it. I don’t see the value PHP gives us. I don’t want to tell other people what language they “should” be using, but it just seems like a sunk-cost thing to me to be honest. As an industry, we would probably be better off without it in the long run IMHO.

                                    1. 4

                                      PHP’s strengths are how easy it is to make quick hacks quickly (similar to Perl) and get it running without any ceremony (if you have a web server with PHP, you can just upload it somewhere and it Just Works, no application server with reverse proxy needed). This is why it survives; I agree that the language’s warts pull you to writing flawed code though, especially once you need to start scaling.

                                      1. 4

                                        Isn’t Python, Ruby, or Perl just as good for that? I mean, you can point at “but I don’t like X or Y” and okay fair enough, but is it really “better” in any meaningful way?

                                        Admittedly, I’ve been out of the loop of PHP for a few years, but I often found it quite hard to get running due to the massive php.ini file which controls a lot of deep aspects of PHP. At least you no longer have to set the bloody timezone in there any more.

                                        1. 2

                                          For the “just copy the file to htdocs” workflow, I think only Perl comes close to that use case; Ruby is so Rails oriented nowadays. I’m not confident to say much about Python web stuff since I don’t know it though.

                                          1. 1

                                            You don’t need to use Rails with Ruby, just like you don’t need to use Laravel with PHP. It’s just common (in both cases) because it works well. Sinatra is a much small web framework that also works quite well for many cases (about 2k lines of code, IIRC).

                                            I do agree that using PHP can have a lower ramp-up time in some common “quick scripts” cases, but on the other hand, some of those cases are also rather hard to get right with PHP (specifically, I’m thinking of mail() being rather hard to use securely so that your contact form won’t be abused to send out spam). I’m not entirely sure just how much value there is in that (and it’s also not exactly what this article is talking about, either).

                                            1. 1

                                              For python you mostly can’t but there is a cgi module in the standard library (probably a bit painful to use cleanly)

                                          2. 2

                                            if you have a web server with PHP

                                            Is that nowadays a common case still? mod_php was an Apache thing and even Apache has been declining over the years so it might actually be easier to find a server that can run Perl or Python as CGI since at least the latter is nearly guaranteed to be installed in any case.

                                            I don’t have any numbers though, so if anyone has recent information on how common it is to have PHP hosting, I’m all ears.

                                            1. 3

                                              Wordpress is still going strong, and I think every el cheapo hosting provider that offers some sort of shared hosting is still using Apache with PHP (think DirectAdmin or CPanel)

                                          3. 2

                                            I agree with this. When my employer gave me an opportunity to do a Greenfield PHP project, I accepted it with the thought that “hey, it’s 2019, and I’ve heard of many good developments in PHP recently, so maybe it’s not so bad anymore.”

                                            I did everything right (as far as I could tell), followed modern best practises, etc. PHP is still so bad for the reasons you mention.

                                            1. 1

                                              “guaranteed correct” with literally every other mainstream language?

                                              ::bitter laughter::

                                            1. 2

                                              Embedding a scripting language into a C99 application for scripting program behavior (startup configuration will be done using these scripts too). I’m not entirely sure which one I’m going to use, right now I’m torn between lua, python, or just handwriting a LISP. I’m leaning towards Lua right now, but I’m sure someone here has done this before and I’m open to recommendations.

                                              1. 3

                                                Lua is definitely easier to deal with when integrating stuff, but then you’re left writing Lua (it’s fun for scripts but less fun for bigger programs I think).

                                                I don’t know your usecase, but you might be able to “invert the stack”, where your entry point is written in Python, and it calls into your application via the Python FFI. This is what stuff like Mercurial does, and lets you more easily manage the dynamic nature of a program entry point and give you a good amount of expressive power.

                                                The Python FFI with C is really straightforward, I’d recommend it.

                                                1. 1

                                                  I don’t know your usecase, but you might be able to “invert the stack”, where your entry point is written in Python, and it calls into your application via the Python FFI. This is what stuff like Mercurial does, and lets you more easily manage the dynamic nature of a program entry point and give you a good amount of expressive power.

                                                  This is for my game engine. There’s an engine API exposed for export as a shared library and games/demos are loaded at runtime from shared objects (or DLLs) by the engine driver (a separate executable) during development. It should be possible to easily generate Python or Lua bindings based on this engine API.

                                                  File parsers (.ini, or XML or JSON data conversions) and script interpreters (C-like or LISP) in my experience become massive time sinks with poor documentation and breed many, many bugs, especially for mapping to/from JSON–“What are the keys this thing accepts?”, “Which parser version is this using and when did this key change?”. Scripts should be able to access engine behavior permitted by the security settings of their runtime environment, so they can be used for engine configuration, scripting gameplay behavior, and asset description (during development).

                                                  Asset descriptions should be human-readable, diff-tool comprehendable, and text searchable until compiled into optimized formats by the asset conditioning pipeline for distribution. Asset descriptions will rely on engine functions to piece together assets for use to simplify debugging, error checking, and to make behavior easily searchable to avoid the “oh, that thing I need is actually in some descendant tree of some binary-based object in the editor!” I’ve seen this done very well with LISPs before, which is why I had been thinking that way.

                                                  This creating binary dependencies–one of the next steps prior to this work is automatically creating and verifying semantic versions based on the symbols generated and function signatures of engine or game dlls. This will identify breakages automatically based on when symbols are added, removed or changed as part of CI/CD (diff can also be used to identify which C functions changed between commits) and simplifying this process is actually part of the reason I’m using C, since it generates consistent symbol names for analysis.

                                                  1. 1

                                                    I can’t speak to this from experience, but some years ago, I came across this page advocating for extending python with your library, rather than embedding: https://twistedmatrix.com/users/glyph/rant/extendit.html, and thought the arguments made sense. On the other hand, I have a sense Lua was made to be embeddable, and your own LISP would presumably be fine.

                                                    1. 1

                                                      That’s a great link! It has a lot of ideas I hadn’t thought about. I commented on another post why I’m doing embedding since it’s doing low-level control, not high level program flow. I should have been more clear about this in my original comment.

                                                  2. 1

                                                    Writing your own lisp or any PL implementation is a deep rabbit hole, approach with caution :) It also taught me a lot about programming though.

                                                    If the goal is actually finishing the main program: Lua; there’s also a few small lisps made with embeddability as a goal, chibi scheme to name one.

                                                    1. 1

                                                      Writing your own lisp or any PL implementation is a deep rabbit hole, approach with caution :) It also taught me a lot about programming though.

                                                      Yeah, I’ve written one before, that’s definitely a problem! :)

                                                      If the goal is actually finishing the main program: Lua

                                                      This is consistent with what I’m finding else in my research.

                                                      1. 1

                                                        I’m kinda in love with Lua since I first learnt about it some 15 (or more?) years ago, so I may not be exactly objective… generally, Lua excels at being an embedded language; also, historically, it evolved in part from a configuration language, so “glorified config” usage is deeply in its DNA, should you want it for this purpose (I like it as an escape hatch from Greenspun’s Tenth Rule whenever I’m tempted to consider adding some logic/templating to JSON). The language (and its implementation) is very simple yet powerful, the API is small, simple, robust, and designed for being embedded. If you are planning to use it for a game, the added benefit is that it’s basically an industry standard for scripting in games. Other than tons of games, it’s also used in a lot of other stuff, including: LuaTeX, Wireshark (IIRC), NetBSD has support for Lua-based drivers (!), and more.

                                                        On the other hand, the elephant in the room is, that Lua has a relatively small ecosystem of third-party libraries; definitely not anywhere near Python. There’s basically LuaRocks, and sometimes some fringe stuff not in Rocks. Alternatively, you can consider embedding LuaJIT instead of vanilla (a.k.a. PUC-Rio) Lua; it has awesome LuaFFI which can make it easier for people/you to do some integrations in the future; this might also allow you to reuse some third-party libraries created by the Love 2D community, though I never tried doing that. One thing to know here, is that LuaJIT’s creator stepped down, and although kinda handed over since, the project seems to me to be slowly stagnating (though, it can also be seen as basically “feature complete”). It wasn’t upgraded to Lua 5.3 (in all fairness, on purpose), though Lua 5.1 from which it was forked is already a very mature language.

                                                        As to writing “huge apps”, I never personally did that in Lua; my assumption here is, that possible issues around that would probably boil down to Lua being dynamically typed (less protections), and an interpreted language (speed). I think one would have similar problems in JS, Python, or LISP. As to speed, all of the languages I listed try to mitigate it to some extent through JIT interpreter(s), and Lua also has one in LuaJIT.

                                                        As to writing your own LISP… yeah, can do, as long as your main goal is to write your own LISP ;) I am sure this is very educating and enlightening, however it will definitely distract you form the game itself for long (if not forever…). Also, as far as ecosystem, I claim you’ll be much, much worse than with Lua, not to even speak about Python; one more dialect of LISP in a world where even mainstream LISPs are kinda fringe…

                                                  1. 2

                                                    $HOME: Trying to finish up a Qt GUI for Circle CI (really can’t handle their web UI). PyQt has been an interesting experience so far.

                                                    $WORK: finishing up our serious move to Webpack, some Django app maintenance.

                                                    1. 3

                                                      FWIW I think most Python programmers prefer list comprehensions to map() and filter(). I personally don’t use the latter in my own code (after ~17 years and hundreds of thousands of lines of Python)

                                                      https://www.oreilly.com/library/view/python-cookbook/0596001673/ch01s11.html

                                                      1. 2

                                                        Oddly this has been a minor point of contention in the past. That’s the majority view but there’s a substantial number of people who prefer it the other way around.

                                                        I think early in py3’s life there was discussion of moving map/filter/reduce to functools but it didn’t end up happening. There’s a bit of an on-going narrative of Guido being a little hostile towards FP in python (LCs over map/filter/reduce, no multi-line lambdas, some of the comments he’s made on these things). I’m not sure how much credence to really give it, but I think it’s worth pointing out LCs over higher order functions isn’t unanimously prefered.

                                                        1. 2

                                                          My personal view is that generator comprehensions should be preferred if the alternative is map(lambda x: and that list comprehensions should be preferred if the alternative is list(map(.... But if I’m just doing something like (foo(x) for x in y) it seems cleaner to me to use map(foo, y).

                                                          I only really write Python code for my own amusement though.

                                                          1. 1

                                                            Less of a problem for scripts, but for long-term maintenance, list comprehensions survive the “small change requires only a small change” metrics a bit better.

                                                            Imagine if you have [foo(x) for x in y] and decide “oh let’s filter out for x = 0. You then have [foo(x) for x in y if x] (or x != 0 or whatever your condition is). If you started off with map(foo, y), you now need to do map(foo, filter(y, lambda x: x!=0)).

                                                            The unfair advantage is that list comprehensions handle both map and filter, so you don’t have to decide ahead of time. It also helps their case that anonymous functions in Python are limited to lambdas, which kinda stink (relative to if you were writing in Haskell for example)

                                                            1. 2

                                                              I don’t see any problem changing map(foo, y) to (foo(x) for x in y if x) if that’s what I’d like to replace it with.

                                                        2. 1

                                                          I personally like comprehensions mainly because I learned how to use them before map/filter and I’ve had a lot more practical use with them.

                                                          I do see the use cases for map/filter though.

                                                        1. 41

                                                          The claim is simple: in a static type system, you must declare the shape of data ahead of time, but in a dynamic type system, the type can be, well, dynamic! It sounds self-evident, so much so that Rich Hickey has practically built a speaking career upon its emotional appeal. The only problem is it isn’t true.

                                                          Immediate standing ovation from me.

                                                          I can only assume that oft-made claim is perpetuated from a position of ignorance. Have those people actually tried doing the thing in a statically typed language that they claim a statically typed language cannot do? Here’s an approach that appears all over my Haskell projects:

                                                            req <- requireCheckJsonBody :: Handler Value
                                                            let chargeId = req ^. key "data" . key "object" . key "id" . _String
                                                          

                                                          I don’t know (or care) what the exact structure of the JSON coming over the network will look like. I just know it will contain this one field that I care about, and here I pull it out and read it as a string.

                                                          Do I need the entire JSON string to conform to some specific protocol (more specific than JSON itself)? No. I am just parsing it as some JSON (which is represented with the Value type).

                                                          Do I need to parse it into some complex data type? No. I’m just building a string. I am doing — in Haskell — exactly the kind of thing that Clojurists do, but without being smug about it.


                                                          If we keep the datatype’s constructor private (that is, we don’t export it from the module that defines this type), then the only way to produce a UserId will be to go through its FromJSON parser.

                                                          I’m glad I read this article even for this design tip alone. I had never thought to do it this way; I thought a “smart constructor” was always necessary, even when that seemed like overkill.

                                                          1. 5
                                                              let chargeId = req ^. key "data" . key "object" . key "id" . _String
                                                            

                                                            So what does this piece of code actually do? Get the value under data->object->id as a String? _String is there to prevent name clashes with actual String? Is the magic here that the JSON payload isn’t parsed any more than it needs to be?

                                                            Stylistically, do you know why Haskell people often seem to decide to use weird operators? Are all alternatives somehow worse?

                                                            1. 8

                                                              So what does this piece of code actually do? Get the value under data->object->id as a String?

                                                              Yeah, exactly. This is how the Stripe API structures their responses. I could have picked a simpler hypothetical example, but I think even this real-world case is simple enough.

                                                              _String is there to prevent name clashes with actual String?

                                                              I believe so, yes. This is just a thing in the lens library.

                                                              Is the magic here that the JSON payload isn’t parsed any more than it needs to be?

                                                              I believe it is parsed only as much as necessary, yes. I’m not sure there’s any magic happening.

                                                              Stylistically, do you know why Haskell people often seem to decide to use weird operators? Are all alternatives somehow worse?

                                                              There are plenty of alternative syntaxes and approaches you could opt for. I happen to find this easy enough to read (and I think you do too, since you worked out exactly what it does), but that is of course subjective.

                                                              1. 3

                                                                the syntactic weirdness is mostly due to the fact that the base grammar is very simple, so you end up basically relying on making infix operators to build symbol-ful DSLs.

                                                                This is very powerful for making certain kinds of libraries, but means that lots of Haskell looks a bit “out there” if you haven’t looked at code using a specific library before. This tends to be at its worst when doing stuff like JSON parsing (where you have very variably-shaped data)

                                                                1. 6

                                                                  Although conversely, I think more typical parsing with Aeson (especially the monadic form) is usually very tidy, and easy to read even by people not familiar with Haskell. It’s much less noisy than my lens example.

                                                                  Here’s an example: https://artyom.me/aeson#recordwildcards

                                                                  I think you probably know this, but I am posting here mostly so that other curious onlookers don’t get the wrong idea and think that Haskell is super weird and difficult.

                                                              2. -5

                                                                Lol what - you’re defining a benefit of dynamically typed language with your example. The json in your case IS a dynamic object.

                                                                1. 7

                                                                  I think you are quite confused about what we’re discussing.

                                                                  The discussion is around type systems in programming languages. JSON is just a protocol. The JSON that my example parses is not a “dynamic object”. There is no such thing as a JSON object. JSON is only ever a string. Some data structure can be serialised as a JSON string. A JSON string can potentially be parsed by a programming language into some data structure.

                                                                  The JSON protocol can be parsed by programming languages with dynamic type systems, e.g., Clojure, and the protocol can also be parsed by programming languages with static type systems, e.g., Haskell.

                                                                  My example is taken verbatim from some Haskell systems I’ve written, so it is not “defining a benefit of dynamically typed language”.

                                                                  You’re going to have to go and do a bit more reading, but luckily there is plenty of material online that explains these things. I think your comment is a good example of the kind of confusion the article’s author is trying to address.

                                                                  1. 2

                                                                    I read the article, and I agree somewhat with the parent commenter. It really seems that the author – and perhaps you as well – was comfortable with the idea of potentially declaring parts of the program as just handling lots of values all of a single generic/opaque/relatively-underspecified type, rather than of a variety of richer/more-specified types.

                                                                    That position is not all that far from being comfortable with all values being of a single generic/opqaue/relatively-underspecified type. Which is, generally, the most charitable description the really hardcore static-typing folks are willing to give to dynamically-typed languages (i.e., “in Python, all values are of type object”, and that’s only if someone is willing to step up their politeness level a bit from the usual descriptions given).

                                                                    In other words, a cynical reading would be that this feels less like a triumphant declaration of “see, static types can do this!” and more an admission of “yeah, we can do it but only by making parts of our programs effectively dynamically typed”.

                                                                    1. 3

                                                                      I don’t know how you’ve come to this conclusion. Moreover, I don’t understand how your conclusion is related to the argument in the article.

                                                                      In other words, a cynical reading would be that this feels less like a triumphant declaration of “see, static types can do this!” and more an admission of “yeah, we can do it but only by making parts of our programs effectively dynamically typed”.

                                                                      What does this even mean? How did you come up with this idea? When you want to parse some arbitrary JSON into a more concrete type, you can just do that. How does parsing make a program no longer statically typed?

                                                                      1. 2

                                                                        What is the difference between:

                                                                        1. “Everything in this part of the program is of type JSON. We don’t know what the detailed structure of a value of that type is; it might contain a huge variety of things, or not, and we have no way of being sure in advance what they will be”.
                                                                        2. “Everything in this part of the program is of type object. We don’t know what the detailed structure of a value of that type is; it might contain a huge variety of things, or not, and we have no way of being sure in advance what they will be”.

                                                                        The first is what the article did. The second is, well, dynamic typing.

                                                                        I mean, sure, you can argue that you could parse a JSON into some type of equally-generic data structure – a hash table, say – but to usefully work with that you’d want to know things like what keys it’s likely to have, what types the values of those keys will have, and so on, and from the type declaration of JSON you receive absolutely none of that information.

                                                                        In much the same way you can reflect on an object to produce some type of equally-generic data structure – a hash table, say – but to usefully work with that you’d want to know things like… hey, this is sounding familiar!

                                                                        Now do you see what I mean? That’s why I said the cynical view here is the author has just introduced a dynamically-typed section into the program.

                                                                        1. 3

                                                                          Any program which reads some JSON and parses it will be making some assumptions about its structure.

                                                                          This is true of a program written in a dynamically-typed language.

                                                                          This is true of a program written in a statically-typed language.

                                                                          Usually, you will want to parse a string of JSON into some detailed structure, and then use that throughout your system instead of some generic Value type. But you don’t necessarily need to do that. Nothing about writing in a statically-typed programming language forces you to do that. And no, Haskell programmers don’t generally intentionally try to make their programs worse by passing Value types, or generic Map types, or just anything encoded as a String, throughout their program. That would be stupid.

                                                                          1. 3

                                                                            OK, I’ll do the long explanation.

                                                                            Many programmers whose primary or sole familiarity is with statically-typed languages assume that in dynamically-typed languages all code must be littered with runtime type checks and assertions. For example, I’ve run into many people who seem to think that all Python code is, or should be, full of:

                                                                            if isinstance(thing, some_type) ...
                                                                            elif isinstance(thing, some_other_type) ...
                                                                            

                                                                            checks in order to avoid ever accidentally performing an operation on a value of the wrong type.

                                                                            While it is true that you can parse a JSON into a data structure you can then pass around and work with, the only way to meaningfully do so is using your language’s equivalent idiom of

                                                                            if has_key(parsed_json, some_key) and isinstance(parsed_json.get(some_key), some_type)) ...
                                                                            elif has_key(parsed_json, some_other_key) and isinstance(parsed_json.get(some_other_key), some_other_type) ...
                                                                            

                                                                            since you do not know from the original type declaration whether any particular key will be present nor, if it is present, what type the value of that key will have (other than some sort of suitably-generic JSONMember or equivalent).

                                                                            Which is to say: the only way to effectively work with a value of type JSON is to check it, at runtime, in the same way the stereotypical static-typing advocate thinks all dynamically-typed programmers write all their code. Thus, there is no observable difference, for such a person, between working with a value of type JSON and writing dynamically-typed code.

                                                                            Now, sure, there are languages which have idioms that make the obsessive checking for members/types etc. shorter and less tedious to write, but the programmer will still be required, at some point, either to write such code or to use a library which provides such code.

                                                                            Thus, the use of JSON as a catch-all “I don’t know what might be in there” type is not distinguishable from dynamically-typed code, and is effectively introducing a section of dynamically-typed code into the program.

                                                                            1. 3

                                                                              I still don’t get what point you’re trying to make. Sorry.

                                                                              Thus, the use of JSON as a catch-all “I don’t know what might be in there” type is not distinguishable from dynamically-typed code, and is effectively introducing a section of dynamically-typed code into the program.

                                                                              This now sounds like you’re making an argument between parsing and validation, and misrepresenting it at static vs dynamic.

                                                                              1. 2

                                                                                This now sounds like you’re making an argument between parsing and validation, and misrepresenting it at static vs dynamic.

                                                                                For an alternative formulation, consider that people often claim, or want to claim, that in a statically-type language most of the information about the program’s behavior is encoded in the types. Some people clearly would like a future where all such information is encoded in the types (so that, for example, an add function would not merely have a signature of add(int, int) -> int, but a signature of add(int, int) -> sum of arguments which could be statically verified).

                                                                                I have complicated thoughts on that – the short hot-take version is those people should read up on what happened to logical positivism – but the point here is a reminder that this article, which was meant to show a way to have nice statically-typed handling of unknown data structures, was able to do so only by dramatically reducing the information being encoded in the types.

                                                                                1. 3

                                                                                  the point here is a reminder that this article, which was meant to show a way to have nice statically-typed handling of unknown data structures, was able to do so only by dramatically reducing the information being encoded in the types.

                                                                                  …How else would a program know what type the program’s author intends for the arbitrary data to be parsed into? Telepathy?

                                                                                  1. 1

                                                                                    I think at this point it’s pretty clear that there’s nothing I can say or do that will get you to understand the point I’m trying to make, so I’m going to bow out.

                                                                          2. 3

                                                                            What is the difference between:

                                                                            1. “Everything in this part of the program is of type JSON. We don’t know what the detailed structure of a value of that type is; it might contain a huge variety of things, or not, and we have no way of being sure in advance what they will
                                                                            2. “Everything in this part of the program is of type object. We don’t know what the detailed structure of a value of that type is; it might contain a huge variety of things, or not, and we have no way of being sure in advance what they will be”.

                                                                            The first is what the article did. The second is, well, dynamic typing.

                                                                            The difference is that in a statically-typed language, you can have other parts of the program where proposition 1. is not the case, but in a dynamically-typed language proposition 2. is true all the time and you can’t do anything about it. No matter what style of typing your language uses, you do have to inspect the parsed JSON at runtime to see if it has the values you expect. But in a statically-typed language, you can do this once, then transform that parsed JSON into another type that you can be sure about the contents of; and then you don’t have to care that this type originally came from JSON in any other part of your program that uses it.

                                                                            Whereas in a dynamically-typed language you have to remember at all times that one value of type Object happens to represent generic JSON and another value of type Object happens to represent a more specific piece of structured data parsed from that JSON, and if you ever forget which is which the program will just blow up at runtime because you called a function that made incorrect assumptions about the interface its arguments conformed to.

                                                                            Anyway even introducing a “generic JSON” type is already encoding more useful information than a dyanmically-typed language lets you. If you have a JSON type you might expect to have some methods like isArray or isObject that you can call on it, you know that you can’t call methods that pertain to completely different types like getCenterPoint or getBankAccountRecordsFromBankAccountId. Being able to say that a value is definitely JSON, even if you don’t know anything about that JSON, at least tells you that it’s not a BankAccount or GLSLShaderHandle or any other thing in the vast universe of computing that isnt JSON.

                                                                            1. 2

                                                                              Whereas in a dynamically-typed language you have to remember at all times that one value of type Object happens to represent generic JSON and another value of type Object happens to represent a more specific piece of structured data parsed from that JSON, and if you ever forget which is which the program will just blow up at runtime because you called a function that made incorrect assumptions about the interface its arguments conformed to.

                                                                              This is where the discussion often veers off into strawman territory, though. Because I’ve written code in both dynamically and statically typed languages (and hybrid-ish stuff like dynamically-typed languages with optional type hints), and all the things people say about inevitable imminent doom from someone passing the wrong types of things into functions are, in my experience, just things people say. They don’t correspond to what I’ve actually seen in real programming.

                                                                              That’s why in one of my comments further down I pointed out that the generic JSON approach used in the article forces the programmer to do what people seem to think all dynamically-typed language programmers do on a daily basis: write incredibly defensive and careful code with tons of runtime checks. My experience is that people who prefer and mostly only know statically-typed languages often write code this way when they’re forced to use a dynamically-typed language or sections of code that are effectively dynamically-typed due to using only very very generic types, but nobody who’s actually comfortable in dynamic typing does that.

                                                                              And the best literature review I know of on the topic struggled to find any meaningful results for impact of static versus dynamic typing on defect rates. So the horror stories of how things will blow up from someone forgetting what they were supposed to pass into a function are just that: stories, not data, let alone useful data.

                                                                              Anyway, cards on the table time here.

                                                                              My personal stance is that I prefer to write code in dynamically-typed languages, and add type hints later on as a belt-and-suspenders approach to go with meaningful tests (though I have a lot of criticism for how Python’s type-hinting and checking tools have evolved, so I don’t use them as much as I otherwise might). I’ve seen too much statically-typed code fall over and burn the instant someone pointed a fuzzer at it to have much faith in the “if it passes type checks, it’s correct” mantra. And while I do enjoy writing the occasional small thing in an ML-ish language and find some of the idioms and patterns of that language family pleasingly elegant, mostly I personally see static typing as a diminishing-returns technique, where beyond a very basic up-front pass or two, the problems that can be prevented by static typing quickly become significantly smaller and/or less likely as the effort required to use the type system to prevent them increases seemingly without bound.

                                                                              1. 3

                                                                                This is where the discussion often veers off into strawman territory, though. Because I’ve written code in both dynamically and statically typed languages (and hybrid-ish stuff like dynamically-typed languages with optional type hints), and all the things people say about inevitable imminent doom from someone passing the wrong types of things into functions are, in my experience, just things people say. They don’t correspond to what I’ve actually seen in real programming.

                                                                                I disagree - passing the wrong types of things into functions is definitely a phenomenon I’ve personally seen (and debugged) in production Ruby, JavaScript, and Python systems I’ve personally worked on.

                                                                                For instance, I’ve worked on rich frontend JavaScript systems where I was tasked with figuring out why a line of code a.b.c was throwing TypeError but only sometimes. After spending a bunch of time checking back to see where a ultimately came from, I might find that there was some function many frames away from the error in the call stack that sets a from the result of an xhr that isn’t actually guaranteed to always set a key b on a, and that code was not conceptually related to the code where the error happened, so no one thought it was unusual that a.b wasn’t guaranteed, which is how the bug happened.

                                                                                In a statically typed language, I could convert the JSON that will eventually become a into a specifically-typed value, then pass that down through 10 function calls to where it’s needed, without worrying that I’ll find 10 frames deep that SpecificType randomly doesn’t have a necessary field, because the conversion from the generic to the specific would’ve failed at the conversion site.

                                                                                I am a fan of statically typed languages, and a huge reason for this is because I’ve debugged large codebases in dynamically-typed languages where I didn’t write the original code and had to figure it out by inspection. Static typing definitely makes my experience as a debugger better.

                                                                                1. 1

                                                                                  definitely a phenomenon I’ve personally seen (and debugged)

                                                                                  Notice I didn’t say “your stories are false”.

                                                                                  Nor did you refute my claims that I’ve seen statically-typed code which passed static type checks fall over and crash when fuzzed.

                                                                                  We each can point to instances where our particular bogeyman has in fact happened. Can either of us generalize usefully from that, though? Could I just use my story to dismiss all static typing approaches as meaningless, because obviously they’re not catching all these huge numbers of bugs that must, by my generalization, be present in absolutely every program every person has ever written in any statically-typed language?

                                                                                  The answer, of course, is no. And so, although, you did write a lot of words there, you didn’t write anything that was a useful rebuttal to what I actually said.

                                                                      2. 0

                                                                        I appreciate your condencending tone but really you should work on your ability to argument. The original post claims that this statement is not true:

                                                                        in a static type system, you must declare the shape of data ahead of time, but in a dynamic type system, the type can be, well, dynamic!

                                                                        You argue however that this is indeed not true because you can have “dynamic” data and “static” types when that’s just a silly loophole. Surely you want data as an object right? A string of characters without the meta structure are completely useless in the context of programming.

                                                                        Just because you can have a static type that doesn’t have strict, full protocol implementation doesn’t mean that you don’t need to declare it before hand which renders the original statement absolutely correct - you must declare static shape of data that matches what your type expects. The claim that types can be “lose” doesn’t invalidate this statement.

                                                                        1. 4

                                                                          I appreciate your condencending tone but really you should work on your ability to argument.

                                                                          I’m sorry you feel that way. I genuinely did my best to be kind, and to present some absolute truths to you that I had hoped would clear up your confusion. Unfortunately, it looks like you’ve decided to dig your heels in.

                                                                          You argue however that this is indeed not true because you can have “dynamic” data and “static” types when that’s just a silly loophole.

                                                                          I don’t know what you are talking about. Dynamic data? What does this mean? And what silly loophole?

                                                                          In the context of this argument: the JSON being parsed is a string. It’s not static. It’s not dynamic. It’s a string.

                                                                          which renders the original statement absolutely correct - you must declare static shape of data that matches what your type expects.

                                                                          No, you don’t. Again, you have misunderstood me, you have misunderstood the article, and you have misunderstood some pretty basic concepts that are fundamental to constructing a cohesive argument in this debate.

                                                                          The argument is whether or not — in a statically-typed programming language — the JSON string you are parsing needs to conform 1:1 to the structure of some data type you are trying to parse it into.

                                                                          The answer is: No. Both statically-typed and dynamically-typed programming languages can parse arbitrary data.

                                                                          1. 0

                                                                            The answer is: No. Both statically-typed and dynamically-typed programming languages can parse arbitrary data.

                                                                            That was never the topic; you can parse arbitrary data with a pen and a piece of toilet paper…

                                                                            1. 2

                                                                              That was never the topic

                                                                              Yes it was. Perhaps you should have actually read the article.

                                                                              you can parse arbitrary data with a pen and a piece of toilet paper…

                                                                              At this point, it is clear you are not even trying to add anything constructive. I suggest we leave this discussion here.

                                                                              1. 0

                                                                                Oof, I guess there had to be first failed discussion experience here on lobsters. I’m sorry but you are absolutely inept at discussing this. Maybe it’s better if we don’t continue this. Cheers.

                                                                      3. 2

                                                                        The grandparent’s code example uses way more than one type.

                                                                    1. 5

                                                                      Didn’t learn it this week, but definitely came up in the last 6 months. From Python 3.5 you can splat multiple dictionaries together.

                                                                      c = {**a, **b}

                                                                      This is a nice little trick for merging dictionaries without some multi-line mess.

                                                                      Highly recommend checking out Raymond Hettinger’s twitter. He will post some interesting tweet-sized tricks and other Python trivia

                                                                      1. 2

                                                                        This one is neat! What happens when the same key exists in both dicts? Does b take precedence? Actually… I figured I would just test it out. b takes precedence.

                                                                        Thanks for posting this! Makes many things much easier to deal with :)

                                                                      1. 19

                                                                        Soundness is definitely a thing that can come up, though in handling tens of thousands of lines I don’t feel like I’ve hit it directly.

                                                                        On the other hand, Typescript’s type system is still the most useful typesystem I have ever used. The way unions, “everything is a dictionary”, being able to filter out keys and the like, and everything else work together mean that I can write business logic that handles really dynamic data shapes, and still get guarantees that are basically impossible in other languages without generating loads of wrapper types.

                                                                        For example just being able to inline declare a variable of having “type A and type B” is such a great way of allowing for new APIs.

                                                                        Ultimately Typescript not seeing typing as a mechanism for implementing the language execution semantics, but really as a checker of code validity, opened it up to really practical advantages.

                                                                        1. -2

                                                                          On the other hand, Typescript’s type system is still the most useful typesystem I have ever used.

                                                                          I have a canary of sorts which I use to guess whether I will appreciate a type system.

                                                                          1 == "1"
                                                                          

                                                                          The correct way to handle this is to issue a type error at compile time (or at least runtime if the language doesn’t have a significant check phase in the compiler). The two ways to fail this are:

                                                                          • typecheck, return false (python2, python3, ruby, crystal do this)
                                                                          • typecheck, return true (typescript, javascript, php do this)

                                                                          I haven’t made final decisions which of these is scarier.

                                                                          Interestingly, some of the above languages do fine if we use inequality instead:

                                                                          1 >= "1"
                                                                          

                                                                          Some languages from the top of my head that do this the way I like it: Haskell (and I assume many of the other strongly typed functional languages as well), Common Lisp (at least sbcl, dunno if it’s standard), Rust, Zig, Nim.

                                                                          1. 9

                                                                            To be fair you should always use === in TypeScript, then it works as expected.

                                                                            1. 6

                                                                              To be fair you should always use === in TypeScript

                                                                              To be even fairer, this is not even a Typescript thing, it’s a Javascript thing? A linter on a JS project will complain about the use of ==, without going near Typescript.

                                                                              1. 3

                                                                                Throwing my 2-cents in here, PHP is the same. Using === will enforce type checking on the comparison.

                                                                                1. 3

                                                                                  I don’t think it “type checks” per se in PHP, but rather avoids doing an implicit conversion.

                                                                                  1 == "1" evaluates to true, while 1 === "1" evaluates to false. It won’t error out, like a type checker will typically do for you.

                                                                                2. 1

                                                                                  To be fairest, Typescript actually does this the way I want. See https://lobste.rs/s/qfpbk9/is_typescript_worth_it#c_hs0olb

                                                                                3. 2

                                                                                  Does it error, or does it return false like python, ruby, crystal (and js with ===)?

                                                                                  1. 2

                                                                                    It does error at compile time.

                                                                                    1. 1

                                                                                      Yeah, seems like both == and === work as I want in Typescript. See https://lobste.rs/s/qfpbk9/is_typescript_worth_it#c_hs0olb

                                                                                      I’m liking Typescript right now.

                                                                                  2. 1

                                                                                    Ah, forgot about that. Do ts linters warn about ==?

                                                                                    1. 2

                                                                                      Yeah they do.

                                                                                  3. 2

                                                                                    I don’t understand why a typecheck of the integer numeral 1 and a string happening to contain the numeral one returning false is scary.

                                                                                    I know nearly zero about type theory. Could you please explain?

                                                                                    1. 6

                                                                                      Because it’s a category mistake – it’s a nonsensical question.

                                                                                      1. 5

                                                                                        Ah I think I see, so that’s what demands that the response be an exception rather than false.

                                                                                        It’s not just “Is a number equal to a string?” “No.” it’s “That’s not even a sensical question to ask.”

                                                                                        I guess I feel like that’s a bit counter intuitive, since I generally want boolean operators to provide answers in boolean form, but then I don’t know anything about category theory either.

                                                                                        sigh clearly I have a lot to learn :)

                                                                                        1. 9

                                                                                          Well, it depends. Mathematically there’s nothing “wrong” with it, but it raises some uncomfortable questions. Like “what’s the domain of the == function?” Turns out, equality is not a mathematical function! If it was, everything would be in its domain, aka the forbidden Set Of All Sets. There’s a couple ways around this: the type way is to say that == is actually a collection of functions, one for each type. This ends up working out, and makes comparing different types impossible.

                                                                                          But there are other formulations! Like there’s nothing wrong with saying int and str always compare false, you just have to be clear what your mathematical axioms are.

                                                                                          1. 4

                                                                                            I think it might help to look at the Haskell signature for ==:

                                                                                            (==) :: a -> a -> Bool

                                                                                            It makes it clear that you can only compare two values of the same type.

                                                                                            1. 4

                                                                                              In Lisps, you typically have = and equal?/equalp. With = you’re asking “are these two things numerically equal”, which is a nonsensical thing to ask about a string and an integer (or a vector and a list, or a character and a boolean, or two other non-number objects). With equal? you’re asking “are these two objects structurally the same thing?”, which makes sense and “false” would be the obvious answer for objects of different types.

                                                                                              So presumably in many languages, == is the equal? in the above description, but there’s no separate = predicate.

                                                                                              And then there’s eq?/eqp, which asks “are these things the same object?”, which is a question of identity, not equality. That’s similar to what the is operator does in Python, for example. But yeah, this stuff is confusing if you’ve only been exposed to languages that conflate these different ways of comparing because you’re not used to having to make the choice of which kind of comparison to use in different situations.

                                                                                              1. 1

                                                                                                so that’s what demands that the response be an exception rather than false

                                                                                                I think no one wants it to throw an exception, but to simply not compile.

                                                                                          2. 2

                                                                                            1 == "1" is a classic JS gotcha (double equals is not “check for equality” but “check for equality and if it fails check for toString equality”) and 1 === "1" does what you expect.

                                                                                            I have decently used “fancy type” languages like Haskell, Purescript and Rust. Their type systems still make me somewhat unhappy compared to TS for “enterprise apps” (the one thing that I kinda wish that TS had but would bef impossible given soundness and how stuff works is the return type polymorphism).

                                                                                            1. 2

                                                                                              1 == "1" is a classic JS gotcha

                                                                                              I call it a classic maldesign, but ok :)

                                                                                              1. 3

                                                                                                I mean it’s not good, and there’s a reason you don’t see == anywhere in most codebases.

                                                                                                I guess it’s just not an issue that one hits in practice if you’re an experienced practitioner (compare to…. array bounds errors that even experienced C programmers will hit often). It’s a thing you set your linter to notice, and then never do it.

                                                                                            2. 1

                                                                                              Couldn’t edit this any longer, so will make an errata to my comment and mark it incorrect:

                                                                                              Contrary to what I stated, Typescript does this exactly as I think is best, i.e. signal an error at compile time.

                                                                                              vegai@Vesas-iMac ~/tmp> cat derp.ts
                                                                                              
                                                                                              let derp1 = 1 == "1";
                                                                                              let derp2 = 1 === "1";
                                                                                              
                                                                                              
                                                                                              vegai@Vesas-iMac ~/tmp> tsc derp.ts
                                                                                              derp.ts:2:13 - error TS2367: This condition will always return 'false' since the types 'number' and 'string' have no overlap.
                                                                                              
                                                                                              2 let derp1 = 1 == "1";
                                                                                                            ~~~~~~~~
                                                                                              
                                                                                              derp.ts:3:13 - error TS2367: This condition will always return 'false' since the types 'number' and 'string' have no overlap.
                                                                                              
                                                                                              3 let derp2 = 1 === "1";
                                                                                                            ~~~~~~~~~
                                                                                              
                                                                                              
                                                                                              Found 2 errors.
                                                                                              

                                                                                              Don’t even need the strict mode. So apologies to everyone for that bit of bullshitting – I went to check the behaviour in the first typescript playground I found, which for some reason did not behave like this.

                                                                                          1. 7

                                                                                            If you look at stuff like Chrome devtools, or even the jetbrains tooling for debugging, I feel like writing software and debugging it has become a lot more accessible.

                                                                                            I remember struggling a lot as a kid to figure out how to get even the most basic of toolchains running. Nowadays things have “real documentation” (not always of course), and there’s some hope of using a tool without having to read its entire manual front-to-back

                                                                                            Some people consider that a negative. Personally I’m not interested in doing a first-principles study of every CLI tool I use

                                                                                            1. 6

                                                                                              I started programming on the MSX in my early teens. The MSX is roughly similar to the C64 or BBC Micro: you turn it on, you’re dropped in a BASIC environment, and you can program.

                                                                                              After we got a Windows machine things were a lot harder; remember, this is around 1998 and I was 14-year old non-native English speaker. I got “Sam’s teach yourself C++ in 10 minutes” in my native language from the local bookstore, which is probably the shortest programming book I’ve seen to date. I think the 4th or 5th chapter introduced templates. You can imagine how well that went for me. In hindsight, that book was not just a waste of my pocket money, it also did me a massive disservice by making me believe I wasn’t smart enough for modern (at the time) programming.

                                                                                              Also, getting a development environment meant getting bootlegged copied of Visual Studio. It was a pain. Perhaps there was an easier way back then, but it was non-obvious, at least for me.

                                                                                              So, I stopped programming for a few years. It until I installed Linux (which got replaced by FreeBSD a bit later) around 2004 that I really got back in programming, as by that time it was so much easier to get started especially on the Unix-y environment.

                                                                                              With “web as OS” we’re essentially back to the MSX/C64/BBC Micro days,where you start the machine and you’re dropped in a programmable environment. Regardless of the criticisms on the web tech and how it’s used, I feel this is a very important and powerful advantage that’s often overlooked.

                                                                                              1. 1

                                                                                                That’s a little harsh given some environments were easy to get if it wasn’t C++. Quite a few languages had installers that let you just get right to it with good documentation. ActiveState’s for Perl and Python on Windows let me experiment with them quickly. I remember I used FreeBASIC for one experiment. It was easy to download, easy to compile, and its commands were online. Lisp had Lisp in a Box with Practical Common Lisp online.

                                                                                                The browser environments are certainly more seemless. It’s just not a huge leap from providing a doc/ebook and an installer. There’s just an extra step in the second scenario. Once installed, you’re doing the same thing. C++ and its tooling are just their own level of pain. I found that out after trying to quickly absorb it using Sams Teach Yourself C++ in 21 Days a long, long time ago. ;) Hey, look what (pdf) I just randomly found checking to see if I remembered its name.

                                                                                                1. 1

                                                                                                  Oh yeah, there were undoubtedly many things out there that I simply didn’t know about; after all, I was just a 14-year old kid who had no idea what he was doing. We didn’t even have internet at first, that didn’t come until half a year later or so (dialup, of course, so you had to be quick about it!)

                                                                                                  But that was kind of my point: you really had to search for solutions and at least vaguely know what you were doing, which was quite a different experience from a machine that’s “programmable by default”.

                                                                                                  1. 1

                                                                                                    Oh I see what you mean. I had the same problem. Google wasn’t there. Had to go with whatever was on the machine, outdated books in thrift stores, etc. My early ideas about programming were probably way off, too. I can’t remember them now.

                                                                                                2. 1

                                                                                                  Curious if you’re still using FreeBSD nowadays?

                                                                                                  …massive disservice by making me believe I wasn’t smart enough for modern (at the time) programming.

                                                                                                  I feel as if modern programmers (pesky humans) have a knack for doing this to others just getting started or wanting to grow. Curious too how you think people have changed since the time you read that book?

                                                                                                  1. 2

                                                                                                    Curious if you’re still using FreeBSD nowadays?

                                                                                                    No, I stopped using it when the old pkg_* stopped working and everyone had to forcibly upgrade to pkgng. There were many bugs (broke literally all four of my systems) and the state of it back then was horrible. I didn’t like the design in the first place, but if it had at least worked it would have been palatable, but it didn’t even do that.

                                                                                                    I don’t know what the current state is; incidentally there was a HN thread yesterday which said it got better, although it still has weird bugs 🤷‍♂️

                                                                                                    …massive disservice by making me believe I wasn’t smart enough for modern (at the time) programming. I feel as if modern programmers (pesky humans) have a knack for doing this to others just getting started or wanting to grow. Curious too how you think people have changed since the time you read that book?

                                                                                                    The book was just really bad; I mean, the entire title is obviously just complete bullshit: teaching anyone C++ is 10 minutes is nothing short of an ludicrous and fraudulent claim, and the content of the book was to match. Applicable. But … I didn’t know any of that at the time.

                                                                                                    There are still really bad books out there; a few years ago a friend was trying to learn C for some embedded project she had to do for uni (industrial design studies). Her C book was beyond horrible; even I had trouble understanding some parts and I actually already know how to program in C. I offered her my copy of K&R – which is probably also not the best introductory book, but much better than what she had – but by this point she was sufficiently demotivated to just give up.

                                                                                                    Books are like software: a lot just gets written by some rando who just wants to make a buck and doesn’t really care. If you put something on the market with a bit of advertising around it, you’re going to get buyers because the quality can be rather hard to appraise before-hand.

                                                                                                    Nowadays, there are a lot more resources in the form of online content, forums, Stack Overflow, YouTube, Kahn academy, meetups, code camps, what-have-you. It’s not like this was completely absent back in the day, but now it’s so much more accessible, especially for interested 14-year olds. To be honest, I think sometimes there’s a bit too much focus on helping new users.

                                                                                              1. 2

                                                                                                Anyone know how to run this on my Desktop without installing Android Studio? I am not too familiar with how Android applications are structured.

                                                                                                1. 3

                                                                                                  Reading https://github.com/yairm210/UnCiv#is-there-a-desktop-version it sounds like you don’t need Android Studio at all to just play the game

                                                                                                  1. 2

                                                                                                    Yup, I grabbed the jar from the releases and it worked out of the box with java -jar Unciv.jar.