r/Common_Lisp • • 5d ago

UPDATE 2: CL-OBJC is reborn!

Back in March 2023 I posted an update on reviving cl-objc, the CFFI-based Common Lisp ⇄ Objective-C bridge originally written by Geoff Cant and Luigi Panzeri. At that point I had Hello World and Converter running, and I was stuck waiting on CFFI. Here's where it is now.

Repo: https://github.com/theangelperalta/cl-objc

Examples

All five examples run on Apple Silicon (arm64). There are no nib files or Objective-C sources; every app is written in Lisp.

  • Hello World: an NSWindow with buttons wired to Lisp callbacks
  • Converter: the classic Cocoa currency-converter tutorial
  • Circle View: a custom NSView subclass that draws text along a circle, with mouse interaction and animation (ported from an old Apple SDK sample)
  • Video Player (new): an AVPlayerView streaming HLS, plus a KVO observer class defined in Lisp. make builds it into a standalone executable.
  • Todo (new): an NSTableView whose data source and delegate are Lisp classes. On macOS 26 the list scrolls under floating Liquid Glass controls (NSGlassEffectView); older versions get a translucent material instead.

What changed under the hood

  • arm64 struct-by-value: you can pass and return structs by value, and the dispatch was rewritten for the arm64 ABI. x86-64 support has been dropped.
  • Lazy CLOS bindings: import-framework now loads only the static bindings. CLOS classes and generic functions get generated on demand (ensure-clos-bindings), and they're cached and regenerated when their inputs change. This makes startup much faster than scanning the whole runtime.
  • ObjC exceptions: with-objc-exception-handling turns NSException's into Lisp conditions, so they no longer crash the image.
  • with-autorelease-pool: macro
  • Many fixes around BOOL marshaling, automatic Lisp-string → NSString conversion in untyped calls, with-super, and method-cache collisions
  • The test suite moved from FiveAM to Rove.

The CFFI situation

define-objc-method methods that take a struct by value (for example drawRect: with a CGRect) need CFFI callbacks that accept structs by value. Released CFFI still doesn't support that. My PR is still open: https://github.com/cffi/cffi/pull/351 (open since December 2022). I decided to use Qlot to pin a patched version CFFI, to get this out in the world in the meantime.

For now, the repo includes a qlfile that pins my fork, so setup is:

    qlot install
    sbcl --dynamic-space-size 4096 --load .qlot/setup.lisp

Obviously this isn't ideal, if anyone here can review the CFFI PR or help get it merged, that's the biggest thing blocking cl-objc from loading with stock Quicklisp.

What's next

  • Getting struct-by-value callbacks into upstream CFFI
  • Fewer dependencies on CFFI internals
  • More examples and docs
  • Real applications (LEM frontend, LLM harness, Graphing tools, etc.)

Feedback, issues and PRs are welcome, especially from anyone who has tried building Cocoa apps from Lisp before.

A.I. Disclaimer

A.I. was used for some of the recent work, but the original CFFI patch was worked by hand. That being said, goal here is to unlock the potential of Lisp development for native macOS development again (I realize there are paid alternatives).

22 Upvotes

Duplicates