1. 84
    1. 14

      @hwayne, you have to link to Frink’s Sample Calculations. You just gotta.

      1. 4

        Also Frink’s default units page is a good read for education and entertainment value, linked to from another one of @hwayne’s posts. I can’t get enough of metrology drama when I see it

      2. 13

        It’s hard for me to keep a bunch of languages in my head at once; if I write in a language I haven’t used in a while, my brain starts demand-paging from the documentation and it feels terribly awkward. So for me terseness is bad — a toolbox language needs to have a clear syntax, and hopefully good autocomplete and easy access to docs.

        (I’m not even good with shells, compared with many/most of you. Partly because sh-type shells are so terse and cryptic. But I know enough that I think it’d be awkward to switch to some modern shell like NuShell.)

        1. 3

          I’m not even good with shells, compared with many/most of you. Partly because sh-type shells are so terse and cryptic.

          Obligatory xkcd.

          1. 3

            The number of languages I’m comfortable using on an occasional basis has genuinely skyrocketed thanks to LLM assistance. I frequently write code in Bash, jq, AppleScript and Go now - all languages which I had previously avoided using because I hadn’t achieved enough fluency to be productive in them without having to look things up every 30 seconds.

            1. 1

              When you say LLMs, are you referring to a Copilot-like integration or do you use the chat UI or the CLI you’ve built?

              I’d like to be fluent in more languages although I don’t write code in non-$WORK languages due to a poor working memory and I suspect I’m not using LLMs to my benefit here.

              1. 1

                All three, but mostly the web UIs or mobile apps for Claude 3.5 Sonnet and GPT-4o - they’re still the most convenient way to access those models.

          2. 12

            Scrolled to see if Raku would be included before I realized this was a Hillel Wayne joint. We who toil on a project which explicitly aims to torment the implementor for the sake of the user are beyond glad to have him as an advocate.

            1. 8

              Something similar to Frink is Rink. I think it was meant to improve on Frink in some way(s), but I’ve forgotten exactly how and its documentation doesn’t seem to say.

              1. 6

                One big difference I can think of is that Rink is open source, whereas Frink is not. (Whether that’s an improvement that matters to the user, I suppose that depends on the user.)

                1. 2

                  Excited to hear about Frink / Rink. I had hoped that Soulver would be a good way to solve simple dimensional analysis problems, but I always ended up fighting its parser and in the end never really used it.

                  Has anyone used Mathematica for day-to-say calculations? I’ve had minimal exposure to it, but I was also under the impression it could be used for this kind of thing.

                2. 3

                  That’s neat, Frink is a really cool language but actually running it is a bit weird - it’s distributed as a jar that has a built in gui and a cli which if I recall correctly is pretty barebones. I would use it more if it had a nicer cli, so Rink is interesting to me!

                3. 7

                  K and Q, with their support for ragged lists and dictionaries instead of exclusively structuring data as rectangular arrays, are much more convenient for working with strings and loosely-structured data than J. K has many fewer primitives than J, but Q is more comparable. Contrast the q reference card to the J NuVoc.

                  Lil carries some features forward from Q, but is generally a smaller language- somewhere between K and Q. Whether it’s comparable or inferior will depend on what sorts of things you’re trying to do. Lil is largely shaped by concerns of graphics programming, text munging/formatting, and CRUD to glue user interfaces together.

                  1. 6

                    I’ve always been array-curious, and finally found out about Uiua, which is array-based but also stack based. It has a very fun web UI for trying things out, a great discord community for asking about how to do stuff, and generally just feels super powerful.

                  2. 5

                    For testing HTTP requests, I use Hurl. Never scraped HTML with it though, I suppose it could be piped to htmlq?

                    Frink is looks very interesting, thank you for sharing. Love the favicon on their website!

                    My own list would include Nix; the language itself is not very “toolbox”, but Nixpkgs lets me easily mix & template many different toolbox languages. Unfortunately, I haven’t figured out how to lint these embedded snippets, so I try to keep them under ~10 lines. It would be a cool project: detect snippets, assign linter, ignore ${} and profit.

                    I’d also add some nice subset of Regex :)

                    1. 5

                      Javascript, so I can modify other people’s websites via the dev console

                      Something that makes webscraping and parsing as easy as calculation. Requests and bs4 ain’t it.

                      I think these are the same thing!

                      The most ergonomic web scraping is the devtools console, JavaScript, and querySelectorAll with CSS selectors


                      When you see all the story boxes on my blog, I don’t write those by hand … I wrote a little web scraper for lobste.rs, HN, and reddit a few years ago that generates them:

                      https://www.oilshell.org/blog/2024/06/cgi.html#history-motivation

                      The JS is pretty clean and short - https://github.com/oilshell/hashdiv/blob/master/stories.js

                      But the key is to interactively develop it by clicking. The browser helps you a lot

                      The problem is that JS is sandboxed. So I just POST it to a web server, by dynamically generating a form URL!

                      https://github.com/oilshell/hashdiv

                      Then I copy and paste it into my markdown as HTML.


                      A concatenative PL if I ever find out what small problems a CPL is really good for

                      I am working on a hybrid of Tcl, Lisp, and Forth, which can express shell … it has been surprisingly enlightening (despite the fact that I played with those languages many years ago)

                      A few things lead to this: a terse syntax means typing less. Lots of builtins means less writing basic stuff myself.

                      The nice thing concatenative style is that you literally “append code to the end” – that’s right in the name!

                      If you are iteratively developing, it is a very nice feeling to just append to the end. Shell pipelines are like that too

                      ls | grep foo | xargs bar | grep ...
                      

                      I never used Forths for anything real, but jq also has a bit of a feeling of Forth. It has the pipeline syntax, and it has the . which is similar to like “top of stack”, but there’s no real stack in jq.


                      jq is unfortunately a sit down and learn it type of language. Unfortunately it seems hard to pick up on the fly.

                      I have found a ton of confusion on it online.

                      I spent a whole day last week writing examples and now I pretty much get it. It is certainly powerful and general, but I am unsure if some parts of the model actually fit the problem (the PEG thing – bactracking, cartesian product – I actually think of these as superfluous, at least for what I want to do)

                      https://lobste.rs/s/eeqpis/jqjq_jq_implementation_jq

                      There are also some places that jq syntax collides badly with itself. For example the comma operator

                      a, b, c
                      

                      is “generate multiple values”, but that’s unrelated to list literals [a, b, c].

                      And it also seems to conflict badly with function args – you write f(a; b; c) rather than f(a, b, c) because of this syntax conflict.

                      1. 4

                        The most ergonomic web scraping is the devtools console, JavaScript, and querySelectorAll with CSS selectors

                        That’s why I built my shot-scraper javascript tool - https://shot-scraper.datasette.io/en/stable/javascript.html - it lets you run a headless browser directly from the terminal, have it load up a page, execute a fragment of JavaScript (e.g. scrape some content with a CSS selector) and dump the results to the console:

                        shot-scraper javascript 'https://www.google.com/search?q=shot-scraper' '
                        Array.from(
                          document.querySelectorAll("h3"),
                          el => ({href: el.parentNode.href, title: el.innerText})
                        )' | jq
                        
                        1. 1

                          Oh cool, I need something like that for screenshots …

                          I used Phantom JS many years ago, but it looks like the world has changed.

                          Honest question: is shot-scraper like a Python version of puppeteer? That is a project I heard of but haven’t used. It looks like a JS API for headless chrome.

                          https://pptr.dev/

                          (Also I am wondering if having an entire browser installed through npm or pip is unwieldy … which one is more stable / has recent updates?)

                          1. 3

                            shot-scraper is a relatively thin Python CLI wrapper around the Playwright Python library.

                            Playwright is effectively Puppeteer 2 - it was built by a team at Microsoft many of whom had previously worked on Puppeteer at Google.

                            The Playwright Python library is great: it pip installs cleanly (using binary wheels) and can then download the custom browser binaries that it needs.

                            Amusingly, if you crack open the Python wheel file you’ll find a full copy of the Node.js binary! The Python library works by running that in a subprocess and wrapping the official JavaScript Node bindings.

                            1. 2

                              Ah OK thanks, I didn’t know about Playwright at all

                              I was wondering if the difference is Python vs. JavaScript APIs, but it seems like it’s not just that:

                              https://medium.com/front-end-weekly/playwright-vs-puppeteer-choosing-the-right-browser-automation-tool-in-2024-d46d2cbadf71

                              Playwright and Puppeteer are both tools that let developers control web browsers programmatically. The main difference is that Playwright can easily handle multiple pages at once and simulate different devices. Puppeteer is simpler and is often used for automating browser tasks in both headless mode (no visible UI) and headful mode (with UI).

                              Overall, Playwright is ideal for testing complex web applications. Puppeteer is ideal for straightforward testing and web scraping tasks.

                              Right now I just need screenshots, not full web app testing, so I guess I prefer the lower level one. And having both nodejs and a browser behind pip more horrifies me than amuses me :-/ :-P

                              1. 1

                                That article looks suspicious to me, I’m getting small vibes of might-be-written-by-an-LLM from it.

                                Having worked with both Puppeteer and Playwright my opinion is that Playwright is just better than Puppeteeer in every way. There’s no situation in which I would chose Puppeteer over Playwright.

                                1. 1

                                  OK well I literally googled “playright vs. puppeteer” and clicked the first result, so yeah I think that’s a fairly reliable way of getting served “LLM slop” these days … :-/

                                  (I switched to Bing on certain devices for awhile, but I imagine that’s an even more reliable way of getting LLM slop now)

                                  But I dunno the answer seemed roughly inline with the idea that Puppeteer is node.js + browser, and Playright is Python + node.js + browser. I guess I’m allergic to standing on too many layers of abstraction, because I often end up debugging them …

                                  We’ll see if I actually get some screenshots on my blog, probably not with all the other things to do :-/

                                  (edit: probably the thing I need more of is terminal screenshots, not browser, so yeah it’s slightly low priority)

                                  1. 2

                                    Playwright is Node.js plus browser too: https://playwright.dev/

                                    It also supports Python, Java and C# but I believe those are all wrappers around the default Node.js implementation.

                                    Here’s how I automate screenshots for my own blog using Playwright via shot-scraper: https://til.simonwillison.net/shot-scraper/social-media-cards

                              2. 1

                                Is playwright based on WebDriver? I’ve done some fairly basic stuff with raw WebDriver because most of the browser automation stuff is aimed at testing (the test framework stuff just gets in the way) and/or comes with a hulking framework of fearsome complexity and dubious utility and/or introduces a huge language runtime where I already had too many.

                                1. 2

                                  No, Playwright doesn’t use WebDriver.

                                  It uses the Chrome DevTools Protocol websocket mechanism for Chrome, the WebKit debug protocol for Safari (similar to the Chrome one) and for Firefox bundles a custom Firefox build with a custom addon called Juggler: https://github.com/microsoft/playwright/tree/main/browser_patches/firefox/juggler

                                  1. 1

                                    Ah, I can imagine that allows it to be a lot better than WebDriver, which is pretty unforgiving. (CasparJS/PhantomJS were more complicated and just as troublesome.) I was using geckodriver which acts as a WebDriver to Firefox protocol conversion proxy; I didn’t find out if Firefox’s native protocol lifted any of the limitations. I never did work out how to inject my own JavaScript, which might have made a few things easier.

                          2. 3

                            There are also some places that jq syntax collides badly with itself. For example the comma operator

                            a, b, c

                            is “generate multiple values”, but that’s unrelated to list literals [a, b, c].

                            That’s not how it works, I believe. There are no list literals.

                            https://jqlang.github.io/jq/manual/#array-construction:

                            Once you understand the “,” operator, you can look at jq’s array syntax in a different light: the expression [1,2,3] is not using a built-in syntax for comma-separated arrays, but is instead applying the [] operator (collect results) to the expression 1,2,3 (which produces three different results).

                            If you have a filter X that produces four results, then the expression [X] will produce a single result, an array of four elements.

                            I suppose that functions use a different syntax since their argument count is static (not a generator).

                            Edit: JQ is the most beautiful programming language I have seen to date.

                            1. 2

                              Oh! Well I think that explains this quirk I was wondering about …

                              • range(3) inside {} produces 3 container
                              • range(3) inside [] produces 1 container

                              So yeah I misunderstood the language, but I’d say that is a bad design … if your goal is to be familiar to people who use JSON.

                              I was thinking I would have made the , operator 1 & 2 & 3, and then simply made List consistent with Dict.

                              I don’t see what you gain by combining the two things, other than it being easier to implement perhaps.

                              Then it also wouldn’t conflict with f(1,2), which is also what JS/Python/C/every language users would expect …

                              $ echo 3 | jq '{a: range(3)}'
                              {
                                "a": 0
                              }
                              {
                                "a": 1
                              }
                              {
                                "a": 2
                              }
                              $ echo 3 | jq '[range(3)]'
                              [
                                0,
                                1,
                                2
                              ]
                              
                              

                              I can certainly see why someone would find jq beautiful / elegant, but I also found so many people confused about it, even after in-depth articles.


                              And even on that thread @wader (who we have to consider the world’s expert in jq now :) ) said:

                              https://lobste.rs/s/eeqpis/jqjq_jq_implementation_jq#c_q5fon5

                              Probably but as I mention above I’m not sure there ever was any grand idea to start with :) maybe Nico one the jq old-timer knows more.

                              I think there is a “better jq” waiting to get out.

                              Also many people have already started such projects:

                              https://github.com/01mf02/jaq

                              1. 2

                                if i saw f(1,2) out of context i’d think it’s f(12/10)

                            2. 2

                              Do you have a link or anything for your concatenative(ish?) project?

                              1. 1

                                The most ergonomic web scraping is the devtools console, JavaScript, and querySelectorAll with CSS selectors

                                That’s why I use python and playwright. Usually when I’m scraping the web, I ultimately want to feed the results to a python script. I get most of the ergonomics of just using devtools console, and the results get right to where I want them, almost as easy as calculation (and certainly easier than requests/bs4 for most of the scraping things I want to do).

                                1. 1

                                  The nice thing concatenative style is that you literally “append code to the end” – that’s right in the name!

                                  A nice observation. Operators are generally like that. Append “+ 4” to “3” and get 7. In your example the operator is “|”.

                                2. 4

                                  For math builtins, Mathematica is hard to beat. I think it might be my current go-to for many of the examples you show here, although I bounce around between languages a bit. Pretty different part of the PL design space than your J and Raku examples though, as there isn’t much syntactic sugar and it’s not particularly terse. But still less clunky then the equivalent Python imo (especially if what you’re doing needs multiple pip-installed packages and glue code between them). Also, expensive, but we have a site license.

                                  1. 3
                                    • I keep wanting to reach for a tool for quick unit conversions and especially time/duration math, definitely going to check out Frink!
                                    • Sad to see jq only mentioned in the footnotes, it’s excellent for JSON processing, but I’ve really enjoyed pushing it beyond its boundaries. I’ve gotten this far without fully learning Awk or Perl, and I think that’s only because I’ve (ab)used jq whenever I hit a situation where I need some basic string processing
                                    • JavaScript is also mentioned in the footnotes… I’ve never really thought about it as a “toolbox language”, but thinking back, there are times where I was able to whip up a quick script to e.g. automate clicking a bunch of buttons on a website. Doesn’t come up very often but good to keep in the back pocket! (and well, it helps I use JS in other contexts too)
                                    1. 3

                                      The units command can do the unit conversion examples too:

                                      $ units -t "2.5 miles / (27 minutes + 16 seconds)" "mph"
                                      5.5012225
                                      $ units -t "1970 years + 1 billion seconds" years
                                      2001.6888
                                      $ units -t "2.5 miles / (27 minutes + 16 seconds)" mph
                                      5.5012225
                                      $ units -t "2.5 miles / (27 minutes + 16 seconds)" "meters/hour"
                                      8853.3594
                                      $ units -t "1 / (4.5 miles / hour)" "minutes/mile; seconds/mile"
                                      13;20
                                      
                                      1. 2

                                        For unit conversions you may also want to look at https://numbat.dev/

                                      2. 3

                                        My favorite toolbox language is GNU Octave. If you’re familiar with MATLAB it’s mostly 1:1 syntactically/semantically, but it doesn’t cost thousands of dollars. I’m not a huge fan of using the Python REPL, matplotlib, or numpy, so Octave scratches the itch whenever I want to do something math-y and need to plot stuff.

                                        1. 1

                                          I remember being fluent in MATLAB in university and it was honestly super nice.

                                        2. 3

                                          Other uses: launch REPLs for toolbox languages. Input the 100-keypress sequence required to beat one videogame (if you know, you know).

                                          😉

                                          Great article!

                                          1. 3

                                            I picked up J by diving into some books and Project Euler problems. I wanted it to be one of my toolbox languages. However, without consistent practice, the vocabulary tends to slip away.

                                            Recently I came across K (which influenced Q and @Internet_Janitor’s lil(part of Decker)), which might have better tacit programming facility as “tacit j and k” on https://ngn.codeberg.page/ suggests. The “Paradox of Choice” might pose a challenge for K. ngn/k might be the most popular open source choice, which has a helpful tutorial https://github.com/razetime/ngn-k-tutorial . I think vocabulary retention is still a hurdle and I should re-read the tutorial at some point :) The Whitney style C (Now also Earnest style JavaScript and AWK? :) used to implement these APL/J/K languages might pose a challenge to understand the interpreter…


                                            I enjoyed diving into your Raku articles and spent a weekend exploring the language. However, for now, I’m sticking with Ruby as I don’t find Raku particularly shorter than Ruby. On the other hand, avoiding sigils is great :)


                                            jq is cool, but its vocabulary slips away for me as well… Now I am sticking with basics like jq -r '.traceEvents[] | select(.name|contains("Total")) | "\(.dur/1000000) \(.name)"' < clang.time-trace to analyze an ld.lld --time-trace output. For more complex queries I might resort to Ruby or mix jq/Ruby. For example, I’ve invoked the following one-liner to analyze relocatable file sizes a lot recently for my ELF compact relocations work.

                                            for i in s2-custom{0,1,2}; do
                                            ruby -e 'tot=totsht=0; Dir.glob("/tmp/out/'$i'/**/*.o").each{|f| s=File.size(f); sht=%x{/tmp/Rel/bin/llvm-readelf --elf-output-style=JSON -h #{f}|jq ".[]|.ElfHeader.SectionHeaderOffset"}.to_i; tot+=s; totsht+=s-sht }; printf "%10d | %10d | %s\n", tot, totsht, "'$i'"'
                                            ; done
                                            
                                            1. 2

                                              Recently I came across K, which might have better tacit programming facility as “tacit j and k” on https://ngn.codeberg.page/ suggests

                                              that is highly controversial

                                              1. 2

                                                That’s putting it lightly.

                                                I think K’s approach is “better” in terms of overall simplicity and consistency, but by the same coin J’s richer collection of tacit forms is more expressive. Complicating the issue further is the fact that K’s lambdas and projection facilities are much more general than the similar-looking dfns-style lambdas in modern APLs, and offer different ways to untangle the same knots.

                                                I think the APL enthusiast community places far too much emphasis specifically upon tacit programming, possibly as a result of the overrepresentation of hobbyists who use the languages primarily for code-golf. Tacit forms strike me as one of the least important characteristics of the family.

                                                1. 2

                                                  K’s lambdas and projection facilities are much more general than the similar-looking dfns-style lambdas in modern APLs, and offer different ways to untangle the same knots.

                                                  why ‘much’? the only difference is the ability to pass an arbitrary number of arguments, no? and i thought ‘projection’ was just partial application (which can be done in apl/j)

                                                  I think the APL enthusiast community places far too much emphasis specifically upon tacit programming, possibly as a result of the overrepresentation of hobbyists who use the languages primarily for code-golf

                                                  100% agree

                                                  1. 1

                                                    I suppose it’s a matter of opinion whether it’s “much” more general or only somewhat, but I think being able to pass an arbitrary number of arguments (limited to 8 or so in some implementations, but easily extended) and project them in any order with an unsurprising generalization from K’s ordinary collection of application and indexing verbs adds up to more than the sum of its parts.

                                              2. 1

                                                Is Lila’s implementation really that challenging? It’s a bit dense, but AWK isn’t a very expressive language, and I avoided using any dialect-specific features (for portability), so there really isn’t much in the way of abstraction, indirection, or fancy programming techniques.

                                                1. 2

                                                  Probably not. I’ll study https://github.com/JohnEarnest/Decker/blob/main/tools/awk/lila.awk at some point. An interpreter in ~2 kloc is great!

                                              3. 3

                                                Perl is not dead?

                                                1. 2

                                                  Paraphrasing Zappa’s comment on Jazz (originally applied to Lisp): Perl isn’t dead, it just smells funny.

                                                2. 3

                                                  This is Ruby for me — I know it like the back of my hand, often tab-complete on objects I’m working with in irb just to see if there’s anything new and helpful, and it’s just so easy to transform data. I usually start in irb, then eventually pull something long enough out of that into a scratch script if I’m starting to get very many layers of transformation, and … yeah, just works.

                                                  1. 5

                                                    Ruby is great for just vibing. My favorite scratch script trick is using inline bundler blocks to pull in just a little bit of dependencies (usually Nokogiri) without escalating things all the way up to “new project” levels of setup.

                                                  2. 2

                                                    A concatenative PL if I ever find out what small problems a CPL is really good for

                                                    Well here are a few translations of given examples into Factor (all the line breaks are optional):

                                                    DISCLAIMER: Though I love Factor, I’m no expert.


                                                    Get all of the prime factors of a number:

                                                    2520 factors
                                                    

                                                    If someone was born at midnight on Jan 1st 1970, when do they become a billion seconds old?

                                                    1970 <year> 
                                                    9 10^ seconds 
                                                    time+ 
                                                    timestamp>string
                                                    

                                                    If I run a certain distance in a certain time, what’s my average pace?

                                                    ! In miles per hour
                                                    2.5 
                                                    27 minutes 16 seconds duration+ 
                                                    duration>hours /
                                                    

                                                    What’s (6 ± 2) * (8 ± 1)?

                                                    6 2 [ - ] [ + ] 2bi [a,b]
                                                    8 1 [ - ] [ + ] 2bi [a,b]
                                                    interval*
                                                    

                                                    Generate three random 10-character lowercase strings.

                                                    3 [ 
                                                      10 
                                                      CHAR: a CHAR: z [a..b] 
                                                      randoms >string 
                                                    ] times
                                                    
                                                    1. 1

                                                      I haven’t heard of “toolbox languages” yet. Is this the authors invented name or is it more general word? (because ryelang would probably fit into this category quite well)

                                                      1. 3

                                                        I made it up!