I have a custom harness that runs in a macOS VM. It has e-mail and its own accounts. I assign it tasks in Linear, it does them and spins up PRs for me to review. This works pretty well, generally. I have to spend time writing stories and doing code review, but I don’t have to follow its (their — I have 3 of them) every move.
HN user
Bogdanp
https://defn.io/page/about
Submissions are not endorsements.
Regardless of the sport you practice, you probably need to be doing some form of periodization. You can’t go for PRs every session because at some point your body stops being able to recover => you get hurt.
It's SwiftUI. See [1] and [2].
Web: create an executable distribution[1] and ship it to a server.
Windows/Linux/macOS: same as web, using cross-compilation[5]. Additionally for macOS, embedded in a Swift app and distributed as a .dmg[2] and on the Mac App Store[3]
iOS: embedded in a Swift app and distributed on the App Store[4].
[1] https://docs.racket-lang.org/raco/exe-dist.html
[3] https://apps.apple.com/us/app/franz-apache-kafka-client/id64...
[4] https://apps.apple.com/us/app/podcatcher-podcast-player/id67...
I’ve used it for all kinds of production stuff over the years, from web to desktop and mobile apps.
Notification names are arbitrary strings. It's convention to use "fully-qualified" names like that to avoid collisions. In this case, I imagine that just happens to be what the developer chose as a prefix.
Yes, although I've never used capnproto, I believe the approach it takes is similar: you define your schema, it generates code to do the serialization and to handle the RPCs.
That still leaves you with the problem of wiring up the request -> response types manually, doesn't it? The serialization format isn't that important in this case.
On average for calendar year 2024, it was $35 a day, living in Cluj-Napoca, Romania. For two people. My guess would be this is on the higher end of things, but I'm curious about others. Another factor is I am 95kg, 15% bf and do resistance training 6 times a week, so I eat a lot.
(I beancount[1] my finances.)
3. from what I understand, shell syntax is available only at REPL top level. Once you switch to Lisp syntax with `(`, you can return to shell syntax only with `)`. Thus means you cannot embed shell syntax inside Lisp syntax, i.e. you cannot do `(define j {find -type f | less})`
It's possible I misunderstand what you mean because I'm not sure what piping to less is supposed to accomplish here, but this is not true. The following program works just fine:
#lang rash
(require racket/port
racket/string)
(define (echo!!! message)
(define output {echo $message |> port->string |> string-trim})
(string-append output "!!!"))
(echo!!! "Hello")Rash is excellent. I use it in production for a process that copies large parquet files on the daily by shelling out to the aws command, then processes them by shelling out to another command and then pushes the processed data over HTTP to another server using a Racket HTTP client[1]. My only complaint would be that the docs could use some cleaning up.
Nice! Yeah, using protobufs seems reasonable. Re. GC, Racket has support for "freezing" values in place to prevent the GC from moving them, but freezing too many values can impact the GC's operation so I'd watch out for that if that's possible in OCaml.
No worries! I like talking about this stuff.
Yes, the app uses Noise under the hood, and the way to think about it is a request-response model[1]. Swift makes an async request to the Racket backend, it constructs some data (either by querying SQLite, or making a request to the backend, etc.) and returns a response. If it doesn't retain any of the data, then it gets GC'd. The ser/de is relatively low overhead -- if you try the app and go to Settings -> Support and take a look at the logs after using it a little, that should give you an idea of how long the requests take. The lines that start with `#` refer to Swift->Racket RPCs. Here's an example from my logs:
2025-01-28 13:04:32 +0000 [io.defn.NoiseBackend.Backend] [debug] #006381: waitForAllDownloads()
2025-01-28 13:04:32 +0000 [io.defn.NoiseBackend.Backend] [debug] #006381: took 319µs to fulfill
2025-01-28 13:04:32 +0000 [io.defn.Podcatcher.AppDelegate] [debug] didBecomeActive: finished downloading pending episodes
2025-01-28 13:04:33 +0000 [io.defn.NoiseBackend.Backend] [debug] #006382: getStats()
2025-01-28 13:04:33 +0000 [io.defn.NoiseBackend.Backend] [debug] #006382: took 2ms to fulfill
Some things on the Racket side are long running, like the download manager. It's like an actor that keeps track of what's being downloaded and the progress of each download. Whenever a download makes progress, it notifies the Swift side by making a callback from Racket->Swift. In this example, there is some duplication since both the Swift and Racket sides each have a view of the same data, but it's negligible.What's not as great from a memory use perspective is how large Racket's baseline memory use is. Loading the Racket runtime and all the app code takes up about 180MB of RAM, but then anything the app does is marginal on top of that (unless there's a bug, of course).
[1]: I did this precisely because, as you say, keeping keeping data in sync in memory between the two languages would be very hard, especially since the Racket GC is allowed to move values in memory. A value you grab at t0 might no longer be available at t1 if the Racket VM was given a chance to run between t0 and t1, so it's better to just let Racket run in its own thread and communicate with it via pipes. Probably, the same would be true for OCaml.
Thanks for the feedback. Subscribing in particular is slow, especially for podcasts with lots of episodes, because it tries to save all the episode metadata locally, to avoid roundtripping later. So, it’s slow initially so it can be fast later and work offline, but I understand how that can be annoying!
Ultimately, you end up calling some system API for I/O, so the only difference is how efficient the implementations of those Frameworks are compared to the embedded language's implementations. On iOS, embedding Racket requires using an interpreted mode (as opposed to Racket's usual native compilation mode), so there is a small hit, but it's not one that is really noticeable in battery or CPU consumption. In fact, Podcatcher seems to do better in battery consumption compared to the competition in my (and my friend's) testing, but I would guess that's not necessarily _because_ of Racket; it probably has more to do with how the system as a whole pays attention to perf.
That's a great point! Shouldn't be hard to add.
A botched deploy that I didn't notice ended up crashing the server, which then failed to recover when the systemd unit tried to restart it (it couldn't bind the port and ran out of retries quickly) overnight, so that's why that was failing. It's back up now and I'll put a fix in today.
Sorry about that! Looks like the server got OOMKilled at one point and failed to recover. It should be back up now and I'll work on a fix.
Thanks! I'm not able to reproduce this on my end, but if you send me the exception (here or via e-mail), I'd be happy to take another look!
That's right, the UI and the Audio Engine bits are in Swift, because it's easier to interface with those Frameworks directly from Swift (and not fight the platform). Everything else (the Database management & the models, the download manager, ID3 parsing, parsing release notes, syncing with the backend server, etc.) is implemented in Racket and is portable.
The app store labels are somewhat broad, but that refers to the error tracking that the app does. When an unhandled exception occurs in the app, that error is sent to Sentry.
Yup, unfortunately Podcatcher works the same way and that explains why you'd be getting that error, since the server can't access your feed. That's certainly something that it could support though, but you would obviously not get any push notifications for that feed.
Not yet, but it's on the list!
Thanks! I used Pixelmator Pro to put the images together. I just made one long image that I cropped at the end into the screenshots you see on the App Store. For the iPhone templates, I think I exported a template from Sketch, but I later found out that Pixelmator also has them (you can find them under the Mockups category in the New dialog).
Noted! There is already an undo function, though. If you seek, an undo button pops up. Also, if you go to settings, there's a "History" view where you can also undo from.
Every podcast has its own RSS feed. As for the index, I use Podcast Index[1] and the iTunes API.
Thanks! That makes a lot of sense. It actually only downloads at most 2 episodes per podcast at a time, but that's still not great for your use case. I do plan to add per-podcast settings in probably the release after next, so stay tuned for that!
Yep!
Yep, it's one of Matthew Butterick's fonts[1].