• 0 Posts
  • 18 Comments
Joined 1 year ago
cake
Cake day: June 25th, 2023

help-circle



  • Obviously the actual programs are trivial. The question is, how are the tools supposed to be used?

    So you say to use deno? Out of all the tutorials I found telling me what tools to use, that wasn’t one of them (I really thought this “typescript” package would be the thing I was supposed to use; I just checked again on a hot cache and it was 1.7 seconds real time, 4.5 seconds cpu time, only 2.9 seconds if I pin everything to a single core). And I swear I just saw this week, people saying “seriously, don’t use deno”. It also doesn’t seem to address the browser use case at all though.

    In other languages I know, I know how to write 4 files (the fib library and 3 frontends), and compile and/or execute them separately. I know how to shove all of them into a single blob with multiple entry points selected dynamically. I know how to shove just one frontend with the library into a single executable. I know how to separately compile the library and each frontend, producing 4 separate artifacts, with the library being dynamically replaceable. I even know how to leave them as loose files and execute them directly (barring things like C). I can choose between these things all in a single codebase, since there are no hard-coded project filenames.

    I learned these things because I knew I wanted the ability from previous languages I’d learned, and very quickly found how the new language’s tools supported that.

    I don’t have that for TS (JS itself seems to be fine, since I have yet to actually need all the polyfill spam). And every time I try to find an answer, I get something that contradicts everything I read before.

    That is why I say that TS is a hopelessly immature ecosystem.




  • I’ve only ever seen two parts of git that could arguably be called unintuitive, and they both got fixes:

    • git reset seems to do 2 unrelated things for some people. Nowadays git restore exists.
    • the inconsistent difference between a..b and a...b commit ranges in various commands. This is admittedly obscure enough that I would have to look up the manual half the time anyway.
    • I suppose we could call the fact that man git foo didn’t used to work unintuitive I guess.

    The tooling to integrate git submodule into normal tree operations could be improved though. But nowadays there’s git subtree for all the people who want to do it wrong but easily.


    The only reason people complain so much about git is that it’s the only VCS that’s actually widely used anymore. All the others have worse problems, but there’s nobody left to complain about them.


  • Python 2 had one mostly-working str class, and a mostly-broken unicode class.

    Python 3, for some reason, got rid of the one that mostly worked, leaving no replacement. The closest you can get is to spam surrogateescape everywhere, which is both incorrect and has significant performance cost - and that still leaves several APIs unavailable.

    Simply removing str indexing would’ve fixed the common user mistake if that was really desirable. It’s not like unicode indexing is meaningful either, and now large amounts of historical data can no longer be accessed from Python.


  • Unfortunately both of those are used in common English or computer words. The only letter pairs not used are: bq, bx, cf, cj, dx, fq, fx, fz, hx, jb, jc, jf, jg, jq, jv, jx, jz, kq, kz, mx, px, qc, qd, qg, qh, qj, qk, ql, qm, qn, qp, qq, qr, qt, qv, qx, qy, qz, sx, tx, vb, vc, vf, vj, vm, vq, vw, vx, wq, wx, xj, zx.

    Personally I have mappings based on <CR>, and press it twice to get a real newline.



  • Even logging can sometimes be enough to hide the heisgenbug.

    Logging to a file descriptor can sometimes be avoided by logging to memory (which for crash-safety includes the possibility of an mmap’ed file, since the kernel will just take care of them as long as the whole system doesn’t go down). But logging from every thread to a single section of memory can also be problematic (even without mutexes, atomics can be expensive and certainly have side-effects) - sometimes you need a separate per-thread log, and combine in the log-reader tool.



  • I haven’t managed to break into the JS-adjacent ecosystem, but tooling around Typescript is definitely a major part of the problem:

    • following a basic tutorial somehow ended up spending multiple seconds just to transpile and run “Hello, World!”.
    • there are at least 3 different ways of specifying the files and settings you want to use, and some of them will cause others to be ignored entirely, even though it looks like they should be used.
    • embracing duck typing means many common type errors simply cannot be caught. Also that means dynamic type checks are impossible, even though JS itself supports them (admittedly with oddities, e.g. with string vs String).
    • there are at least 3 incompatible ways to define and use a “module”, and it’s not clear what’s actually useful or intended to be used, or what the outputs are supposed to be for different environments.

    At this point I’m seriously considering writing my own sanelanguage-to-JS transpiler or using some other one (maybe Haxe? but I’m not sure its object model allows full performance tweaking), because I’ve written literally dozens of other languages without this kind of pain.

    WASM has its own problems (we shouldn’t be quick to call asm.js obsolete … also, C’s object model is not what people think it is) but that’s another story.


    At this point, I’d be happy with some basic code reuse. Have a “generalized fibonacci” module taking 3 inputs, and call it 3 ways: from a web browser on the client side, as a web browser request to server (which is running nodejs), or as a nodejs command-line program. Transpiling one of the callers should not force the others to be transpiled, but if multiple of the callers need to be transpiled at once, it should not typecheck the library internals multiple times. I should also be able to choose whether to produce a “dynamic” library (which can be recompiled later without recompiling the dependencies) or a “static” one (only output a single merged file), and whether to minify.

    I’m not sure the TS ecosystem is competent enough to deal with this.


  • All of these can be done with raw strings just fine.

    For the first pathlib bug case, PATH-like lookup is common, not just for binaries but also data and conf files. If users explicitly request ./foo they will be very upset if your program instead looks at /defaultpath/foo. Also, God forbid you dare pass a Path("./--help") to some program. If you’re using os.path.dirname this works just fine.

    For the second pathlib bug case, dir/ is often written so that you’ll cause explicit errors if there’s a file by that name. Also there are programs like rsync where the trailing slash outright changes the meaning of the command. Again, os.path APIs give you the correct result.

    For the article mistake, backslash is a perfectly legal character in non-Windows filenames and should not be treated as a directory component separator. Thankfully, pathlib doesn’t make this mistake at least. OTOH, / is reasonable to treat as a directory component separator on Windows (and some native APIs already handle it, though normalization is always a problem).

    I also just found that the pathlib.Path constructor ignores extra kwargs. But Python has never bothered much with safety anyway, and this minor compared to the outright bugs the other issues cause.



  • I’ve done something similar. In my case it was a startup script that did something like the following:

    • poll github using the search API for PR labels (note that this has sometimes stopped returning correct results, but …).
      • always do this once at startup
      • you might do this based on notifications; I didn’t bother since I didn’t need rapid responsiveness. Note that you should not do this for the specific data from a notification though; it’s only a way to wake up the script.
      • but no matter what, you should do this after N minutes, since notifications can be lost.
    • perform a git fetch for your main development branch (the one you perform the real merges to) and all pull/ refs (git does not do this by default; you’ll have to set them up for your local test repo. Note that you want to refer to the unmerged commits for these)
    • if the set of commits for all tagged PRs has not changed, wait and poll again
    • reset the test repo to the most recent commit from your main development branch
    • iterate over all PRs with the appropriate label:
      • ordering notes:
        • if there are commits that have previously tested successfully, you might do them first. But still test again since the merge order could be different. This of course depends on the level of tests you’re doing.
        • if you have PRs that depend on other PRs, do them in an appropriate order (perhaps the following will suffice, or maybe you’ll have some way of detecting this). As a rule we soft-forbid this though; such PRs should have been merged early.
        • finally, ordering by PR number is probably better than ordering by last commit date
      • attempt the merge (or rebase). If a nop, log that somewhere. If not clean, skip the PR for now (and log that), but only mark this as an error if it was the first PR you’ve merged (since if there’s a conflict it could be a prior PR’s fault).
      • Run pre-build stuff that might need to create further commits, build the product, and run some quick tests. If they fail, rollback the repo to the previous merge and complain.
      • Mark the commit as apparently good. Note that this is specifically applying to commits not PRs or branch names; I admit I’ve been sloppy above.
    • perform a pre-build, build and quick test again (since we may have rolled back and have a dirty build - in fact, we might not have ended up merging anything!)
    • if you have expensive tests, run them only here (and treat this as “unexpected early exit” below). It’s presumed that separate parts of your codebase aren’t too crazily entangled, so if a particular test fails it should be “obvious” which PR is relevant. Keep in mind that I used this system for assumed viable-work-in-progress PRs.
    • kill any existing instance and launch a new instance of the product using the build from the final merged commit and begin accepting real traffic from devs and beta users.
    • users connecting to the instance should see the log
    • if the launched instance exits unexpectedly within M minutes AND we actually ended up merging anything into the known-good branch, then reset to the main development branch (and build etc.) so that people at least have a functioning test server, but complain loudly in the MOTD when they connect to it. The condition here means that if it exits suddenly again the whole script goes up and starts again, which may be necessary if someone intentionally tried to kill the server to force a new merge sequence but it was too soon.
      • alternatively you could try bisecting the set of PR commits or something, but I never bothered. Note that you probably can’t use git bisect for this since you explicitly do not want to try commit from the middle of a PR. It might be simpler to whitelist or blacklist one commit at a time, but if you’re failing here remember that all tests are unreliable.

  • I likewise don’t really use Godot, but for graphics in general, the 4th coordinate is important, even if it is “usually” 1. It’s most obvious to correctly interpolate near the poles of a sphere with a single rectangular texture, but think for a minute what “near” means.

    Back to the main point though: the important things we normally rely on for matrix math are associativity (particularly, for exponentiation!) and anticommutativity (beware definitions that are sloppy about “inverse”).


  • o11c@programming.devtoProgrammer Humor@programming.devBorrow Checker
    link
    fedilink
    English
    arrow-up
    5
    arrow-down
    2
    ·
    1 year ago

    What you are missing, of course, is the Rc<Refcell<T>> that you have to stick everywhere to make a nontrivial Rust program. It’s like monads in Haskell, parentheses in lisp, verbosity in Java, or warnings in C - they’re the magic words you have to incant correctly to make things work in their weird paradigms.