HNHacker News
TopNewBestAskShowJobs

swannodette

12,589 karma · joined January 12, 2009

Staff Software Engineer at https://lightweightlabs.com. Java, JavaScript, Clojure, ClojureScript mostly, but a language geek at heart.

https://convivivialcomputing.net

submissionscomments
swannodette··on The Early History of Smalltalk (1993)
Interface Builder was originally a Lisp project

https://www.inria.fr/en/jean-marie-hullot-visionary-computer...

swannodette··on Clojure 1.13 adds support for checked keys
Honestly what's mostly at the forefront of my mind is greatly improving the documentation around ClojureScript as well as our fork of Google Closure Library (GCL). At work we've switched to DataStar (a single JS include) and coupled that with ClojureScript/GCL - we no longer rely on anything from NPM, to call this a simplification would be a gross understatement. Bundle size is 30K gzipped and we spend no time thinking about our build or JS tooling/dependency tomfoolery.

So less about ClojureScript specifically, and more generally how I think we're well situated for people looking for a way out. The current mainstream practice dead end is bigger than the one that made React (also originally just a script tag include) appealing to me back in 2013. There are of course many ways forward that don't involve CLJS, but I think ClojureScript/GCL and the new crop of NPM-dep free pure CLJS solutions like Replicant are well situated for folks who can see that accepted practices are not delivering enough value even with AI assistance.

swannodette··on Clojure 1.13 adds support for checked keys
Working on it :)
swannodette··on Permacomputing Principles
The original article on permacomputing by viznut https://viznut.fi/texts-en/permacomputing.html
swannodette··on Understanding Clojure's Persistent Vectors, pt. 1 (2013)
That's a big contribution, also the original HAMTs are not a functional data structure. See Section 3.4.1 in https://docdrop.org/download_annotation_doc/3386321-trk2f.pd...
swannodette··on Understanding Clojure's Persistent Vectors, pt. 1 (2013)
Clarifying Clojure had them in 2007. Scala implementations were inspired by Clojure’s. Both Odersky and Bagwell have given credit to Hickey.
swannodette··on Monosketch
If you use Emacs, there's a pretty nifty package https://github.com/tbanel/uniline
swannodette··on Offpunk 3.0
I don't think Gnus is that bad once you spend some time setting it up. For groups with a ton of content where I mostly want to search, I found it was better to just download the whole group and index into notmuch. I could query 20 years of the Smalltalk USENET group or the Supercollider mailing list instantly.
swannodette··on Claude wrote a functional NES emulator using my engine's API
WASM and the performance seems catastrophically bad (45ms to render a frame on an M4 laptop)? It would be much more impressive if Claude could optimize it into something that someone would actually want to play? Compare this to a random hit from Google, https://jsnes.org/ which has sound, much smaller payload, and runs really fast (<1ms to render a frame).

The cost of slop is >40X drop in performance? Pick any metric that you care about for your domain perhaps that's what you're going to lose and is the effort to recover that practical with current vibe-coding strategies?

swannodette··on Homeschooling hits record numbers
If you can afford it! "Grass-roots segregation hits records numbers" would be an equally fitting title.
swannodette··on One Handed Keyboard
Some research on this topic http://edgarmatias.com/papers/hci96/

On OS X you can achieve this with Keyb, Karabiner Elements, etc. It's also easy to do with a programmable keyboard with ZMK/QMK. I've set up my Kinesis 360 Pro this way, being symmetrical means I can access every key easily. Hardware support for sticky keys also helps quite a bit.

swannodette··on Mathematical exploration and discovery at scale
I don't see how anything about what's presented here that refutes such claims. This mostly confirms that LLM based approaches need some serious baby-sitting from experts and those experts can derive some value from them but generally with non-trivial levels of effort and non-LLM supported thinking.
swannodette··on GHC now runs in the browser
Loading 50mb of WASM is a big tradeoff just to run code on a website.
swannodette··on Learn to play Go
Blitz format is reasonably popular on KGS (once you get to a certain level) usually 10+0. Blitz is harder to find on Pandanet - but you can easily blitz on Fox.
swannodette··on Learn to play Go
It's easier to get games on Pandanet https://pandanet-igs.com/communities/pandanet and Fox Weiqi https://www.foxwq.com. You can run Fox on OS X w/ Parallels or Crossover. It supposedly possible with Wine but I could never get it to work.
swannodette··on I used standard Emacs extension-points to extend org-mode
Yes! Recent personal hacks with advice interactively programmed with the help of Claude 4.1 chat session running in Emacs.

  - popping the global mark to travel backwards to previous edit points would reuse the same Emacs window instead of using a window that is already showing the buffer. Fixed
  - I would accidentally destroy my window configuration w/ C-x 1. Fixed, use advice to automatically save the window configuration into a register if I invoke the command to remove all other windows. Now I can easily recover if I make that mistake.
  - I want to be able to select and then scroll any other open window w/o leaving the current one. Fixed
  - A crazy one. I collect note w/ links or whatever for reading/watching later, these are marked w/ a timestamp. My notes file is not an agenda file, i.e. not filled with todos / tasks. I made a hack to temporarily include the current non-agenda org-mode buffer in the agenda list and then show inactive timestamps. Now I can scan a day/week/month for interesting notes I took. This doesn't interact at all with my real agenda.
  - org-agenda opens items in weird places, use advice to fix it so that it always appears where I like.
  - fix inf-clojure so that it uses dep.edn as project root over .git
I used IntelliJ happily for 10 years (I was a heavy Emacs user for 10 years before that). While it's true that some things are a little less convenient (I don't use LSP), knowing that I can tailor things exactly to my tastes is a serious breath of fresh air.

It used to be I mostly used IntelliJ for work/OSS and Emacs for org-mode. Now the situation is likely reversed. Emacs for work/OSS and IntelliJ only if I need step debugging/global refactoring.

swannodette··on A beginner's guide to extending Emacs
Yes. gptel supports two output modes out of the box, markdown (meh), and org-mode. I make an org-mode heading with my question, then I press `C-c <enter>`. Claude will use the area bellow the heading for its answer. It will make sub-headings, code blocks, lists, tables, etc. If I have another question then I make a new top level org-mode heading.

I started doing this because I got fed-up with Claude Desktop for my StackOverflow style programming questions - and then stumbled upon using it to configure Emacs.

swannodette··on A beginner's guide to extending Emacs

  > My experience extending a core emacs function was an instructive and 
  > interesting exercise. I don't know what the future of emacs looks like in an
  > increasingly LLM-crazed world, but I hope that future  includes an open and
  > powerful way to extend and customize the tools we use to write software.
I have gptel configured with Claude 4.1 via API. Claude generates an org-mode file. I ask it questions about Emacs packages, Emacs configuration, and Emacs customization. It responds w/ Elisp snippets that I can eval immediately and see the effect. Claude knows a lot about Emacs. All these chats are version controlled into git so I can easily pull, consult-ripgrep, and pickup where I left off from any of my machines.

I can add my `.emacs` to the Claude context to get more precise answers. If it falls over on some package I can `M-x find-library` to add that library's source to the context. If the code it wrote doesn't work, I add the `Messages` buffer and the `Backtrace` buffer for errors. I eval the snippet, reprompt, rinse and repeat.

With this fast feedback loop (no restarting Emacs, just live coding), I've added a ton of customizations that in my twenty years of using Emacs previously just never felt like I had the time or enthusiasm for given higher priorities:

* Boring stuff: managing where modes open buffers in which windows

* More ambitious stuff: standard org-remark behavior isn't that natural for highlighting and making notes so I made a nicer Transient based thing for it.

* Stuff for work: a fast logging minor mode that font locks incrementally, disables all the save prompting, and handles ASCII color codes. Later I intend to linkify stack traces, linkify data so that they open pretty printed in a different buffer, collect errors and show an unobtrusive notification in the active window, etc. etc.

In two weeks, I've learned more Emacs than I did the 10 years prior. Most of all, this is a usage of LLMs that I can say I honestly love - improving my own day-to-day tools. Because Emacs is a text-oriented live programming environment - LLM integration just feels like it's on a completely different level.

Claude (or any good LLM) + Emacs is a killer app.

swannodette··on The End of Handwriting
I have 2 Sailor Pro Gear Slims 14K and one Sailor Pro Gear 21K. The later is the best writing experience. I'm not an ink maniac but I tried a few and I found that Sailor inks tend to dry reasonably quickly while still having a good flow. Midori paper and Tomoe River both perform very well. If I'm going to do a whole page of writing smearing from hand moisture can be a problem, especially w/ non-Sailor inks so in that case I use the "today" cards you can get for the Hobonichi.
swannodette··on The End of Handwriting
I’m left handed, with the right ink and paper this isn’t a huge problem. I picked up fountain pens a year ago and I will never go back to regular pens for my own writing.
swannodette··on Why MIT switched from Scheme to Python (2009)
> Following SICM I think even its authors were doubtful about whether Scheme was a good medium for working with those ideas.

Hrm https://mitp-content-server.mit.edu/books/content/sectbyfn/b...

swannodette··on ClojureScript from First Principles [video]
We forked Google Closure Library. This is mostly a good thing as GCL started introducing frustrating breaking changes around 2018/2019. We've backed everything out and now ClojureScript libraries from 14 years ago work again.
swannodette··on ClojureScript 1.12.42
I'm curious as to how you think this is different from say, committing to JavaScript/BundlerX/NPM or TypeScript/BundlerX/NPM? Surely those have an equally heavy influence on how you write code?
swannodette··on Google Closure Library has been archived
The implementation really is not built on top of GCL, the bulk of the standard library is persistent datastructure implementation and functional apis. The few cases here and there like `goog.string` can be removed over time. But really what's the rush? Large portions of GCL have worked unchanged for nearly two decades. Does Google archiving GCL make perfectly good code stop working?

When we eventually remove the direct GCL dependency it will sadly be more for optics than anything else.

swannodette··on Bach’s Cantata Calendar
I felt that way at first, but after getting around to listening to a few of them you start to realize he repurposed a lot his favorite material from these cantatas into his instrumental works. For example, one of Bach's innovations was the keyboard concerto - where the keyboard played the central solo role. The profound Harpsichord Concerto in D minor BWV 1052, https://www.youtube.com/watch?v=9ZX_XCYokQo, is actually reworked from the cantatas BWV 146 & 148.

While there's obviously plenty to enjoy w/o dipping into the cantatas, you might miss out on making these kinds of connections which I think really enriches our understanding of the instrumental works.

swannodette··on Organ pipe physics
Some scholars believe it to be an early work - possibly Bach was 19 years old. No copy in Bach's hand has ever been discovered, so there's no certainty. It is a wonderful, memorable piece of music but Bach was pretty good at that and not just at the keyboard.
swannodette··on Developing mobile digital key applications with ClojureScript
No App Store issues as far as I know.
swannodette··on ClojureScript 1.10.866 Release
To be fair there were definitely still bugs in Om as well as om.next (and both were marked as being experimental and of alpha quality) - however Fulcro came along and more less took the ideas and took them further and far as I know is actively maintained. I don't use it - but I suspect there was/is probably some kind of migration path.
swannodette··on Developing mobile digital key applications with ClojureScript
Some technical points about Krell. It leverages the ClojureScript `:bundle` target which was released last year https://clojurescript.org/news/2020-04-24-bundle-target. By simply producing output that is JS bundler friendly we can just piggieback on Facebook's Metro just like we piggieback on Webpack etc. when targeting the web.

We simply reuse the debug loader provided by Google Closure and load ClojureScript and Google Closure JavaScript files through the Metro server. But this is the core of ClojureScript's hot-reloading capabilities without caveats. In Google Closure namespaces can be represented as nested JavaScript objects which delivers pervasive late-binding - which simply cannot be done with ES6 modules because imports will be captured (early bound).

The REPL bit (which is an independent piece from the hot-reloader) just runs on top of react-native-tcp-socket.

The only tricky part is that we need to be able to require Node libraries and assets into ClojureScript during development. This is done by a compiler pass - first we start at the entry point of the ClojureScript React Native project and follow the dep graph collecting all libraries required from `node_modules`. This is dumped to a file that is required transitively by `index.js`.

Asset handling is done as a simple compiler pass over every AST node searching for JavaScript `require` statements in the ClojureScript.

The end result is that we have an extremely rapid development workflow that simply is not possible with other existing technologies - not React Native, not Flutter, not SwiftUI. All of our apps are built via live-coding from our text editor of choice + REPL (either embedded in IDE or via shell).

Happy to answer any further questions!

swannodette··on ClojureScript 1.10.866 Release
Mostly a maintenance release, but the and/or compiler pass thing is somewhat neat. First, it might seem that such an optimization would not be that useful, but actually it's one of the most critical ones - we want to write the persistent data structure code in a language idiomatic way yet know that performance sensitive JavaScript code will be generated.

Here's the problem.

In Clojure `and` and `or` are just macros that expand to `let` + `if`, simple and elegant:

    (defmacro and
      ([] true)
      ([x] x)
      ([x & next]
       `(let [and# ~x]
          (if and# (and ~@next) and#))))
... except when you target JavaScript - which is not an expression oriented language. So `let` will be wrapped in an immediately invoked function expression (IIFE). Google Closure does elide these when it can, but it will give up after only a few levels of nesting. For an IIFE to appear in the middle of a conditional is a performance killer.

Another complication is that it's not safe to just use `&&` and `||` blindly because of `0` and the empty string and the other cases which are not false-y in Clojure(Script). Checks for JavaScript false-y values are a performance killer.

So years ago we implemented a simple form of type inference which annotates the AST with type information as a compiler pass on every node when possible. As long we know the JavaScript `if` will receive a boolean value we can elide the JS false-y value check.

But this is also exactly the information we need to safely run a following pass that looks for the above and/or syntactical patterns and optimize it. As long as each nested `if` is guaranteed to return a boolean, we can remove the local and use `&&` or `||` instead.

The and/or optimization pass ended up being ~120 lines of code with no actual dependencies on anything else in the ClojureScript analyzer or compiler because the ClojureScript AST is just plain EDN - https://github.com/clojure/clojurescript/blob/master/src/mai...

Happy to answer any further questions about this!

UPDATE: Also ClojureScript turned 10 years old this month :)

Page 1 of 34Next →