Having a Lisp system to control your WM/compositor is amazingly empowering, being able to just write some expressions in a buffer and evaluate them on the fly to command windows, apps, keybindings, various settings from sound and display (e.g., color temp) to bluetooth, etc. is crazy awesome.
Once you have that taste of freedom - there's no going back. The traditional way of "write, save, reload/restart" would feel so clunky, annoying and stupid.
And you can absolutely do this as long your WM supports some kind of IPC. I'm slowly building my Hyprland config in Clojure using babashka. I wish I could share it publicly, but it's still in early experimental stage - too messy, too opinionated, there are some bugs, and I still may decide to switch to Janet, CL, nbb or some other Lisp option, I'm glad we have numerous options to choose from.
So, for example, I would write in my editor an expression to get the list of windows on the current display, then I can immediately map/filter through them, group, sort, read any parameters, etc. Then without missing a beat I can apply some transformations - programmatically move any window, resize, etc; on the fly, like playing a fucking video game.
Compare that to "more traditional" approach of issuing commands, then piping them to jq or something, then figuring out where the stuff is, then writing that into a file, then having your WM to pick up those changes, often you'd completely lose the state, then you'd have to go back to the script files, etc. I, don't have to save anything anywhere until I'm sure that shit works - I simply eval things on the go - I can "touch, grab and interact" with entities immediately after querying their info, there's no physical, mental or contextual separation here - I don't have to write something in the terminal, then something in one of my scripts, some parts in some other file, etc. - everything controlled directly from my editor.
Here's more practical example. Hyprland has something called Hyprsunset to deal with color temp and gamma. I wrote an extension that changes display color temp based on time of day - it's a simple clojure.core/async go loop, it reads from a hashmap where specific hours map to temperatures and then checks every 10 minutes; if it's time to apply new color, it does. That took me just a few minutes to whip out with a connected Lisp REPL. I'm pretty sure, it would've taken me far longer without it. The way how the "true" REPLs work (which e.g., Python one is not) simply is shockingly crazy awesome for rapid prototyping. Why more programmers don't do this is a complete mystery to me - getting into Lisp is not even that difficult, Clojure for example is far more simpler and more straightforward than even Javascript and Python. These days, you don't even need to know Emacs - install Calva for VSCode - that's all you need, it has quickstart guide and all.
The custom actions on windows when needed sounds great.
I thought hyprsunset already changes the temperature automatically for you. Nevertheless, sounds interesting, do you have this loop started when you launch your editor?
What is the difference between a "true" repl and one like python?
> I thought hyprsunset already changes the temperature automatically for you.
It seems it does now, when I wrote my thing there wasn't multiple profiles feature - it's a very recent feature that was added not too long ago. I guess I can remove my custom script now. But then again, if it just works, why bother?
> What is the difference between a "true" repl and one like python?
Like I said, all stages in R.E.P.L. do differ:
Read:
In Lisp, lexical analysis and tokenization keeps the forms as data - no need to do much here. In Python, this step produces private AST representation you cannot easily access and modify.
Eval:
In Lisp, the compiler just walks the forms freely - because they are already in the shape; in Python, parser-generated AST nodes get passed to compiler, AST is opaque to user code, it doesn't allow manipulation semantics, Macros in Python require AST manipulation libs - heavyweight, not first-class; This is fixed compile phase in Python - no runtime AST rewriting possible.
Print:
Serializes eval results back to readable representation. In Lisp - this is trivial because results already data structures in the right shape. In Python, you can't feed printed output back through eval and get meaningful metaprogramming.
Loop:
Reader directly consumes user input, they are just native forms, compiler sees the code like data, this allows transparent metaprogramming - you can easily write code that re-writes itself. In Python: string input first compiles then gets processed for execution; metaprogramming is pretty opaque - reconstructing the AST is much more difficult - not easy to write code that re-writes itself; eval/exec operate on bytecode, not source semantics.
In practice, what these seemingly unimportant differences make possible is:
first of all you get to write code as you are playing a video game - you just write shit and eval things, and immediately make things fly - find Figwheel demo video, Bruce Hauman there writes a clone of FlappyBird where he manipulates the physics directly from the editor - without any saving, reloading anything, without state changes, etc., he literally uses his editor like a joystick to move the effing bird around. And it's some darn old flick - there's nothing "revolutionary" about that technique - Lispers been doing shit like that for a long time. Or search up on YouTube "Clojure/Overtone DJ programming", where folks are playing music while manipulating some code in their editors, all in real-time. Or check this out - we run our services in k8s cluster. In staging env we maintain a REPL connection, so if we need to test something out, we just connect to it, change the code, eval and manipulate our entire pipeline directly from our editors.
These differences in REPL also open up metaprogramming otherwise much harder to achieve. Like for example, you can write code that says: "here's the server part, here's the client part" and then let the multi-phase eval figure out the semantics - sometimes, these semantics can mean that the server part actually executes in a different runtime - checkout Hyperfiddle/Electric demos - some of them are jaw dropping - you write code in one place but some of it executes on JVM, some in the browser - completely different runtime. Same code, unified logic, lives in one place - goes to run on different worlds.
Automated behaviours like forcing windows to different virtual desktops, modifying windowing behaviour dynamically. Some windows are automatically tiled and others float, etc.
Once you have that taste of freedom - there's no going back. The traditional way of "write, save, reload/restart" would feel so clunky, annoying and stupid.
And you can absolutely do this as long your WM supports some kind of IPC. I'm slowly building my Hyprland config in Clojure using babashka. I wish I could share it publicly, but it's still in early experimental stage - too messy, too opinionated, there are some bugs, and I still may decide to switch to Janet, CL, nbb or some other Lisp option, I'm glad we have numerous options to choose from.