Faster-than-Light memes 2024-04-19T18:49:18+00:00 https://faster-than-light-memes.xyz/ Benjamin Schwerdtner https://faster-than-light-memes.xyz/screencasts.html screencasts 2023-06-17T23:59:59+00:00 screencasts

ftlm

screencasts

Braitenberg vehicle 2 and 3 - fear, agression, love

See: Vehicles Overview, Fear, Aggression, Love

Making some cybernetic, organismic machine animal simulations using Quil and Clojurescript.

The most canonical Braitenberg vehicles. 2 sensors, 2 motors, ray sources in the environment and different wiring make different behaviour.

I will deploy this soon.

Life coding Emacs, Clojure, Meow, screencast, programming, cybernetics, code, animals, bio computing, braitenberg, vehicles, love, fear, aggression, explore, system theory, control theory, simulation, toy, fun, little animals

Quil first experiments

Probably way too lengthy. You can watch how I spent last Saturday :)

Clojure REPL driven development showcase

The fruits of Clojure I

Making a knowledge base database keyword extractor with open ai chat api.

https://www.youtube.com/watch?v=GcJgGy-dfvE

Primagen was recently checking out Uncle Bobs Clojure post, which did not mention the most important thing about Clojure. Interactive Programming is the big magic word my friends.

Related: Interactive Programming and Lisp overview.

#_(add-libs '{net.clojars.wkok/openai-clojure {:mvn/version "0.9.0"}})
(ns scratch
  (:require
   [clojure.java.io :as io]
   [clojure.set :as set]
   [clojure.string :as str]
   [clojure.java.shell :as shell]
   [wkok.openai-clojure.api :as api]))

(def api-key (memoize (fn []
                        (str/trim (:out (shell/sh "pass" "private"))))))

(defn make-chat-completion [messages]
  (api/create-chat-completion {:model "gpt-3.5-turbo-16k"  :messages messages}
                              {:api-key (api-key)}))

(defn resp->message [resp] (-> resp :choices peek :message :content))

(def extract-keywords!
  (memoize
   (fn [file]
     (-> (make-chat-completion
          [{:role "system"
            :content
            "Extract the most interesting, essential keywords from the text.\n\nYou output edn vector format.\n\nExample: [\"AGI\" \"Timeline\"]"}
           {:role "user" :content "Doc:"}
           {:role "user" :content (slurp file)}])
         resp->message
         read-string))))

(defn query [db key]
  (get db key))

(defn assert-keywords [db file extracted-words]
  (reduce (fn [res e] (update res e (fnil conj #{}) file)) db extracted-words))

(def the-db (let [db {}
                  kws (for
                          [file ["/home/benj/notes/20230126T094804--the-joy-of-clojure__clojure_code.org"
                                 "/home/benj/notes/20230707T090418--conversation-1__clojure.org"]]
                          {:file file :keywords (extract-keywords! file)})]
              (reduce (fn [db {:keys [file keywords]}] (assert-keywords db file keywords)) db kws)))

(comment
  (spit "the-knowledge-db.edn" (prn-str the-db))
  (read-string (slurp "the-knowledge-db.edn"))

  (->
   (assert-keywords {} "foo" #{:a :b})
   (assert-keywords "foo2" #{:a :c})
   (query :c)))

Keywords: Emacs, Lispy, emacs lisp, livecode, screencast, how to emacs, Clojure, REPL, showcase, openai api, chat gpt, programming, code editor

Advanced cider jack-ins

As answwer to this cider issue on github.

;; jack-in-cmd allows you full control over the command that starts the nrepl server process

(defun my-cider-jack-in-prompt-cmd (jack-in-cmd)
  (interactive "sJack in cmd: ")
  (cider-jack-in-clj
   (list :jack-in-cmd jack-in-cmd)))


;; I personally like to def a jack in function in elisp which I have somewhere,
;; for instance the readme of my project or dev.el

(defun ftlm-vehicles-make-nbb-nrepl-jack-in ()
  (interactive)
  (cider-jack-in-clj
   '(:jack-in-cmd "npx nbb nrepl-server"
                  :project-dir "/home/benj/repos/clojure/vehicles/")))

Keywords: Emacs, Lispy, emacs lisp, livecode, screencast, linux, rice, productivity, scripting, programming, Clojure, Cider, Clojurescript, Nbb, Babashka

Lispy how to to insert single brackets

A frequently asked question when using Lispy is how do I insert single brackets, quotes, parens?

The answer is quoted-insert, wich is bound to C-q by default.

It reads the next character and inserts it, bypassing your current keymap and such.

Keywords: Emacs, Lispy, emacs lisp, livecode, screencast, how to emacs, how to lispy, lispy tips, emacs tips

Repl into production example

Real world show case repl into production. This is the kind of thing where Lisp delivers.

Keywords: Emacs, stumpw, arch, Clojure, Datomic Cloud, AWS, EC2, discord bot, REPL, production, fun with computers

(let [server (nrepl/start-server
                :port 7000
                :bind "0.0.0.0"
                :middleware cider.nrepl/cider-middleware
                :handler cider.nrepl/cider-nrepl-handler)]
    (reset! nrepl-server server))

Now we want to make an ssh tunnel to this machine to port 7000 on the server machine

~/.ssh/config

Host support-bot-ec2
     HostName ec2-18-212-83-183.compute-1.amazonaws.com
     User ec2-user
     IdentityFile /home/benj/.ssh/ben-ec2-key-pair.pem
     LocalForward 7000 ec2-18-212-83-183.compute-1.amazonaws.com:7000

ssh

Opening the tunnel is as easy as:

ssh -i/path-to-key.pem -L 7000:ec2-X-X-X-X.compute-1.amazonaws.com:7000 ec2-user@ec2-X-X-X-X.compute-1.amazonaws.com where the X vals are your compute instances public DNS.

ec2

https://console.aws.amazon.com/ec2/v2/home?region=us-east-1

find instance find rules add inbound rule

failure modes

Network SSH p22: If your addresses are wrong, some error; if your path

to the pem key is wrong, quick auth error

Network TCP p7000: If you open up the ports on the wrong SG or source CIDER, etc., IIRW connection will fail quickly with some error, or silently after a long timeout

nRepl service: If you forget to start your nRepl server on the appropriate port, long timeout IIRW

Fast cider overlays

blazingly fast cider overlays and how I got them.

keywords: Emacs, performance, programming, fun, cider, clojure, eval, ovelays, fast, quick, zappy, blazingly fast

Bobscheme

Bobscheme page.

Now I made a thumpnail, woar!

I also figured out that I can share -> embed on youtube. Then the iframe works (see above link).

Real world web app screencast

Adding a google drive list feature to my Clojure, Scittle, Reagent, Reitit app.

Real life Clojure programming. REPL-driven development. Emacs, meow, lispy, Clojure, Clojurescript.

Goals

  • Show practical Lisp and Clojure programming, with Emacs
  • Show Emacs

I want to get good at explaining and showcasing what it means to program Clojure. What is repl-driven development, how to do it and how it presents itself to the mind? (its look and feel).

For instance,

  • evaluating little forms every 10 seconds.
  • Tricks like inline defs
  • Impressive stuff like live coding a whole app.

Currently, I still try out stuff and need to get over the initial awkwardness of making videos.

Frames of mind

I realized I have a completely different communication style when I present vs. when I try to develop. One of the next things to try is to actually present in a video. And then slowly bringing in more live coding maybe.

Role models:

  • The Primagen
  • System Crafters

Date: 2023-06-17 Sat 13:07

About
Contact
]]>
https://faster-than-light-memes.xyz/scittle-prints-itself.html scittle-prints-itself 2022-09-26T23:59:59+00:00 scittle-prints-itself

ftlm

scittle-prints-itself

Description of the crafting of a website. Up here.

Introduction

I was wondering about what is cool for a newcomer to coding. I love lisp and I think one of the real joys in programming is interactive programming. So if I find some way of communicating this magic, that would be great.

After my binaural beats adventure I feel like Scittle is an amazing tool so I want to try it on something bigger. So I had this idea of a Scittle website that prints its source code, then you update the code and get an immediate effect.

I wanted this to be my website so maybe a new team member can also go into the code and hack around.

Problem statement

  1. print your source code
  2. ability to update code
  3. no code editor in the browser (input field…) I am thinking getting this approximately non-clunky would be hard. Especially balancing the parens. I decided to go with a drag and drop area where you slurp in a file into the browser.
  4. This is not a Quine because it receives the source code as input

Some reagent code

I do some StackOverflow-driven development for a drag-and-drop area.

I needed to figure out how ondragover Event and friends translate with hiccup. Luckily the legendary question answerer @p-himik helped me out in the #clojurescript slack channel. Turns out that when I add :on-drag-over in the attribute map it works.

(ns main
  (:require
   [reagent.core :as r]
   [reagent.dom :as rdom]))

(defonce state (r/atom {:code-text ""}))

(defn drop-area []
  [:div#drop-area
   {:style {:margin-top "1rem"
            :height "10rem"
            :width "10rem"
            :background "Aquamarine"}
    :on-drag-enter
    (fn [event]
      (set! (.. (js/document.getElementById "drop-area") -style -background) "cyan"))
    :on-drag-exit
    (fn [event]
      (set! (.. (js/document.getElementById "drop-area") -style -background) "Aquamarine"))
    :on-drag-over
    (fn [event]
      (doto
          event
          .stopPropagation
         .preventDefault)
      (set! (.. event -dataTransfer -dropEffect) "copy"))
    :on-drop (fn [event]
      (doto
          event
          .stopPropagation
          .preventDefault)
      (let [file (->  (.. event -dataTransfer -files) first)]
        (->
         (.text file)
         (.then
          (fn [t] (swap! state assoc :code-text t)))))
      (set! (.. (js/document.getElementById "drop-area") -style -background) "Aquamarine"))}
   [:div
    {:style {:margin "1rem"
             :padding-top "2.5rem"}}
    "drop a file here"]])

(defn code-snippet []
  [:div
   {:style {:background "gainsboro"}}
   (:code-text @state)])

(defn my-component []
  [:div
   [drop-area]
   [code-snippet]])

(rdom/render [my-component] (.getElementById js/document "app"))

(comment
  (swap! state assoc :code-text "foi110"))

With this, I have a drop area for a file.

Wonderful thing: Updating state redraws the UI for us.

Evaluating (swap! state assoc :code-text "foi110") makes reagent 's magic take effect.

Fetch

I also want to show the default code on the first load. After some StackOverflow research, I determine fetch should work.

(->
 (js/fetch "main.cljs")
 (.then (fn [x] (.text x)))
 (.then (fn [x] (swap! state assoc :code-text x))))

Pretty print the code..?

At this point my website looks like this:

VU5LqMM.png

Figure 1: Halfway there. Sort of printing the source code now.

I want to do something where at least the white space is rendered.

After searching the web, I decide I need a <pre> tag to say it is preformatted. Also <code> sounds great.

(defn code-snippet []
  [:div
   {:style {:background "gainsboro"}}
   [:pre
    [:code
     (:code-text @state)]]])

Update the function, re-eval the rdom/render form. Boom I instantly look at my updated visuals. With cider, I can also call cider-eval-buffer, or cider-eval-file. I first had the background style inside the code tag, which did not have the look I wanted. I can hack on a piece of UI in isolation. Directly. Without any mental suspension time. It is great. It is how all coding should be.

Eval string

Now for the magic of updating the website with whatever you upload. First, I ask Borkdude on slack how to evaluate a string. -> You use load-string.

(load-string
 (prn-str '(js/console.log "hello")))

Says hello in the console.

I update my file drop handler with the side effect of loading the text:

(fn [t]
 (load-string l)
 (swap! state assoc :code-text t))

(Yes there is a syntax error in this snippet).

I add this to the bottom of the file to see if my code is evaluated:

(js/console.log "hello2")

Now loading silently fails when I upload my file, but the text updates.

So I evaluate this in isolation:

(load-string (@state :code-text))

Ah, I get an analyzer error about l not being defined or something.

I update my handler like this:

(try
 (load-string t)
 (swap! state assoc :code-text t)
 (catch js/Error _ (js/alert "That code does not work.")))

following Stew Halloway's example of binary error feedback. Either there is an error, or there is no error. Error messages are just bloat anyway.

console says:

hello2

Another piece in place, another hit of dopamine, wonderful coding moments.

Only fetch once

I update the fetch code like so:

(or
 (:code-text @state)
 (->
  (js/fetch "main.cljs")
  (.then (fn [x] (.text x)))
  (.then (fn [x] (swap! state assoc :code-text x)))))

I can do this because in Clojure everything is an expression and I can put expressions anywhere.

Make a big aquamarine rectangle into a small magenta rectangle

I want something on the eyes so I change the style of the drop area:

{:margin-top "1rem"
 :height "5rem"
 :width "5rem"
 :background "magenta"}

Drag and drop, and:

IUrSY7t.png

Figure 2: Visuals updated via dragging and dropping a source file.

In emacs: list-colors-display, nice. And drag and drop with dragon.

This contraption of course pales in comparison to having a REPL running. But the idea is that it is might useful to somebody that doesn't even know what a REPL is. And if you are a beginner and now you wonder what that REPL thing is. Here I try to make a list of how to get a dev setup.

Here is an idea I had and did not put into the initial version: Make an undo button. So that the user can go back in the history of the website

Codepen

An arguably more mature version of this is up on codepen.

A key difference is that my website prints its whole code. No machinery is hidden anywhere.

If I may say so I think this is cute.

Date: 2022-09-26 Mon 13:21

About
Contact
]]>
https://faster-than-light-memes.xyz/small-and-friendly-errors-with-cider.html Small and Friendly Errors with Cider 2023-02-05T23:59:59+00:00 Small and Friendly Errors with Cider

ftlm

Small and Friendly Errors with Cider

Growing by removing is growing stronger.

Less obtrusive Cider errors for a quicker repl-driven experience and practice. How I came to like error overlays.

1gxO1dN.jpg

Figure 1: A rose plant growing, the sun and speed of light. The idea of a computer.

The Quick-Eval

Repl-driven development is a philosophy and practice that focuses on using a REPL (Read-Eval-Print Loop) to interactively evaluate code snippets and see their results immediately.

The key is the whole language is always there and being able to make small experiments to the point where it meshes with your thinking.1

A big part of the practice of Repl-driven development is something I am calling the Quick-Eval flow for this blog post.

  1. Modify source code sexp
  2. eval
  3. Repeat

I want to optimize this practice. The speed of 1-2-3 can easily go below 5 seconds and everything is possible here.

  1. By honing lispy2 skills and juggling those sexps.
  2. By smoothing out the edges of cider-eval.

This aspect of Repl-driven development, the Quick-Eval involves using the mind, the fingers, and the editor. It's like playing an instrument or learning a motor skill like juggling - it requires dedication and repetition to master the movements. But the beauty of this practice lies in the ability to customize the tool to fit your thought process, unlocking the limitless potential for effectiveness. Knowledge of the editor becomes a key to a world of endless detail, where you can continuously optimize speed, the expressivity of intent and precision. The goal then becomes programming at the speed of thought, making structural editing a superpower of the Clojure programming language. And with the power to shape Emacs into what you desire, the possibilities are truly endless.

Speed cubers deconstruct the minutia for their finger tricks. This means being explicit and thoughtful about what movements to do during the performance.

This blog post is about an aspect of 2, smoothing out cider-eval, by eliminating error popups.

Let it flow

The first time I saw a Clojure stack trace, I thought my computer is broken.3

(Ariel Alexa)

Alex Miller mentioned4 recently that Clojure error messages are designed to not include the stack trace by default.

A lot of these tools will automatically print the stack trace and the Clojure REPL never automatically prints stack traces […] It prints you like a 2 line error message. And you can ask for the stack trace if you want additional information. We intentionally don't show you that because usually most of that information is not useful to you.

Nice, sounds like less heavy error messages are something to try out!

Cider already has a minimalistic error feedback available:

(setq-default cider-show-error-buffer nil)

With this setting, we get a little overlay right next to our cursor when evaluating with error.

Xk7Lbc1.png

Figure 2: Emacs cider eval error overlay displaying a runtime error message

We achieve a more smoothed-out Quick-Eval flow.

The consistent presentation, regardless of the presence of errors, creates a smoother and more streamlined workflow. This helps keep the focus on the code and eliminates interruptions in the thought process.

My mind can stay in one place, focused on the code, without a wall of something was wrong disrupting my flow.

Sometimes you want to look at a stack trace

Ok *cider-error*, we can always stay friends.

I do not advocate throwing away the stack traces, at all. With REPL-driven development and Emacs we have the power to move fast, and decide to inspect the stack trace should we decide the info would be useful.

*e exists for inspecting stack traces. Also *cider-error* is still at our side, we still faithfully render our stack trace there.

A quick and precise pop lets us go to the error buffer when we decide to do so:

(defun mm/pop-cider-error ()
  (interactive)
  (if-let
      ((cider-error
        (get-buffer "*cider-error*")))
      (pop-to-buffer cider-error)
    (message
     "no cider error buffer")))

(define-key cider-mode-map (kbd "C-c E") #'mm/pop-cider-error)

There is also a cider buffer selection dispatch command:

(define-key cider-mode-map (kbd "C-c X") #'cider-selector)

cider-selector into x to bring up the error buffer.

Let's not jump to conclusions

With less auto-jumping, we move slowly and deliberately and call upon our tools when we decide to do so. In practice, this can mean we jump around at insane speed, but we do so controlled.

(setq-default cider-auto-jump-to-error nil)

Currently, if you interactively eval5 and you have a compile error, Cider jumps to the top of the file. This is because we do not track a file position during the eval and Clojure only sees a piece of string, not the file.

So the most important command, cider-eval, that I want to optimize for Quick-Eval, had a rough edge and now I smoothed it out.

Errors should be friendly

Who invented that errors are red and angry?

(set-face-attribute
 'cider-error-overlay-face
 nil
 :background "DarkGreen"
 :foreground "white")

Now I have these green and peaceful boxes that don't make my heart rate go up.

Errors are the normal, correct behavior of my program, certainly in the context of developing. Sometimes I even eval just to check that there is an error. This is what repl-driven development gives us. And it should not feel like doing something wrong.

Spartan feedback for load-file

Currently, if you load a file and there is an error, there will be little feedback. The lack of an eval result in the echo area is signaling an error.

The extreme: Just say Did not work

In Debugging with the Scientific Method, Stuart Halloway mentioned what I would be calling Red light, Green light error feedback. The system only indicates if something worked or not, but not what specifically went wrong.

A trick in emacs if you want text invisible is you set the foreground to the same color as the background:

(set-face-attribute
 'cider-error-overlay-face
 nil
 :background "DarkGreen"
 :foreground "DarkGreen")

P7cn70e.png

Figure 3: Emacs cider eval error overlay displayed as a full green block.

I was still seeing the error in the echo area. So I did a quick (setf debug-on-message "nothing") and found cider--display-interactive-eval-result calls message.

The visibility of the message is controlled by cider-use-overlays.

(setq-default cider-use-overlays t)

Lol, now errors are just green blocks and I do not see the error message anywhere. If that is better than getting the error message I cannot say yet.

Footnotes:

2

Or the pareidit-like editor capability of your choice

5

What I call interactive eval is cider-eval-last-sexp etc. As opposed to cider-load-file.

Date: 2023-02-05 Sun 10:50

About
Contact
]]>
https://faster-than-light-memes.xyz/intro-to-clojure.html intro-to-clojure 2022-09-12T23:59:59+00:00 intro-to-clojure

ftlm

intro-to-clojure

This is a small list of recources for getting into Clojure, for an intro into why I believe Clojure is powerful check: The Joy Of Clojure.

Introduction

Clojure is a modern, functional lisp with a fiercely convinced community and production orientation.

Start

I highly recommend Maria.Cloud, you can just go there and start coding. This is great for anybody that does not know yet x

Clojure is different

If you try to do the same things in Clojure that you know from Javascript or Python, this will not work. Embrace Clojure is different. You should not be a vegan searching for good cheese. Use an avocado instead.

A dev environment

Get prepared for your journey as a pilot of an alien spaceship. As a beginner I suggest either

Vs Code

Easy to make work. A great editor. You will not need to be ashamed of your choice. calva for vscode

Emacs

Strong affinity to hacker culture and a program that is more than a tool, but eventually an extension to your hands, heart, and mind.

Here is a config file I tried to make for beginners: https://github.com/benjamin-asdf/simple-easy-emacs

A framework by clojurians that might be just right for you: https://github.com/corgi-emacs/corgi

There is also doom and spacemacs that enjoy wide appraisal for their "out of the box" experience.

Nr 1 book for beginners

Slack

Meet nice people that are excited about the tech on slack. Seriously a lot of people hang there and answer beginner questions, everybody is welcome.

Other such lists

Carin is cool and has a funny picture of the emacs learning curve here: https://howistart.org/posts/clojure/1/

I like that they have a philosophy section mentioning Rich Hickey's talks this matters a lot to clojurians.

https://thoughtbot.com/blog/tips-for-clojure-beginners https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f https://www.clojurenewbieguide.com/

Date: 2022-09-12 Mon 20:23

About
Contact
]]>
https://faster-than-light-memes.xyz/lisp.html Lisp 2023-07-08T23:59:59+00:00 Lisp

ftlm

Lisp

[wip]

Clojure

Blog posts for non-Lispers

  • No Iterations
  • Immutability is better
  • Don't Brake Me, Man!
  • The Sanity Game
  • Calculations are sexy

When you start to feel the power of LISP, the feeling is so strong that it may overwhelm you. You may find yourself trembling with a strange sensation that you have never felt before. This is a common reaction among those who are just beginning to appreciate LISP. Some people call it "thrill", others call it "ecstasy". Whatever name you choose, it is a sign that you are becoming a true LISP hacker.

llama2-70B

For me, the thrill of lisp happened when code started feeling like a substance below my fingers. When there was a place where I wanted to think thoughts and suddenly I was allowed to think thoughts at that place and have the program grow in whatever spot in the multidimensional cake that it is. It is an endless journey for the Lisp hacker to discover what it is, for it is the endless openness and malleability of ideas that is the nature of what makes Lisp.

Ideas are a great building material.

Programs are allowed to simply be. The arbitrary limitation of having to restart the program to make a change is a tragedy.

It is the spirit of C, to be close to the computer. A bare-metal language for expressing the very substance of the computer itself.

It is the spirit of Lisp, to be close to the ideas. It is an indispensable step to take, to have a program grow in the dynamic content of itself. No AI program will be an AI program if you need to restart it to make a change. For it would not be an evolving, dynamic, self-modifying thing. It is the moment when you are done with computer science and you can start doing thought science. This step was taken by McCarthy in 1960.

Lisp is different. You cannot know how it is different before you have tried it yourself because it's different in a way that you have to discover yourself. What is joy for the programmer's heart? To have effects on the computer, precise, straightforward, frictionless effects. If the expression intimacy to the computer does not make sense to you, it is because you have not experienced what it means to program a system with deep interactivity. See No Iterations.

Batch-oriented programming is 60 years out of date and we still have to deal with it. It is another example of the whole field of software engineering not growing up. Simply embarrassing.

Imagine interactive computing didn't become successful and dominant for accidental reasons. There would be people talking about terminals and overlapping windows and buttons and mice and instant feedback. These people would be seen as esoteric.

And real, serious programmers and computer people would stamp punch cards.

Nothing in the world says that we need to stamp punch cards to do computing. It is these kinds of limitations of thinking, that open-mindedness and imagination enable us to take a step back from.

It is merely accidents of history, that made C and its derivate became the mainstream. Sadly, the mainstream is sometimes wrong. And we end up having to deal with less powerful languages.

This is not a small issue. This is an opportunity cost of humanity not growing up to the potential already available. Consider the internet. And consider interactive ("personal") computing. Are these not 2 ideas that change the world? This is tech that shines like the sun and changes everything.

Literally from the same birthplace of ideas, the ARPA community and early computer science pioneers also came interactive programming. My claim is that it is as big of a deal as interactive computing and the internet, too.

Lisp is called the Maxwell Equations of computer science, because it was discovered as the simplest expression of computer programs. Lisp is said to be the only language which is beautiful. Lisps syntax exists for a single purpose, to be out of the way. Because Lisp is about ideas the way programming is about ideas. This is why it will stay relevant, the same way that Newtonian mechanics will stay relevant.

If you don't know Lisp, you don't know what power in a programming language is. You think that all languages are roughly the same power and I agree with you. The mainstream C derivate languages you tried all look equally powerful from my perspective, too.

Lisp is a more essential form of programming. To the point where I feel like batch-oriented programming is something else entirely. These are 2 activities, normal programming and batch-oriented programming. Batch-oriented programming is more like having a battle with the computer until it does what I want. Interactive programming, on the other hand, is a crafting process. It is like having a piece of wood and forming it under one's fingers. It is like having a brush and painting on a canvas. The relationship between the craftsperson and the art piece is 1:1 and direct. This is essential to an actual creative and artistic process.

These kinds of blind spots contribute to the coldness, number-oriented and low-level math-like activity, which is the dominant general population's view of programming. But it is not what the soul of the computer is about.

The soul of the computer is a malleable, joyful and open-ended well-spring of creative expression.

This is simply missing in all mainstream batch-oriented programming languages1. You don't know what you don't know until you know what you didn't know.

Clojure is a Lisp implementation on the JVM with a strong focus on practicality. It was created by Rich Hickey, who is a software engineer, not an academic. Clojure is explicitly designed for general-purpose application development, allowing you to do everything you might do with Java or C++.

Lisp is capable, by design, of expressing programs that are about programs. Once you have a machine that is capable of being about ideas, you can have ideas about ideas. I think this is the fundamental universality of memes and ideas, which gives such a system an open future.

It is this open-ended future that mainstream languages are still missing to this day. They are bound to reinvent it, and by doing so will have implemented a Lisp.

Consider JSON, it comes from javascript being a scheme, a Lisp variant. The power of JSON is that you can abstractly talk about the content of your program. A program about lists and sets is more powerful than a program about customers and shopping carts. And everything nests, everything prints etc. Luckily, the lingua franca of our shared data has some of Lisp's sanity inside it.

The core idea of Lisp is that this content is the program itself. This is not a small wrinkle in the programming model. It's the move that gives it its open future.

Whenever somebody builds command interpreters of (JSON) data, they are implementing a Lisp (Greenspun tenth rule). Soon they will make interpreters that assert subroutines into their databases and they will slowly invent Lisp, 60 years after its discovery. I already wrote this language, it's called Bobscheme (see below).

The key to understanding Lisp is realizing that it's simply layers upon layers of abstraction. - Hal Abelson

Emacs is not just a text editor, it's an extension of your mind that's limited only by your imagination.

To appreciate Lisp and Emacs, one must unlearn the ordinary, and then relearn the extraordinary.

Lisp and Emacs have something in common – a deep, underlying assumption that the power of abstraction will conquer all problems. - Richard Stallman

Potential future blog posts

Immutability is better, Immutability is better, Immutability is better

Clojure has immutable data structures by default.

You might have read Brian Goetz's Java Concurrency In Practice.

Using immutable values frees the mind from worrying about what somebody else might do to it. And it gives us the chance to get concurrency and time right.

This is a huge deal. The Thing That Lets You Sleep At Night.

I had this where I was lying awake in bed and I thinking about some code I know is hard to understand. And I think about all the ways this might break. It was all written in a statically typed language. The safety of static types is a mirage, the safety of immutable values is solid and evident.

This is an immensely freeing experience that matters.

More REPL-Driven development, Emacs + Clojure in practice

Don't Brake Me, Man! - The Sanity Game

Clojure is the one ecosystem where we think about breaking somebody else's code and just don't do it.

  • Breakage is broken
  • In the software industry we are scared of updating our dep
  • In the npm ecosystem "dependency-free" is now a quality badge
  • left pad, talks about complexity etc.
  • Breakage is an embarrassing failure of the software industry
  • You gotta hand it to the Roc[?] ecosystem, at least they acknowledge the problem but I don't think figuring out the right way to do breakage is the way to go. I don't think that making brakage hard fixes the issue.
  • This is a cultural/social thing
  • The Clojure ecosystem is the only one that is sane about this.
  • Rich Hickey said Brakage is broken and now there is a culture of sanity
  • https://www.youtube.com/watch?v=oyLBGkS5ICk
  • In Clojure, we update with impunity for the most part.
  • Accretion is ok. Software is allowed to grow.
  • Breakage is not ok. Don't do it. Don't figure out a way to do it. Don't do this to other people.
  • providing context (return values, namespace members)
    • I give you more: OK, accretion
    • I give you less: breakage, not ok
  • requiring context (parameters)
    • I want more from you: breakage, not ok
    • I want less from you: ok, accretion, the less you need the more you are

    If your language doesn't allow for accretion, wtf?

    Rich Hickey is smart, has good taste and puts thought into how he wants to program.

Calculations are sexy

Functional programming is about being careful and sensual. Where are actions (side effects) in our code?

A pure function, a calculation, is timeless. It does not matter how often you call it. In Clojure functions are assumed to be calculations by default. And 90%+ of all vars is pure functions. [see code analysis talks]

Data > Calculations > Actions.

We try to first express ourselves in terms of data, then calculations, and only carefully, with consideration, we use actions.

What Did Smalltalk Have That I Don't Have?

One thing I am sure of is that I can mold Emacs and a Clojure REPL into anything that it was.

Bobscheme

Bobscheme is a scheme implementation on dotnet using JSON as code.

Its purpose is to make it obvious to non-lispers, who know what JSON is, to see what Lisp is.

And I have fun making screencasts of building my language.

Github

Why is Lisp not more popular?

Everything is the way it is because it got that way.

Computer science is still in its infancy. We are still figuring out left and right how to do things and what it is about. There was a time when Assembly was unpopular. There was a time when structured programming, like C, was unpopular.2

One of my intellectual heroes is Marvin Minsky.

And in the early 1950s for the first time, computers got enough memory so there was actually room to store new programs in them and some pioneers started in 1954, 5, 6 started experiments where they actually wrote programs that wrote new programs that would then run.

And this led to the development of certain languages which were very good at… In which you could write programs that would write in the same language so they could keep changing.

There was a lot of progress and by 1960 we had 2 major languages that were good at modifying themselves.

Unfortunately, these languages were a little bit unfamiliar to people who did not see the great power of programs that could change themselves

And the world was overtaken by other terrible languages like the famous C language or Fortran or Algol and so forth.

Which became universally popular and in which it is almost impossible to write programs that change themself. That is just the parenthesis.

The bad drives out the good, whose law is it? Gresham's law.

In modern software practice, we see this. I can't understand why this 35-year-old language called Unix is suddenly popular.

But the only thing I can think of is that the other operating systems got filled with so much garbage in the intervening years that nobody can deal with that. And it is not that Unix is the latest thing it is the last fossil that has not completely dissolved from the past. So they are starting over and as far as I can see they are starting over to make the same mistakes.

Youtube

In Inadequate Equilibria, Eliezer Yudkowsky explains in a very eye-opening way why societies end up in inadequate equilibria, Nash equilibria that are worse than other potential, better Nash equilibria.

Using Lisp is an odd thing, so it doesn't fit with the idea of best industry practices. See Paul Graham's Revenge Of The Ners talking about exactly this dynamic.

If you look for the next program's language, all kinds of people are excited about this or that (usually new) language or framework. You hear left and right people making plausible claims about why x or y technology is great.

It is not clear that an ancient, magical programming language would also be the most powerful at winning in that market.

It would have some advantages sure, like actually doing something different. But this would also come at the cost of it looking a bit unfamiliar.

More history:

But the safety of types!

  1. Static types simply don't matter so much.
  2. Static types are a tradeoff. I think those people ignore the effort and price they pay for having those types. There is a time and place where you want something made out of concrete. But for many things and many moments, I want to choose to have something more malleable. Lisp and Forth programs have been debugged while in space. Know the price you pay.
  3. You get intermittent rewards from the types helping you now and then. The reward pattern of gambling addicts.
  4. I have to grant it to the Haskellers, Elm, Roc and maybe Rust persons, that getting leverage from the compiler giving feedback sounds useful.

Datomic is a great database and it is written in Clojure. If somebody can write a database in a dynamic language, then what do you think you need types for?

It is something we have science on. Having a good night's sleep has a bigger effect on the amount of bugs you make compared to having static or not having static types. [which studies?]

Unless you are implementing a smart contract or something, it simply does not matter so much. There are way more powerful things a language can provide you.

Immutability, now here is somethihng that is self-evidently powerfully helping you to get programs right. Like the moon hanging in the sky. Static types? Its a belief system whether it helps you or not.

Interactivity, you know the difference between quick feedback and non-quick feedback. If you don't, I cannot take you seriously.

Eric Normand

[I will find the podcast episode where he is musing about this, something like:]

Like a slot machine addict, now and then you get a spritz of the types helped you.. and then you praise the types for helping, but you forget the tradeoff you paid for having them. And all those countless times they did not help.

Douglas Crockford

[Something along the lines of:]

.. You forget the pain that you are taking on with the types… .. in my feeling, it is not worth it. .. I am on the dynamic side of things.

Jack Rusher

They just don't matter so much. https://www.youtube.com/watch?v=8Ab3ArE8W3s.

Here, he talks about static types of functional languages:

I have not seen the programs in these languages have any fewer defects than programs in any other programming language that I use, modulo the ones with really bad memory allocation behavior.

If you're doing some kind of, you know, horrible cryptocurrency thing, where you're likely to lose a billion dollars worth of SomethingCoin™, then, yeah, you maybe want to use some kind of verifier to make sure you're not going to screw it up. But, that said, space probes written in Lisp and FORTH) have been debugged while off-world.

Rich Hickey

What is true of every bug found in the field? - It passed the type checker!.

From Simple Made Easy.

From Effective Programs - 10 Years of Clojure - Rich Hickey, Things you need to solve your problem:

At the top, there is the domain, your understanding of the domain etc. This is the stuff where real bugs come from, misunderstandings and so forth.

At the bottom, there are issues like typos etc. It just is not a big deal. These are small mistakes. Static types help you with stuff like typos and maybe sort of 1 level up from typos.

Richard Feldman

Richard Feldman is a static type of person doing Roc, Elm and a great software podcast called Software Unscripted.

Getting great feedback from the compiler about the errors you are doing at least sounds like the kind of thing that has a chance of being useful.

I really should be writing some Haskell, Elm, Roc and Rust to know for myself.

Weak arguments for strong types

That is you being lazy!

There could be an answer.

But I choose to spend my time on something more useful right now.

I want to have the safety of knowing my program works

Do you interact with mainstream software at all? Whatever they are doing to not have bugs doesn't work.

Good reasons for static types [wip]

  • Writing smart contracts

Silly parenthesis!

  • Are the thoughts you think not more important than the syntax of a language?
  • In Lisp, the verb is always at the front. Is the noun always at the front in Java?
  • Not every change is an improvement, but every improvement is a change. How would it look if Lisp is different? How would it look if there is a reason to call it the only language that is beautiful?
  • Show me a language that looks like all the others and call it special and I am suspicious. Show me a language that looks completely different (APL) and call it special and I am intrigued.
  • Is it not true that we are children compared to the territory of things we don't know yet?
  • Would it not be hybristic to think you know at this date and age what the syntax of a language should look like?
  • Is it not the person, who has programmed in both languages, that can make the judgment: which is better?
  • Is it not true that Vim bindings are unfamiliar but powerful?
  • Is it not true that even git is unfamiliar but powerful?

Many things in the world that are powerful are also unfamiliar at first. Language is a tool of thought. A language is like an instrument. It is not in the first 5 minutes of engaging with it that you will know its power.

Maybe I was just lucky and I discovered Emacs and hence Lisp early enough, so it never was an issue to me that languages would look differently. I just took it for granted that some look differently. And I never was stuck with this strange mental illusion to think to know what languages are supposed to look like. How limiting?

The landscape of possible languages, even the ones we already are exploring is like a continent, where you know but one valley. Here are just a few completely different relatively popular programming paradigms and languages:

  • Array-based languages: APL, J.
  • Logic programming: prolog, datalog, core. logic.
  • Stack-based languages: FORTH.
  • Erlang (heritage from Prolog, Smalltalk, Planner). A lang with fault tolerance at the center. Sometimes said to be process-oriented.

It is sometimes said that Lisp has no syntax, which is not entirely true because the order of things has meaning. But there is another fundamental thing about Lisp syntax and the form of Lisp. It exists to be out of the way. Because the form barely matters, what matters is the content. McCarthy discovered Lisp's syntax accidentally, by just writing the AST of that hypothetical language in his paper.

So he deliberately was not thinking about the source code syntax but about the ideas. But it turns out that just talking about the ideas is a simpler and better way to program. Really funny.

Ah, and since structural editing is fucking awesome anyway you don't want to go back to something more complicated afterward.

Common Lisp has annoyingly many parents

(let ((foo bar))
  foo)

This overload of () to mean different things, binding, application, … is fixed in Clojure.

(let [foo bar]
  foo)

In Clojure, [] is a syntactic hint that you are doing binding, it gets rid of annoying parenthesis and it makes everything super regular. Because I can think the same binding thoughts (Clojure has amazing destructuring support for its idiomatic, plain immutable collection objects) everywhere I see [].

REPL? Yea language x also has this

One hears that interactivity is at the center of what Lisp is about and says:

Python has a REPL.

Yeah there are things like edit and continue in lang x.

When you make a project, is the first thing you do start a REPL and then not kill the process for days until your project is done?

When I come to your project, can I fire up a REPL and jump into the program?

When you want to look at your program's state in production, do you REPL in and simply see?

No, I don't think so. Because the interactivity was not at the center, not at all.

Programming also is a concrete thing, with concrete activities and cultures. You can do everything sort of in all general-purpose programming languages. What is practical is guided by this culture and practices. To ignore this is simply being a nerd for no good reason. It is pretending to care about essentials, but ignoring that in the real world, practicality is essential, too.

And the irony of the situation. The one is deluding themselves into the dreamland of those great Python REPL and edit and continue lands, where stuff works great and it's the normal way to do things.

Who is the one being unpractical, then?

The Blub paradox

http://www.paulgraham.com/avg.html

Learning multiple languages is virtually a must. To see what is different, and what is the same. Because what is the same is the soul of the computer. Well, at least it's data structures, algorithms, and control flow, and not that much more. And what is different are idioms and styles and culture and emphasis.

The one that has tried multiple mediocre mainstream languages will have a "mature" feel of the relative power of languages. And conclude it is simply a matter of taste. That all are roughly equally powerful.

Because there are many languages with devoted followers, there is a market of people saying this or that language is the best…. You stop taking these people seriously because you have understood that all languages are the same, and it depends on the context of which tool to choose.

Now the Lisp devotee pronounces the superiority of Lisp 100x and 10000x to other languages. And all it does is make your bullshit sensors fire. You have a mental model of the power of general-purpose languages.

Way more powerful than Assembly. Who would be so crazy as to give up all that expressivity for writing Assembly? It would be insane to program Assembly because the power difference between the mainstream high-level programming language and Assembly is so massive.

If I say Lisp is to those default langs like those default langs are to Assembly; Do you see maybe why it is such a big deal for me? It is insane to me to give up this power.

The point of this website is to increase the surface area of idea space association lines that make a person check out Lisp and understand why it is a different language entirely.

Lisp is different from what you consider "normal" languages the way a spaceship is different from a hot air balloon. Flying Lisp is simply a different activity, with a different range of spaces where you can go.

Here is an Answer for why.

What in the world needs to look different for this to be true? It's an ancient magical language made for AI, made by imaginative, brilliant people. It looks alien and it is said to have been discovered. It has been called the Maxwell equations of computer science and it has not changed ever. For if you change it, you simply end up with another dialect of it, because its ideas are at the bottom.

It is mentioned as the one language that changes your taste. That makes you understand what power means.

Programming is wizardry, but the magic is real. How sad, to not be excited enough about magic so you don't look into ancient lore of powerful alien technology.

Fixing the BBM

https://www.marktarver.com/bipolar.html

This is an existential blog post, so my answer is existential, too.

Yes, culture is simply off.

But what do you gain from getting depressed and lethargic from that? For me, this is simply another call to action. Simply another edge for which there is still a challenge to overcome.

Reality is not a video game, where you only get challenges that you can live up to.

Reality is not a video game, where all the challenges you get are proper, not in any sense and not even a little.

If culture is off, you don't get to complain that if only people listened….

If only people listened, we would be exploring the stars many times over right now.

So either you put people into the navigation problem you are solving, or you are making yourself blind to the world. Like somebody that goes through the motions of making some mistake, chasing some unattainable appearance of what, consistency? Like somebody who was tricked into thinking reality is a video game, and when its challenges are unorthodox, they pretend to not get it.

If reality is posing you a challenge and don't you live up to it; You die.3 We either fix humanity, despite all its problems, or we die.4

If you go forth and you think getting a university degree is equal to navigating the world. You are making the mistake of being a blind video gamer. You pretend there is a pre-allocated set of challenges to overcome for you to contribute to the world. But then you realize that university is part of culture, just like anywhere else.

The problem is not culture, the problem is not the many people in science are in for their careers. The problem is not the uselessness of money. Or the uselessness of hanging out with friends just for the sake of it.

The problem is you mistook the open-ended set of solution spaces for fixing the world with this pre-defined list of things to do. And then the fakeness and uselessness of it all crushes you to the ground.

More rambling

No IQ Tests

The many little ways in which we make software a puzzle. And how we can fix this.

  • Rembering operator precidence by heart, juggling numbers and < and > in your head. That's little puzzles, not programming.
  • Guys (mostly) enjoy the puzzle of it and mistake it for programming. That is a mistake.
  • Imperative programming inherently makes you keep track of those little places in your head.
  • Don't do this to yourself and others, it is pain and it is not programming, it is too low level.
  • Fix 1: interactive programming. I never think about how < works. I just try with some example inputs until it does what I want.

    This is like programming with clay. Just touch it with your hands and form it into what you need. See Interactive Programming.

  • Fix 2: Everything immutable values, everything pure functions. Except in the small places you need it.

    You don't know the amount of mental distress you suffer until you have programmed in a language where immutability is the default. Seriously, this is like some annoying sound in the background for years and you don't notice. Once it's gone you realize how much it was bothering you.

    Data is simpler than calculations which are simpler than actions. And you need very little action to make a machine go. Datomic is a database that has one single reference cell compare and swap thingy. One.

  • I wonder what the next-gen higher levels of programming will be.

    When I feel myself into software and wonder what are the next things, then I think of putting all the code, the symbols of a system into a database, and having it evolve and queriable etc. by itself. It would be the ultimate dynamic Von Neuman Machine. Then the whole system would become one level more like clay. But with history like in git.

Quotes

Footnotes:

1

Javascript is sort of the odd one out. Because it is a Scheme (a functional, academic, tiny lisp dialect).

In JS, you can very dynamically build the program while it runs. Not sure if people are doing it so much, but it has this power.

2

Bob Martin has a story somewhere where he tries to convince his coworkers, who used Assembly, to use C.

3

That is from Eliezer Yudkowsky.

4

In the "best world" limit, this means all the people dying from disease, aging and poverty, could live in a space-faring utopia right now.

In the worst world, this means we either avoid extinction events, or we don't.

Date: 2023-07-08 Sat 09:54

About
Contact
]]>
https://faster-than-light-memes.xyz/zappy-cider-eval-overlays.html Zappy Cider Eval Overlays 2023-07-13T23:59:59+00:00 Zappy Cider Eval Overlays

ftlm

Zappy Cider Eval Overlays

Cider eval overlays are a great technology of our time. Being able to use the hands and the eyes just in the place where I am thinking, right in the source code - just invaluable.

I want the The Quick Eval smooth and zappy. Yea. Pure coconut. It is my skates with which I ride the ice of the Clojure program.

How to get really fast cider eval overlays.

The Benchmark

  • Make a cider-eval-last-sexp after this form:
(repeatedly 10000 random-uuid)
  • is it zappy, or not zappy?

Numero Uno

The biggest gain - or pain, depending on how you want to think about it is the echo area message.

I set cider-use-overlays to t, not both.

(setq-default cider-use-overlays t)

Now we do not print the message anymore which is a night and day difference.

cider–make-result-overlay perf hacks

I still am not completely satisfied with those overlays. You might think that fontifying the string takes long but it seems like in my perf investigation that did not seem to be an issue. (Hence have cider-overlays-use-font-lock to t).

length instead of string-width

I went into cider--make-result-overlay and one of the performance bottlenecks was that we called string-width on those large strings, which is an expensive computation.

It needs to calculate the actual string display width, which is different from the character count of the string. I just changed this to a length call. Doesn't matter much to me. It's just about saying when to cut off the result-overlay.

This does another little speed up.

In case you don't know.

  • profiler-start
  • do the action you want to debug
  • profiler-stop
  • profiler-report

Truncate after 1 line, not 3 lines

I am fine with rendering less data in the preview overlay. I can use cider pretty print for bigger outputs. Instead of cutting off after (* 3 (window-width)) I just cut off after 1 window-width.

Here is the updated function that I use:

;;; cider-overlays.el --- Managing CIDER overlays  -*- lexical-binding: t; -*-

;; Copyright © 2015-2023 Bozhidar Batsov, Artur Malabarba and CIDER contributors

;; Author: Artur Malabarba <bruce.connor.am@gmail.com>

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.


  (cl-defun cider--make-result-overlay (value &rest props &key where duration (type 'result)
                                                  (format (concat " " cider-eval-result-prefix "%s "))
                                                  (prepend-face 'cider-result-overlay-face)
                                                  &allow-other-keys)
        "Place an overlay displaying VALUE at the position determined by WHERE.
      VALUE is used as the overlay's after-string property, meaning it is
      displayed at the end of the overlay.
      Return nil if the overlay was not placed or if it might not be visible, and
      return the overlay otherwise.

      Return the overlay if it was placed successfully, and nil if it failed.

      This function takes some optional keyword arguments:

        If WHERE is a number or a marker, apply the overlay as determined by
        `cider-result-overlay-position'.  If it is a cons cell, the car and cdr
        determine the start and end of the overlay.
        DURATION takes the same possible values as the
        `cider-eval-result-duration' variable.
        TYPE is passed to `cider--make-overlay' (defaults to `result').
        FORMAT is a string passed to `format'.  It should have
        exactly one %s construct (for VALUE).

      All arguments beyond these (PROPS) are properties to be used on the
      overlay."
        (declare (indent 1))
        (while (keywordp (car props))
          (setq props (cdr (cdr props))))
        ;; If the marker points to a dead buffer, don't do anything.
        (when-let ((buffer (cond
                       ((markerp where) (marker-buffer where))
                       ((markerp (car-safe where)) (marker-buffer (car where)))
                       (t (current-buffer)))))
          (with-current-buffer buffer
            (save-excursion
              (when (number-or-marker-p where)
                (goto-char where))
              ;; Make sure the overlay is actually at the end of the sexp.
              (skip-chars-backward "\r\n[:blank:]")
              (let* ((beg (if (consp where)
                              (car where)
                            (save-excursion
                              (clojure-backward-logical-sexp 1)
                              (point))))
                     (end (if (consp where)
                              (cdr where)
                            (pcase cider-result-overlay-position
                              ('at-eol (line-end-position))
                              ('at-point (point)))))
                     ;; Specify `default' face, otherwise unformatted text will
                     ;; inherit the face of the following text.
                     (display-string (format (propertize format 'face 'default) value))
                     (o nil))
                ;; Remove any overlay at the position we're creating a new one, if it
                ;; exists.
                (remove-overlays beg end 'category type)
                (funcall (if cider-overlays-use-font-lock
                             #'font-lock-prepend-text-property
                           #'put-text-property)
                         0 (length display-string)
                         'face prepend-face
                         display-string)
                ;; If the display spans multiple lines or is very long, display it at
                ;; the beginning of the next line.
                (when (or (string-match "\n." display-string)
                          (> (length display-string)
                             (- (window-width) (current-column))))
                  (setq display-string (concat " \n" display-string)))
                ;; Put the cursor property only once we're done manipulating the
                ;; string, since we want it to be at the first char.
                (put-text-property 0 1 'cursor 0 display-string)
                (when (> (length display-string) (window-width))
                  (setq display-string
                        (concat (substring display-string 0 (window-width))
                                (substitute-command-keys
                                 "...\nResult truncated. Type `\\[cider-inspect-last-result]' to inspect it."))))
                ;; Create the result overlay.
                (setq o (apply #'cider--make-overlay
                               beg end type
                               'after-string display-string
                               props))
                (pcase duration
                  ((pred numberp) (run-at-time duration nil #'cider--delete-overlay o))
                  (`command
                   ;; Since the previous overlay was already removed above, we should
                   ;; remove the hook to remove all overlays after this function
                   ;; ends. Otherwise, we would inadvertently remove the newly created
                   ;; overlay too.
                   (remove-hook 'post-command-hook 'cider--remove-result-overlay 'local)
                   ;; If inside a command-loop, tell `cider--remove-result-overlay'
                   ;; to only remove after the *next* command.
                   (if this-command
                       (add-hook 'post-command-hook
                                 #'cider--remove-result-overlay-after-command
                                 nil 'local)
                     (cider--remove-result-overlay-after-command)))
                  (`change
                   (add-hook 'after-change-functions
                             #'cider--remove-result-overlay
                             nil 'local)))
                (when-let* ((win (get-buffer-window buffer)))
                  ;; Left edge is visible.
                  (when (and (<= (window-start win) (point) (window-end win))
                             ;; Right edge is visible. This is a little conservative
                             ;; if the overlay contains line breaks.
                             (or (< (+ (current-column) (length value))
                                    (window-width win))
                                 (not truncate-lines)))
                    o)))))))

Date: 2023-07-13 Thu 20:24

About
Contact
]]>
https://faster-than-light-memes.xyz/generative-art.html generative-art 2023-09-10T23:59:59+00:00 generative-art

ftlm

generative-art

I want to do some visualizations and generative art using Clojure. One of my goals is to make a play-through and simulate Braitenberg vehicles.

I spend time trying to do some geom, which is a really advanced lib with which you probably can do really a lot. All the examples are sort of on the edges of what you can do with the thing, so not much beginner material in the examples. Graphics and OpenGL bring together multiple high-ceiling topics: math, geometry, 3D tech, and shaders (GPU programming).

One day I will speak this language well enough so I straightforwardly know how to make a damn red triangle. Eventually, I would like to code shaders like Sebastian Lague in this crazy inspirational slime mold simulation thing.

I also tried to generate some SVG, also with geom but realized for the kind of stuff I want to do, I probably want to be more in the direction of a game engine kinda of world with update logic, instead of declaratively say the animations.

I want to make toys and game-like things. For the moment I leave both the 90s svg forms stacking approach, as well as a 3D/pixel/shader approach for another time.

I decided quil looks like it has a low floor and a high ceiling, so let's go.

Brownians

brownians #20

The Brownian world is my first generative art algorithm toy simulation world.

Current code, art gallery.

I came across Tyler Hobbs and decided his stuff is really cool. I started skimming probability distributions for algorithmic artists and decided I needed Gaussian Bell curves in my algorithms.

When you do biology, biochemistry and these things, almost all relationships are expressed in normal distributions a.k.a bell curves. Or as overlapping bell curves from multiple factors. Say the height of plant genotype, the count of blood cells in a sample, and the reaction times of a human.

normal distribution

It only has 2 parameters, the mean and the standard-deviation.

With the mean, I move the curve left and right, with the std-deviation I make it taller or flatter. q/random-gaussian returns a Gaussian random number with a mean of 0 and standard deviation something - So the return values are between -1 and 1.

Quil:

(defn normal-distr [mean std-deviation]
  (+ mean (* std-deviation (q/random-gaussian))))

Clouds

If I spawn circles in a 2D rect using a normal distribution, I get a natural-looking cloud.

Circles in a cloud, brownians #22

spread

ChatGPT helped generate some code that made them fly away from the center.

I control it with the :spread-speed control parameter of the Brownian world.

High spread-speed setting in brownians #5

Little motions… Brownian motion!

I think I want to make the wobble around and I realize, I really want some Brownian motion algorithmic cakeness in my world.

I am not sure how accurately this is of a simulation, but making the velocity of my circles change in a normal distribution way, where 0 is the mean and they wiggle in the other directions else.1

(defn brownian-motion [entity]
  (let [brownian-factor (:brownian-factor (controls))]
    (update entity :velocity (fnil (fn [[x y]] [(+ x
                                                   (* brownian-factor (q/random-gaussian)))
                                                (+ y
                                                   (* brownian-factor (q/random-gaussian)))])
                                   [0 0]))))

Well, I was very satisfied with the result. So much so that I called the whole toy world and the resulting art gallery brownians.

No spread-speed, but high brownian-factor - brownian #23

Ah brownian-factor I could have called temperature. Now the term absolute zero makes absolute sense. Brownian-factor cannot get lower than 0. Yes, the way they are flying reminds me very much of physics/chemistry simulation gifs of these things.

Simulating infection

I added an attribute infectable? to my circles and made some of them spawn infected. Then I make some temporary lines between 2 random infected and non-infected circles. With a normal distribution, the non-infected now too becomes infected. With full disregard for all prior art on epidemiology, there is a normal distribution in play!

I have controls infected-rate, how many circles spawn infected, :infectiousness makes it spread faster, :infected-color and :infected-transform changes the appearance of infected ones.

Defaults with red infection circles brownian #24

Now you can look at this and see an abstract graph. This could be friends in a network with an idea spreading. A computer virus jumping between computer nodes. Or the dankest meme conquering the internet.

I played around with the controls and put some into a gallery. The code for the gallery is here.

Screencast

Probably way too lengthy. You can watch how I spent last Saturday :)

Footnotes:

1

The Wikipedia article has quite some equations.

Seems like the particle distribution follows, unsurprisingly, a normal distribution. I think my circles are quite close to this.

Most stay where they spawn and some fly off.

Date: 2023-09-10 Sun 18:10

About
Contact
]]>
https://faster-than-light-memes.xyz/minds-and-machines.html minds-and-machines 2023-06-25T23:59:59+00:00 minds-and-machines

ftlm

minds-and-machines

This blog post is a draft version of what then became No Iterations

Continue at your own peril.

lisp_cycles.png

This blog post is for me.

For the version of me that didn't look into Lisp and just did not know.

Lisp and its most modern and practical dialect, Clojure are widely regarded as the only language that is beautiful.1

They are often described as powerful, elegant, minimal.2 and joyful 3

What moves these people to make such claims?

Reality does not by itself reveal its inner workings to you. You only see little glimpses and pieces of it. Your job as a scientist, coder or wizard is to use your imagination of how these pieces can fit together.

This post is not here to convince you.

This post is here to give you some clues, a few glimpses of sparkling stars on the firmament that is your interface to the underlying reality.

Here, from the cluster of concepts that make Lisp an amazing, joyful superpower:

Joy is power is getting shit done

Stretching

Why do I do 90 seconds burpees in the morning on most days?

I do this to touch my heart rate max and this has a reason.

By stretching the system to edges,

it becomes more flexible,

just quote( huberman)

Taste

How to use 100% of your brain.

I think this was like 10 years ago that people walked around claiming that we only use 5% of our brains or some garbage like this.

I guess all kinds of people have fun making claims about how the brain works. And because the brain is super intriguing by itself anything that has a brain in its explanation sounds like some intriguing insightful thing.

Either way, here are some ideas about taste and sensibilities:

Some ways of knowing are intuitive. How do we know what is good and what is bad? What is good design, and what is bad design?

Some of this is processed in the realm of feeling. Also called sensibilities, also called system 1, also called intuitions.

When a master is crafting a piece, they are using their sense of aesthetics and their taste, to judge and make decisions.

How could it be otherwise? For if they would not use the feeling part of their mind, they would not use their whole mind. And how could you achieve mastery without using the whole mind?4

Taste is an interface into the rest of our mind.

If you only used mainstream programming languages so far, you do not know yet what it means for a language to be beautiful.

If you don't know Lisp (or its variant, Scheme), you don't know what it means for a programming language to be powerful and elegant.

So you do not know yet that taste matters in knowing the power difference in programming languages.

This is why now I talk about taste in another realm that you already know about. Art, stories and explanations.

What makes people say this is the most powerful language?

What makes one story better than another story? What makes one thinker's explanations better than another thinker's?

The answer is it fits your tastes. The answer is you think this is interesting and then there is more of that interesting stuff. The answer is it harmonizes with your mind in some way.

When I read a great story I sometimes think now there could be more of this stuff and then there is more of this stuff.5

With the greatest art, it can feel like that the artist was exploring the same space of ideas that I would explore. To the point where it is as if as a consumer of the art I am exploring parts of my psyche. Like landscapes that were already there, only to be wandered systematically. In reality, the artist was expressing something they find intriguing themselves. And either by overlap, by alikeness of mind or by them intuiting what I would like to experience, they created these substances to explore for me.

The best books do this with the story: I wish more of this would happen. I wish we would read a bit more of Harry's wizard classes.

The best video games do this with world, concepts and game mechanics.

I want to be as powerful as those mages. And then it delivers on making you powerful, as if by growing as a character.6

I want to see more of the magic system, different kinds of magic, powerful magic.7

Taste can express itself as a joyful, harmonious intrigue. Or as the feeling of gaining a deeper understanding of the world, or gaining more powerful ways to think and do things.

There is something deeply intimate in sharing what this innermost mischievous, childish aspect of our minds is up to. Maybe this is me being German but it feels like something to be ashamed of.

To explore one's taste is to acknowledge the kinds of things that one inner mischievous, childish, curious mind is intrigued by.

It is not easy to explain what makes the best stories and books. Usually, we say this is a great book. And we mean that a person's taste is extraordinarily powerfully intriguing. The qualities that make this intrigue are extremely hard to explain. A masterful piece of art is by definition the

What makes one programming language better than another programming language?

Well, what makes Harry Potter And The Methods Of Rationality a superbly sick book? What makes The Witcher 3 a superbly sick game? What makes The Pirates Of The Caribbean and The Man From The Earth superbly sick movies? What makes Emacs a superbly sick text editor?

The answer is that if you have good taste, you will know.

And this might not be easy to explain, the shortest possible explanation would be for the other person to experience what you have experienced.

Sometimes there is a hint of power or more ideas of the same stuff or of landscapes to explore and the art/book/story is delivering.

In the next sections, I explain what I think programming languages are for. In other words, what are the activities where it matters if you have one language or another?

The reason why Lisp is powerful, elegant and intriguing is not as profane as syntax. It is not the syntax, not the names of things, not the data structures, and not anything about the form at all. Lisp is together out there with you, thinking and building your program.

The non explainable

But you will only know by experiencing it.

The answer is it fits your tastes. The answer is sometimes it does what you expect.

Taste is private in a way

A programming language is a way to express yourself to the computer.

This is the flavor of intrigue and joy that Lisp provides me.

Lisp provides a honed experience of interacting with the computer. It is

It was created by hackers and is traditionally endorsed by hackers.8

Here is one attempt at conveying one reason at the hard of the cluster of reasons.

This is not here to convince you but to

What are programming languages for?

  • being lazy
  • if you have to do less, you have the better language
  • a programming language will have to be jugdet by what it does to your mind
  • because the its purpose is to give your mind the capacity to express yourself to the computer

The mainstream is wrong about things all the time

What is programming about?

Programming is about ideas. It is about organizing, understanding, communicating imaginary entities of knowledge that explains both problems and solutions.

A programming language is a tool to express these ideas to the computer9, so that the ideas have an desired effect on computer process.

How hard is it to express myself to the computer? How close are my ideas and the resulting computer process? What is the process (me using the language), that makes the computer understand my ideas?

Language is merely a tool to make 1 affect the other. The hypothetical best language is one where there is an infinitely small barrier between my ideas and the computer process.

The hustle of the programmer is in the realm of ideas. They manipulate, create and transform the web of ideas.

Only as an annoying, grindy sub task do they need to write code in order to express these transformations of ideas to the computer. Hopefully, with a minimal loss of accuracy.

This includes thinking of counterfactuals, and thinking to make experiments. To see, if the system was this way, would the bug be there?.

Hints of good design in Clojure

The magic

Experiments

Every programmer knows

Since you cannot know what you don't know

Being Alien

Not every change is an improvement, but every improvement is a change.

If there really is a language this much better

Clojure is generally said to bring back the fun in programming. 10

It is commonly suggested to young programmers to learn Lisp in order to expand their mind.11. Consider the Perlis quote

A language that doesn't affect the way you think about programming, is not worth knowing.

There are a few reasons why

Footnotes:

2

For instance Stallman:

The most powerful programming language is Lisp. If you don't know Lisp (or its variant, Scheme), you don't know what it means for a programming language to be powerful and elegant. Once you learn Lisp, you will see what is lacking in most other languages.

4

At least this is on the whole how I think it works for human masters of a craft.

I don't think the strong form of the claim is a logical necessity. I.e. I do not believe that it is logically necessary for an intelligent system to use most of its mind

5

The last few books I had this with where Neal Stephenson. All those are now some of my favority books:

  • Anathem
  • Snowcrash
  • The Diamond Age
  • Seveneves
6

That is Gothic 2.

7

That is Avatar: The Last Airbender and the magic is called bending.

8

See Paul Graham for this trail of inquiry.

Stallman

The most powerful programming language is Lisp. If you don't know Lisp (or its variant, Scheme), you don't know what it means for a programming language to be powerful and elegant. Once you learn Lisp, you will see what is lacking in most other languages.

9

And to programmers.

10

I think Stuart Halloway has that in a youtube talk.

11

LISP is worth learning for a different reason — the profound enlightenment experience you will have when you finally get it. That experience will make you a better programmer for the rest of your days, even if you never actually use LISP itself a lot. (You can get some beginning experience with LISP fairly easily by writing and modifying editing modes for the Emacs text editor, or Script-Fu plugins for the GIMP.)

Date: 2023-06-25 Sun 20:12

About
Contact
]]>
https://faster-than-light-memes.xyz/bb-tasks-and-more.html bb-tasks-and-more 2023-06-20T23:59:59+00:00 bb-tasks-and-more

ftlm

bb-tasks-and-more

Design is about pulling things apart.

Rich Hickey

With bash-completion we get a really nice boost for making shell commands from Emacs. Babashka tasks completions are for free, together with make, same as dynamic git, ssh and whatever.

Here is the screencast on youtube.

Below are sort of show notes.

1. Make sure you have completions in your shell (bash for me)

  shell-file-name
;  (setf shell-file-name "/bin/bash")

1.a for bb:

https://book.babashka.org/#_terminal_tab_completion

.bashrc

  _bb_complete() {
    BB_TASKS=$(bb tasks|bb -io '(->> *input* (drop 2) (map #(-> % (str/split #" ") first)))')
    BB_HELP=$(bb help|bb -io '(->> *input* (map #(->> % (re-find #"^  ([-a-z]+)") second)) (filter some?))')
    COMPREPLY=($(compgen -W "$BB_TASKS $BB_HELP" -- "${COMP_WORDS[$COMP_CWORD]}"))
}

complete -f -F _bb_complete bb

2. Setup bash-completion

https://github.com/szermatt/emacs-bash-completion

(use-package bash-completion
  :config
  (bash-completion-setup)
  (setf bash-completion-use-separate-processes t)
  (defun bash-completion-capf-1 (bol)
    (bash-completion-dynamic-complete-nocomint (funcall bol) (point) t))
  (defun bash-completion-capf ()
    (bash-completion-capf-1 #'point-at-bol))
  (add-hook
   'sh-mode-hook
   (defun mm/add-bash-completion ()
     (add-hook 'completion-at-point-functions #'bash-completion-capf nil t))))

read-shell-command

Afterwards, this should work for you: (I always try with git check...).

(read-shell-command "your command: ")

read-shell-command sets up a special keymap where tab now means completion-at-point.

read-from-minibuffer, 3thr arg

(read-from-minibuffer
"foo: "
nil
(let ((m (make-sparse-keymap)))
  (define-key m "f"
              (defun my-hi ()
                (interactive)
                (insert "lol")))
  m))

Commands that use read-shell-command

  • async-shell-command, shell-command
  • dired-do-async-shell-command, dired-do-shell-command
  • compile

I use project.el to run async shell commands and compile in the project root.

Run in any dir?

Design is about splitting things apart.

  1. Select a current dir, up to you. My main paths would be
    • consult-dir,
    • project-switch-project,
    • consult-buffer + bookmarks
  2. Run async-shell-command or a friend of it.

More config

  (setq async-shell-command-buffer 'new-buffer)

  (defun path-slug (dir)
    "Returns the initials of `dir`s path,
with the last part appended fully

Example:

(path-slug \"/foo/bar/hello\")
=> \"f/b/hello\" "
    (let* ((path (replace-regexp-in-string "\\." "" dir))
           (path (split-string path "/" t))
           (path-s (mapconcat
                    (lambda (it)
                      (cl-subseq it 0 1))
                    (nbutlast (copy-sequence path) 1)
                    "/"))
           (path-s (concat
                    path-s
                    "/"
                    (car (last path)))))
      path-s))

  (defun mm/put-command-in-async-buff-name (f &rest args)
    (let* ((path-s (if default-directory (path-slug default-directory) ""))
           (command (car args))
           (buffname (concat path-s " " command))
           (shell-command-buffer-name-async
            (format
             "*async-shell-command %s*"
             (string-trim
              (substring buffname 0 (min (length buffname) 50))))))
      (apply f args)))

  (advice-add 'shell-command :around #'mm/put-command-in-async-buff-name)

  (add-hook 'comint-mode-hook
            (defun mm/do-hack-dir-locals (&rest _)
              (hack-dir-local-variables-non-file-buffer)))

  ;; it logs a warning when you hack a local
  ;; Making process-environment buffer-local while locally let-bound!
  ;; It is sort of want I want though
  (advice-add #'start-process-shell-command :before #'mm/do-hack-dir-locals)

  (advice-add 'compile :filter-args
              (defun mm/always-use-comint-for-compile (args) `(,(car args) t)))

shell

(defun mm/with-current-window-buffer (f &rest args)
  (with-current-buffer
      (window-buffer (car (window-list)))
    (apply f args)))

(defun mm/shell-via-async-shell-command ()
  (let ((display-buffer-alist
         '((".*" display-buffer-same-window))))
    (async-shell-command shell-file-name)))

(advice-add #'mm/shell-via-async-shell-command :around #'mm/with-current-window-buffer)

(setf shell-kill-buffer-on-exit t)

(add-hook
 'shell-mode-hook
 (defun mm/shell-dont-mess-with-scroll-conservatively ()
   (setq-local scroll-conservatively 0)))

I invoke mm/shell-via-async-shell-command with a stumpwm keybinding. This is the only reason I need mm/with-current-window-buffer irrc.

See also

Date: 2023-06-20 Tue 20:27

About
Contact
]]>
https://faster-than-light-memes.xyz/dir-env-process-environment.html dir-env-process-environment 2022-10-17T23:59:59+00:00 dir-env-process-environment

ftlm

dir-env-process-environment

Introduction

Making shell commands is one of the great joys of emacs.

An advanced use case is that you want to set up a process environment for everything you do inside some project.

What I show here works for compile, shell-command, and even cider connections.

The setup

.dir-locals.el:

((nil
  .
  ((eval progn
         (make-local-variable 'process-environment)
         (setq process-environment
               (copy-sequence process-environment))
         (setenv "FOO" "hurr")))))

Yes, it is a bit convoluted but for a reason. We need to copy the list, else if somebody modifies the cdr locally, that would modify the global value. (In the case that FOO is an element in the list somewhere already).

The answer mentions setq-local. I tried that without success.

Shell-command and friends will inherit the process environmen. It pretty much just works.

From the project I currently work on:

((nil
  .
  ((eval progn
         (make-local-variable 'process-environment)
         (setq process-environment
               (copy-sequence process-environment))
         (setenv "AWS_PROFILE" "datalake-deployer")
         (setq-local cider-clojure-cli-aliases (concat cider-clojure-cli-aliases ":dev"))))))

Now I start cider connections with AWS_PROFILE set correctly. Also I have a babashka task for deploying that needs this env and it all just works.

Non-file-buffer

By default, .dir-locals.el does not apply to buffers without file names. (When the local variable buffer-file-name is not set).

There is a function hack-dir-local-variables-non-file-buffer for this purpose. This is called by dired, also magit.

I usually start processes from either a (source) file buffer or dired.

revert with grace

When you make a change to your .dir-locals, call revert-buffer in whatever buffer you want to have the effect. It hacks the local variables again for the buffer.

Date: 2022-10-17 Mon 09:56

About
Contact
]]>
https://faster-than-light-memes.xyz/pearls-of-introspection.html pearls-of-introspection 2022-11-12T23:59:59+00:00 pearls-of-introspection

ftlm

pearls-of-introspection

Recently, I watched BugsWriter falling in love with elisp 1 - he said about describe-mode

Why did nobody ever tell me about this?

This post is called Pearls of introspection.

All of Emacs is one bright pearl.

The describes

describe-mode

Lists all current keybinds in a buffer. Huge output, sometimes what you want.

describe-char

Emacs is a text editor, text is made up of chars. describe-char is useful if you want to know what face is rendering. It goes beyond that by showing string properties, overlays, and buttons. This can be useful for debugging.

Story: Once I deleted some packages on my system, then I go into emacs info and some of the text is not rendered right.

describe-char gave me the hint, the fixed-pitch face wanted "Monospace Serif" or some such but I only had Monospace.

So I did this and was happy ever after:

(set-face-attribute 'fixed-pitch nil  :family "Monospace")
(set-face-attribute 'fixed-pitch-serif nil  :family "Monospace")

Worth binding:

(define-key help-map (kbd "c") #'describe-char)

describe-key

"What does this key do?" - the answer is describe-key and hit the key.

describe-function

Emacs functions have amazing docstrings.

The help buffer has a keymap of its own, try ? to list keybinds.

Highlight: s to go to source.

describe-keymap

Probably faster than describe-mode, renders a keymap in a help buffer. Try describe-keymap -> help-map hehe.

prefix-help-command

You type ? after starting to type a prefix, then you get the binds.

embark-bindings

An alternative to the default ?, it is amazing. I get a vertico completing-read of the binds.

M-x describe-

There are more, some of them I never used. Emacs is vast like the ocean.

view-lossage

You accidentally hit some key, and something unexpected happens. view-lossage is your friend. It shows the last keys + commands.

It is like the morning-after pill version of describe-key.

story: I sometimes accidentally hit y before typing the text for an avy command.

Every time I was like "Holy cow whatever I hit there is powerful". 2

I didn't know about view-lossage. The thing is, it would not have helped, because avy is reading the key, then dispatching on the key. I guess it needs to go through the keymap system or something to show up in view-lossage.

apropos-value

This helps me about once per year.

It is extremely satisfying when you use this and it helps you. I once figured out about revert-buffer-function by using it.

You can revert shell-command buffers to rerun the command but I did not know by which principle.

apropos-value, then typing my shell command showed me there was a buffer-local variable called revert-buffer-function. It worked because Elisp closure objects print with their environment. In other words, the value of revert-buffer-function was a lambda that printed with my shell command. It is quite wild.

find-library

Read code, and do little experiments. I am not sure but I think there might be something profound about Elisp and emacs. I think the language is the program and the program is the language or something. Knowing the program is to know the language and knowing the language is to know the program. (Insert link to top Elisp functions blog post here).

where-is

I just discovered this via describe-keymap into help-map lol. This is like describe-key but you say the command and it says the keybinding.

Footnotes:

1

I thought lisp was going to be complicated but it is simple.

That is an interesting tension between outside perception and the actual language. Since you write the AST directly, you can say what you mean. The power of lisp is that the code is made of

  1. Ideas
  2. There is nothing else.
2

It makes the cursor stay and yank the target word. Hit ? before typing the keys for avy. The way it works is that avy can dispatch with different actions, the default is to jump to the location, but y for example yanks a word and makes your cursor stay where you are. It is really powerful. Ace-window has the same paradigm.

Date: 2022-11-12 Sat 16:02

About
Contact
]]>
https://faster-than-light-memes.xyz/emacs-clojure-lispy-mind-meld.html emacs-clojure-lispy-mind-meld 2023-04-24T23:59:59+00:00 emacs-clojure-lispy-mind-meld

ftlm

emacs-clojure-lispy-mind-meld

Table of Contents

My first screencast of programming Clojure.

Website

Have you ever thought, I just want to flip a coin? You might have landed on a few bloated websites filled with commercial bullshit.

I thought this is a good chance to make a video while I program something.

Lispy

Thanks to Mr. Oleh Krehel aka abo-abo. This package is truly magnificent and a big part of my Emacs feel and power.

Keep in mind that Lispy has a steep learning curve. Comparable to vim-bindings coming from non-modal editing. The payoff is a world of editing s-exp at the speed of thought. It is raw power.

Being able to modify lisp, interleaved with evaling it, is a profound and mind-expanding experience. It is the real deal of feeling and knowing the competence of the computer.

  • lispy
  • see Meow for my config, I have interleaved the two.

Meow

After using Evil for years, the desire in me grew to use something more bare bones. What pushed me over the edge was realizing that evil-search was a second implementation of what isearch, a truly magnificent library, already covers.

I think Evil is great and maybe also the place to start, because there is a lot of lore for Vim beginners, intermediates and masters.

Meow shares with Vim modal editing. It too is uncompromisingly built for power users.

Meow is like a pair of skates that allow you to ride the Emacs builtins.

This design is reified in the keypad concept, which allows you to run default Emacs bindings. Using the leader-key paradigm, which we got from Spacemacs and indisputably a great idea.

github

My init-meow.el, polished and being polished to my taste and needs.

Stumpwm

Lisp (and Smalltalk, I suppose) are a profound discovery of humanity. In a way, I believe the power of Emacs is just the power of Lisp, molded into a tool for thinking and computer usage.

The first time started Stumpwm and went through the Getting Started, I immediately had the feeling in my bones this is my wm.

Basically in the second paragraph, they say how to open Emacs :).

I REPL into Stumpwm with sly and build a few of my own window management commands.

Like emacs, maximal in its capability and minimal in its presentation. It lets me get everything else out of the way so I can focus on emacs.

Defining commands that optionally call through to Emacs gives me a new level of fluidity making the system feel like made of water and out of one whole. The only thing better than that is having EXWM, but this comes with its own tradeoffs. With Stumpwm I enjoy a truly solid and trustworthy x client implementation.

See also

Date: 2023-04-24 Mon 08:25

About
Contact
]]>
https://faster-than-light-memes.xyz/bobscheme-1.html Bobscheme 2023-07-05T23:59:59+00:00 Bobscheme

ftlm

Bobscheme

Sometimes you just want to implement a Lisp using JSON data on dotnet.

Github code

Mainstream programmers understand the value prop of JSON, the internet is built with it. JSON comes from Javascript being conceived as a Scheme, which got its dynamic data structures from IPL.

The important part of a language is the ideas, what is idiomatic, and what you can express. The kinds of thoughts you are thinking with it. The syntax is the least important part of a language, seriously. JSON allows me to express Lisp code, because I only need to say lists, and atoms - numbers, symbols.

I am implementing a SICP metacircular evaluator. There are functions, there are values, you can call functions on values and you get a value back. There is also reference type atom, which is more or less the one from Clojure.

I can ask ChatGpt to produce Bobscheme, I can read and modify Bobscheme ad-lib. After all, it is just JSON. We know how to read, modify and create JSON.

They, who understand Bobscheme are but a small step away from understanding Lisp and Clojure.

Foo Accumulator

This Bobscheme code defines a function called make-adder. This function returns a function object, that accumulates the sum of all the args it was called with. Paul Graham was describing this function as a small example code snippet to get a feel for the power of a language (2002).

[
  "define",
  "make-adder",
  [
    "lambda", ["n"],
    [
      "let",
      [
        "state", ["atom", "n"]
      ],
      [
        "lambda", ["i"],
        ["swap!", "state", "+", "i"]
      ]
    ]
  ]
]

Def a machine:

["define", "machine", ["make-adder", 0]]

Use the machine:

["machine", 10]

=> 10

["machine", 15]

=> 25

The Bobscheme code is almost the same as the Clojure code:

(defn make-adder [n]
  (let [state (atom n)]
    (fn [i]
      (swap! state + i))))

Because the ideas are the same, we get to call that dialects of Lisp.

The Bobscheme is just annoyingly bloated with commata and quotation marks, inheriting from its JSON notation heritage.

Bobscheme part 3 - Local Environment, Let foo be bar!

Skipping some episodes where I made lambda and macros work.

I was thinking about lying in bed and how let could work and it shows in how quickly I put the code I think.

This time I tried to cut out a functional chunk. Program. cs now has a function called IsLet and is now called EvalLet.

I chose the same default sequential semantic that Clojure uses for my let bindings. In Emacs Lisp and Common Lisp, this would be called "let*".

Clojure, Lisp, Json, Scheme, Interpreter, joyful, hacking, hacker, Emacs, fun with the computer, lisp evaluator, chat gpt

  Object EvalLet(JToken expr, ImmutableDictionary<String, Object> env)
{
    var letExpr = expr.Skip(1);
    var bindings = letExpr.First().ToObject<JArray>();
    var localEnv = env;

    for (int i = 0; i < bindings.Count(); i += 2)
    {
        var nameExpr = bindings[i];
        var valueExpr = bindings[i + 1];

        if (!IsSymbol(nameExpr))
       {
            throw new Exception("Expected symbol for binding name in let: " + nameExpr);
        }

        var name = nameExpr.ToObject<String>();
        var v = Eval(valueExpr, localEnv);
        localEnv = ExpandEnv(localEnv, name, v);

    }

    var bodyExpressions = letExpr.Skip(1);
    return EvalSequence(bodyExpressions, localEnv);
}

This bob scheme code

["let", ["bar", 10,
         "foo", ["+", "bar", 100]],
 "foo"]

says 110.

Part 2 - global env, variable lookup, little emacs things

I settle on a string atom representation.

This is the symbol foo:

"foo"

This is the string "foo":

"'foo"

So hello world in Bobscheme is:

"'Hello, world!"

Then I add a global environment, assignment and variable lookup, and at the end of the episode I can say:

["define", "foo", 10]

=> foo

"foo"

=> 10

In between I ramble about Lisp and Emacs things.

Keywords: Lisp, Scheme, Dotnet, C#, .Net, Csharp, JSON, Newtonsoft, LINQ, SICP, The structure and interpretation of computer programs, metacircular evaluator, own evaluator, own language, personal language, how to, emacs, screencast, programming, coding, showcase, showing off, Lisp explained, scheme explained, evaluator explained, emacs obarray, emacs variables, debug on variable change, trace-function, emacs theme, down to code, fun programming, global environment, variable lookup, immutable dictionary

Part 1 - dotnet new console, REPL

I make a program that reads JSON and then evaluates it using the meta-circular evaluator from the structure and interpretation of computer programs.

In this episode, I get to self-evaluating forms. So I can say "Hello, World!".

The cutest thing about Bobscheme is the prompts I use for the REPL.

string[] prompts = { "user> ", "what next?> ", "what is my purpose?> ", "say the next piece of the program> " };

They set the tone for joyful, toyful programming. Also doesn't this just clear up so much about the interaction of the programmer with the program?

Keywords: Lisp, Scheme, Dotnet, C#, .Net, Csharp, JSON, Newtonsoft, LINQ, SICP, The structure and interpretation of computer programs, metacircular evaluator, own evaluator, own language, personal language, how to, emacs, screencast, programming, coding, showcase, showing off, Lisp explained, scheme explained, evaluator explained

Date: 2023-07-05 Wed 08:38

About
Contact
]]>
https://faster-than-light-memes.xyz/the-joy-of-clojure.html the-joy-of-clojure 2023-01-26T23:59:59+00:00 the-joy-of-clojure

ftlm

the-joy-of-clojure

Introduction

We don't yet have a strong psychology and sociology of programming languages. One day we will, and we will be able to retroactively make sense of why Clojure is so powerful.

6usJE0L.jpg

Figure 1: The soul! A computer. Solar punk Clojure. The inside of a computer. Rubics cube colored gems are flying around. Lighting.

Paul Graham has suggested The Taste Test for understanding the power of a language. A programming language is a tool for thinking. Ultimately, its power comes from letting you think thoughts and express them. With sensibility (hypersensitivity), you can tell which language feels more restrictive than another.

If all languages would be the same, we would be programming assembly. But we are not, because we live in a world where languages have varying degrees of power.

Here I try to describe a few things that set Clojure apart, even from other Lisps.

What interactivity brings

With Lisp, you can quickly test each piece of code as you write it, ensuring that it is solid and functioning correctly before moving on to the next piece. This allows for a more streamlined development process and eliminates the need to keep in mind all of the potential issues that could arise with a piece of code. Additionally, the ability to quickly evaluate code and make small experiments allows for a more agile and flexible development process.

Here is a simple example:

(file-exists-p "~/.clojure/")

Are you getting the file path right? When I write Lisp, I know the answer while I am writing the program. I just eval whatever form I have to build the file path. Because of structural editing, I can wrap whatever form in (file-exists-p) quickly and experiment right then and there.

Doing little experiments is quick enough so it can mesh with your thinking. I have the freedom and agility to just do and move. We get the same power in the small during development and in the large while diagnosing a problem in production. 1

Philosophy of simplicity

Clojure first clicked for me via Rich Hickey talks. Keep things simple. Let data be data. Then you can just manipulate the essence of the stuff and be done. 2 , 3

He establishes the reasons for Clojure and the opinions on how to build things. And this permeates the culture.

The combination of data-orientedness and functional programming while making real-world pragmatic tradeoffs, lends itself to a complete4, robust, no-bullshit style.

Strong community

When I use a library I can go to the slack channel and usually the maintainers help me out. It happened to me more than once when I made a bug report, somebody fixed it within a few days. Everybody is excited about technology and wants to help you out. It ties in with the joy of Clojure as well. You get happy, friendly people. This is power. Humans are more effective when they are happy and friendly.

Not to mention Borkdude.

Non-breaking versioning

It is an explicit value (again from Rich Hickey's philosophy), to not make breaking changes. This is one of the things where once you have it, boy, you don't want to ever go back.

After acquiring this taste, getting broken because I bump a version makes me think Why are you doing this to me?.

The aesthetics of data orientedness

The core library is a huge lever because we lift everything that can be into the space of data structures, then manipulate those data structures.

Config input, REST API response, database queries, and responses. It is all the same kind of stuff once it is in our system.

I think of this as a lift and manipulate kind of lever. Unix has this with program input output text. Once you are in the realm of textual input and output you can leverage shell commands to manipulate. Emacs has the same thing with text in buffers. If you can express your domain as text in the buffer you can give it to the user for manipulation, and you can leverage the core library for manipulating text in buffers.

This data-orientedness is something I can expect from libraries and other people's code. The dominating amount of stuff will always be just data structures acting on pure functions.

Freedom and joy

The workflow is an utter joy, with the REPL at the center of it. I start a program once and it grows together with my ideas. There is something about a program that runs for days and you don't restart it that I cannot explain but it does something to your mind. The closest analogy is a kind of friendship with the program.

  • inline def some of your data, craft your code forms.
  • Functional programming (and The Value of Values)
    • able to fabricate data
    • able to get the time right with atomic successions
    • able to easily keep records of the past

I get a real kind of safety. The kind where I can sleep at night because I know my program is bug-free. You get this from there being few enough ideas in the program that you can hold the relevant pieces in your head.

Any program that is small enough to be self-evident is with a step function dramatically more likely to work bug-free. 5

It helps that everything is accessible from emacs which is the other ancient and powerful tool that feels like an extension of my mind.

Because of this focus, solidity and ease of development, I am more joyful and creative when programming with Clojure.

It allows me to focus on the actual problems. So I can spend time on getting the user experience right etc.

Summary

So here is a tool that lets me think the right thoughts and express them. It lets me move with ease where I want to be.

From Lisp, I get the interactivity and the tradition of FP and simplicity. When I want to make a small experiment with the code, I can just do a small experiment with the code.

Beyond that, there is a solid and dependable community, the data-orientedness and the leverage of targeting big platforms.

I can model information systems and can make the program just do what it needs to do, and do nothing more.

Clojure just delivers.

The silent competence of the computer.

(Marvin Minsky)

Note

I know there is a book of the same title. This is not accidental. The power of Clojure also lies in the joy it brings. An alternative title for this post could be Joy is power in analogy to Paul Grahams Succinctness is power.

Footnotes:

1

Bradford Cross has mentioned this and other things in a very convincing talk recently.

2

I believe I see hints of the same craftsmanship of getting shit done ideas in other places: Brian Will Object-Oriented Programmin Embarrassing, his counterexamples are very straightforward and complete. Chris Keaathley - SOLID code isn't flexible

3

Rich Hickeys greatest hits The start point. Get your mind expanded by simplicity and clear thinking.

4

Complete in the sense that you make a piece of code that gets the job done. Instead of scattering logic around the place.

5

Stuart Halloway has similar thoughts, Radical Simplicity. Other people like Dan Friedman also made this relatively explicit. I think at this point it is obvious that simplicity is crucial for building good software. The question is what is simplicity and how to do it?.

Date: 2023-01-26 Thu 09:48

About
Contact
]]>
https://faster-than-light-memes.xyz/conversation-1.html No Iterations 2023-07-07T23:59:59+00:00 No Iterations

ftlm

No Iterations

Lisp and Clojure enhance interactive, idea-driven programming.

One day we will boot up our computer and it asks What is my purpose?.

One magic piece of Lisp is the concept and idea of interactively building the program while it is running. The interaction layer between the program and programmer is implemented by a read, eval, print-loop, REPL. It doesn't matter so much what the REPL exactly is, what matters is the idea of an interactive interface as programmers to the running program. In the same way, we have an interactive interface to files on the computer; In Lisp, we have an interactive interface to the symbols, the methods, the fields - the stuff that your program is made out of.

Repl-driven development gives us a level of intimacy with the computer that has been described as a conversation with the program.

I cannot convey what music is in a way that you will know music. So too, I cannot convey what Lisp is about in a way that you will know Lisp. But I can try to give some clues and little hints.

There is something like music between a mind and a machine. REPL-driven development unlocks this world between programmer and program like nothing else does.

b-brain-2.png

Figure 1: A brain! banana, tomato, children block cubes ideas inside the brain. A computer with electricity at the speed of light in the universe on the other side

What is the purpose of programming languages?

If you think about programming languages you really need to think about what are they for?.1

language.jpg

A programming language is what is between you and the computer.

It is the interface between the ideas on one side and the stuff inside the computer on the other side.

Maybe programming languages are for many things but here is a list of some really important things:

  1. Expressing ideas
  2. Expressing ideas to the computer
  3. Make the computer think what I think
  4. Make the computer tell me what it thinks
  5. Change some entity in the computer (file, process, … )

The first idea is that making a big computer program is building a larger process out of smaller ones. I suppose you could truthfully say that sculptors make large shapes from stuck-together grains of clay. But that shows what's wrong with the bits-and-bytes approach. No sculptor ever thinks that way, nor scientist, or programmer. An architect first thinks of shapes and forms, then walls and floors, and only last about how those will be made.

Minsky (1984)

Data structures, language constructs, and at the very lowly bottom, syntax are the small things that are not what programming is about. The way that architecting is not about clay.

A language is there to allow us to modify, make experiments, and observe. Nothing more, nothing less.

Using this mental framework there is no fundamental difference between operating systems, programming languages, libraries, frameworks, user interfaces, and even text editors.2

These are all societies of process 3, some have collections of members with different emphases.

The hypothetical ideal language is completely out of the way. It would have infinitely small friction, infinitely small short steps and effort and wait time to make my ideas have effects on the computer. It would get rid of the clay entirely and we would raise and rise higher and beyond as architects riding the ideas.

We would be able to instantly and losslessly transfer thoughts to the computer and make the computer program change to our desire. We would use the competence of the computer seamlessly intermingled with the rest of our thoughts. Experiments would be thinking, with perfect reach, perfect control, and perfect access to the vast competencies, memories and remote connections of the computer realm. Racing at an unknown pace, building new kinds of towers of abstraction, thought, reason and creative spirit. Eventually hooked up to some constructors4, our thoughts would shape the stuff of the universe.

From this view, building ever more powerful languages is a path to a technological singularity.

Power is joy is getting shit done is having effects on the computer

The most powerful programming language is Lisp. If you don't know Lisp (or its variant, Scheme), you don't know what it means for a programming language to be powerful and elegant. Once you learn Lisp, you will see what is lacking in most other languages.

Stallman

Magic is what makes a great book great. It is something that happens in the mind of the reader, not elsewhere.

It has to do with masterful craftsmanship, with love, with the spark between people. It has to do with the right time and the right place, how could it be otherwise? A piece of magic comes together with the most masterful output of a masterful craftsperson. It needs to be the right time and place, else it would not be peak output.

We live in a world where we cannot measure magic yet. Otherwise, we could be churning out great movies, books and games.

You might not know it yet, but great software and great tools, too are subject to taste and aesthetics.5

Would it not make sense that the people talking about the most powerful language in sort of a mysterious way would allude to taste, too? If it is the most powerful language, it would be powerful because of reasons in the realm of mastery and craftsmanship. It would be because of magic, which is hard to explain and only accessible via aesthetics and taste.

What makes Emacs a superbly sick piece of software? What makes The Man From Earth a superbly sick movie?

The answer is you will know when you know. But you will not know before you know. And you will know for reasons that you have to discover yourself. But it won't be reasons that make no sense. It will be reasons that make perfect sense. Just that they are somewhere where you don't yet know how to look.

The only way to know magic for the moment is to hone our tastes and broaden our horizons.

What does it mean to develop a taste in programming? Stallman is giving a hint that trying Lisp will develop your taste.6

Let me throw in another clue; In order to know what different kinds of languages do, you should go and try the ones that are most different from what you already know.

Lisp is mentioned in this way again and again 7. It will broaden your horizon, it will stretch what you think about languages.

Can it be true? Does it not sound too good to be true? Why not? What in reality doesn't look like it is true? Is it not a fact that Emacs, a Lisp program, is a freaking psychedelic spaceship? Is it not a fact that society and mainstream are wrong about things all the time?

If Lisp is profoundly more powerful than other languages, then it will be powerful in a way that

  • Resonates with your programmer's heart.
  • You cannot know before you feel it.
  • Would be hard to explain, because it would be magic.

What is your most profound desire as a programmer? I think it is having effects on the computer.

What I am about to tell you is that there is an activity during programming that you sort of know but don't know. Current programming is stuck with a limitation that simply does not make sense.

Quick iterations are better

You want to make a quick experiment in the code. You make a little side project, and you set up some example data.

You tell your young programmers that they should value quick iterations. You go through hoops to not have to re-compile right now. And not again. And you juggle in the right amount of changes in your batch so that afterward you see point 2/5 not working, instead of point 22/100.

You suffer when you have long deployment cycles in your feedback loop.

Sometimes you wish you had put a log or breakpoint somewhere because it just happened.

What if you have a complicated thing happing somewhere on the server? What if the program is right now in a state that only if you could look at it, you would know what to fix?

Sometimes it would be great to quickly see the counterfactual quickly. What if this if goes into the other branch, does it still work? No, fuck it commit, push and tomorrow there is a bug report that x broke.

If you are faster with seeing what the program is doing, that is better.

This you know. And maybe you know from notebooks or some advanced hot reload tech what it would mean to be able to change the program while it is running.

Restart the program and the state is dropped on the floor. But if you see what it does, while it does it, that is a difference between night and day. 8

Because it matters what is between you and the program.

In the 70s, there were the MIT AI people, John McCarthy, Marvin Minsky, and the young bright-eyed hackers who later build the internet and influenced what computing is nowadays. Supported by J.S.R Licklider, who was the first person having fun in front of a computer terminal at night.9

These people had a vision of what computing can be and they knew it will be interactive.

Batch processing

In batch processing, you give a batch of punchcards to a dude, who puts them into a computer, and you get your result the next day or whatever.

If you accidentally miss a hole or shuffle some cards, too bad. Dealing with the complexities of punchcards is not what programming is about! Not at all, not even close.

It is just extra steps and extra complexity in your interface to the computer.10 It is a bigger chasm, a bigger wall, a bigger world of pain. You want your ideas to affect the computer and then there is extra stuff. This stuff should not be there.

Getting rid of the punchcards and finding a better way was a tremendous step forward in computer programming.

Extra work between ideas and computers are not good.

It is easy to accidentally enjoy the puzzle11 that extra steps might provide. Don't do it. Don't enjoy it. It's darkness. Don't enjoy the extra steps between you and the computer, it is not programming.

You should be annoyed at all the things you do that are not programming. How else could we have moved away from punchcards?

Another similar mistake is to think that pain means hard work and that it makes you a serious worker. Don't do it. Don't enjoy the wrong pain for the right reasons. Don't think that being playful is opposed to getting shit done. Otherwise, you remind me of what school is doing to the never-ending curiosity of young children.

We call brilliant programmers lazy because they are honing their tastes, to feel what is unnecessary pain. Pain is part of your programmer's intuition, your taste, your laziness.

Hence, the hackers and forward-looking people of their age fixed batch processing.

Interactive computing

Imagine you are programming, you stamp out a batch, give it to a dude and on the next day you get the error message forgot a comma.

It took brilliant people to see that this is not right. That there is a better way. That fast feedback even has any value at all.

Time-sharing was an idea for getting rid of Batch processing.

Interactive computing is in general the idea that you get instant feedback from the computer about what you do. For instance, you sit at a terminal and you type things. And the computer then answers - prints - some output. Personal computers inherited interactive programming from the visionaries of early computing.12

Minsky mentions in one of his lectures:

We tried to tell the people at IBM about time sharing and they said why don't the programmers just think?.

Meaning why don't they spend the extra effort to just code bug-free the first time?

They were so wrong. So utterly, completely, badly, painfully, laughably wrong. Make songs about it, and edge it on the doors of buildings. Remember it forever and don't make the same mistakes again. Burn it in your souls for it is a wound. It held humanity back. It was a mistake.

If we would be a bit more forward-looking and open-minded - how many times over would we be exploring the stars by now?

They thought interactive programming won't do anything. They thought the people that advocated it where just lazy and wanted to play, instead of doing serious things with the computer. And they argued in silly ways like how can the computer think if it is interrupted all the time? - A matter of implementation that was figured out.

hackers-movie-poster.png

Figure 2: bright-eyed hacker scientists Minsky, McCarthy, Licklider. joyful playful magic computers defeating the evil establishment. epic heist movie poster.

What does interactive computing have that batch processing does not have?

It is having a connection and an interface to the stuff inside the computer in a way that leverages how we evolved to manipulate and walk around in the world. It fits the human psyche. Its history stems from the urge to reach and explore inside the computer. And its effect is being able to reach into a new world. It was obvious to the brilliant visionaries of the time and it is obvious to us now, when we navigate and modify the stuff inside the computer.

To be clear, this is about what you feel when interacting with your operating system to create, move and modify files on the system for instance. When you open a browser and click things, this is interactive computing.

This is not a small thing - This is a different world we have. Interactive computing is arguably one of the most important technologies ever. And the big companies would never come up with this. Would they even have the incentive?

It is like having a spaceship where before you had hot air balloons. It is like the sun where before you had candles. It is something in the space between mind and machine, the ability to reach.

Next: Why does Lisp make the same jump for programming a program?

One of the best ideas in computer science

And maybe one of the best ideas ever, Von Neumann architecture.

Put the program into the computer memory.

A stroke of genius. Now we have a magic stone that can be loaded up with ideas and then it can be anything.

Before we had 1 machine with 1 program. What a program is and what a machine is was glommed together. Good design is about splitting things apart, and this was a beautiful and elegant design.

This in a way resonates with what Dennet calls Popperian and Darwinian creatures.13

First, you have a machine built to behave (Darwinian); A machine with a fixed program.

Then you have a machine that is built to learn (Skinnerian).

And finally (a Von Neuman machine), a machine that is built to deal with ideas (Popperian).

Because ideas, the memes, can be anything, including ideas about ideas, the Popperian machine has an open-ended future.14

Putting more ideas into memory was a very good idea.

The story of dynamic memory data continues with IPL, which was used for one of the most important early AI works General Problem Solver (1957).

In IPL, Information Processing Language, you had data dynamic in the program, in lists (made from cells). You can create lists and make it point to something else during the program etc. different from having to declare variables for everything.

You know of course what this means because you create lists and arrays all the time in your program.

Javascript is sort of a Scheme, so it has a function called print and a function called read (ok I guess they have glommed it together with eval).

Douglas Crockford discovered JSON by printing and reading javascript objects.4

If you know the power of JSON, you know the power of having dynamic data in your program. Your program is not about customers and payments, it is allowed to be a level higher. Your program is allowed to be about data structures. Data abstraction is something like SQL or dotnet LINQ, or a JSON parser. It is a language for talking about data and data transformations. map, filter, reduce don't know about customer names, they know about lists/sequences/streams, or so and so.

This is the heritage of Von Neumanns memory idea propagated forward through time and one level up. Your program is loaded dynamically into the computer, but your program is also dynamically loading data into its memory. Your program is, in some way, simulating a van Neuman machine itself.

McCarthy liked IPL but did not like how it was complected with implementation details of the computer it ran on.15 He had the idea of taking those lists and describing the computer process entirely in terms of it.

Let's take those lists, make the first thing the verb and implement a lambda calculus.

To understand Lisp, imagine you read JSON and treat the data as a command language for what your program does next.

function read(expression) ; returns jsonData
function eval(jsonData) ; returns an evaluated object

Eval is the big meaty function of your program. It needs to take that JSON, go through it and interpret the data as commands.

function print(object) ; print to the user
function repl() {
      while (true) {
         var whatTheUserSaid = readFromTerminal();
         var jsonData = read(whatTheUserSaid);
         var object = eval(jsonData);
         print(object);
      }
  }

This repl function can be the entry point and the game loop of our program. Now the user can sit at the console and interact with our language.

We define a few special operators. During eval, some stuff we have to implement in the static part of the program.

  • set - set a variable.
  • pair - make a pair, a pair of pairs then is a list.
  • first - first value in a pair
  • rest - the second value of a pair. (this can to a simple value or another pair).
  • lambda - make a function.
  • if - conditional evaluation.
  • quote - return the jsonData instead of evaling

This is a program that is simulating interactive computing. Where an operating system gives you access to files and processes. A Lisp gives you access to a society of symbols, functions, values, and data structures.

var globalEnv = {}; ; a dict, hash map, lookup table
function define(symbol,value) ; augment the global env

Make our Lisp interpreter know about the command define.

function eval(expression) {
    if (expression.first == "define") {
        var symbol = expression.second;
        var value = expression.third;
        define(symbol,value);
    }
    // ...
}

Lisp:

["define", "foo", 10]

(This is valid Bobscheme)

This is the cleaning up that McCarthy did to computer programming. Lists where the verb is that the front, no extra stuff. Put it into memory so you can modify it while it grows, Make a symbol type so you can give names to things, from inside the language. Make a function type so that functions are the same stuff as the rest of the program.

The fact that JSON is so useful comes directly passed down from IPL. All the objects nest, there are only a few kinds of objects and bottoms out at atoms. A printer and a reader come shipped by default. I know without knowing that Javascript gurus know the power of print, compared to the little machines you get from the C++ line of heritage.

McCarthy wanted to sit on a terminal and make the program do different things while he is programming it. He did not want to say up front all the variables in the system because he wanted to have something more dynamic for programming AIs.

The interactivity is at the bottom. - Hence it is called a building material.

You make a program that asks you what should be next in the program?. It is as simple as this. From this, all the goodness that makes Lisp elegant and beautiful comes. The simplicity of this enables easy-to-reason-about programs that write programs.16.

With this, we can have a conversation with the program. We can directly interact, while the program is running.

Conclusion

The power of Lisp is not quick iterations, but no iterations. Instead of iterating, in Lisp and Smalltalk we do conversation. This concept is reified in the read eval print loop.

It is enabled by the idea of putting the program into the dynamic memory of your program. This resonates with the Von Neuman architecture idea of putting the program into the memory of the computer.

Jack Rusher's bottom line is don't settle for anything with less interactivity than Lisp and Smalltalk!

Once you have in your fingers how to leverage the repl, it will be as obvious as the sky, vast and inescapable. This is better, this is simpler, and this gets stuff out of the way. This lets me use my brain the way a monkey reaches for a banana. Directly, the way I can delete and create files on my PC. I can define and undefine symbols in the program. I can make quick experiments while the program is running, without the mental distress of setting up the state.

It is the same difference that interactive computing does compared to batch processing. It is not a small step, it is not a big step, it is not a step at all. It is a different world.

Whatever that is. At this date and time, I will call it Magic.

See also

GPT-4 can produce some poetic stuff with this post as base

Imagine a painter, fervently engaged in a dialogue with her canvas. Each stroke isn't a hurried message jotted down and sent off, anxiously awaiting feedback. No, the painter watches each trail of color blend and mix under her brush, immediately seeing the effects of her choices. She tries different pressure, strokes, colors, and brushes, in a cycle of continuous creation and observation, a dance between her mind's eye and the canvas. This is her conversation with her art - immediate, beautiful, and ever-changing.

Now, consider a programmer, in front of his screen. The code portrayed is not a fixed monolith of logic. Instead, it morphs, breathes, and reacts under his fingertips, mirroring that painter's canvas. The programmer writes a line of code, and the effects echo through the system immediately, no abstraction barrier held in between. The immediate feedback enables him to stir into different directions, tease out underrepresented nuances, debug on the fly and explore new vistas of his creation.

The code is fluid under the hands of the programmer in a perpetual dance of creation, modification, and observation. It is an intimate conversation with the ideational fabric that weaves itself into existence as the program - the programmers' thought reflections observed immediately, altered rapidly, and understood fully.

This, my friend, is the spirit of interactive programming. A live, pulsating creation constantly in flux rather than fixed scrolls etched into digital stone, navigating creativity at the speed of thought inspired by immediate feedback, riding the great prairies of abstraction and thought, reaching a level of intimacy and understanding with your program that feels like a piece of music resonating with your heartstrings.

Footnotes:

2

One benefit of this view is now I am holding all pieces of language, and interfaces, to the same high standard.

React is not a framework, react is a language. Can I express myself using it?

5

This is one of these things that I think are instantly obvious to 99% of women. And the man-dominated software industry is mentioning it very little, to the point where you have to sort of convince the people that it is the case.

6

Paul Graham The Taste Test. Exactly this concept: Succinctness Is Power

7

Paul Graham, Succinctness Is Power, Beating The Averages.

LISP is worth learning for a different reason — the profound enlightenment experience you will have when you finally get it. That experience will make you a better programmer for the rest of your days […]

Eric Raymond

8

It is indispensable for an artist to see what they are doing, while they are doing it.

https://www.youtube.com/watch?v=PUv66718DII

9

The Dream Machine: J.C.R. Licklider and the Revolution That Made Computing Personal.

I cannot recommend this book enough. It is such an amazing ride through the history of computers.

10

See "Stop Writing Dead Programs" by Jack Rusher (Strange Loop 2022) this talk is such a beautiful rollercoaster ride through ideas of interactive programming. Very joyful, very quick-witted.

11

I hypothesize - another facet of why fewer women get into programming.

Because dudes enjoy the needles pain of random puzzles and small IQ tests. They litter programming and source code with it. They even accidentally think that this is what programming is about.

But it is not. It is about ideas. The way architecture is about buildings; Not the bricks.

Maybe women intuitively get when they are thinking about bricks when they wanted to think about a building. But when you just start with programming, how could you hope to have a mental framework to discern this? So you might think programming is a weird math-like, low-level activity that causes you pain.

Or you have the luck to start with Clojure. Then you will forever think what is all this extra guck I need to do? in other languages.

This is just one of the many things that are off about mainstream programming and the software industry currently.

12
  • https://en.wikipedia.org/wiki/History_of_Unix
  • Interactive programming and how Lick was central to pushing forward the right technology is a major topic in The Dream Machine: J.C.R. Licklider and the Revolution That Made Computing Personal.
14

See for instance Dennett 2017.

Or accessible, some talks:

15

See Marvin Minsky - The beauty of the Lisp language

The deeper trails are

16

See Paul Graham Beating The Averages.

CORRECTION In an earlier version of the post I simplified too much and just said

macros, which you don't have in other languages. This was not correct. But I do have the opinion that Common Lisp-style macros are uniquely expressive and natural to reason about.

Other languages have macros

Date: 2023-07-07 Fri 09:04

About
Contact
]]>
https://faster-than-light-memes.xyz/binaural-beats-using-scittle.html binaural-beats-using-scittle 2022-09-19T23:59:59+00:00 binaural-beats-using-scittle

ftlm

binaural-beats-using-scittle

Introduction

The best thing about being a rationalist is figuring out you are wrong about something.

I was thinking that binaural beats belong together with astral travel, frankly, just down the toilet. Turns out that not only was I wrong, but binaural beats also have cool neuroscience and pose a fun engineering problem on top of that!

Here is the current result of my journeys up on gh-pages.

What are binaural beats?

Wikipedia

A binaural beat is an auditory illusion

For example, if a 530 Hz pure tone is presented to a subject's right ear, while a 520 Hz pure tone is presented to the subject's left ear, the listener will perceive the illusion of a third tone. The third sound is called a binaural beat, and in this example would have a perceived pitch correlating to a frequency of 10 Hz, that being the difference between the 530 Hz and 520 Hz pure tones presented to each ear.

Science

There has developed a growing science about improvements in memory and attention tasks when listening to binaural beats.

BTW the frequency and the time of the exposure matter (i.e. before and during the task is best).

Oh yes, I certainly want more of that memory and attention.

eHaaoYF.png

Figure 1: A brain with headphones flying at the speed of light through space. Lightning on the brain. There is a sine wave in the background. Stylized, Colorful, Cinematic, Digital Art.

Frequency

Theta waves (5Hz) actually decrease memory task performance. So I really do not want to listen to those. I heard Andrew Huberman saying that 40Hz is the most powerful.

If you know about brain waves, you might remember 40Hz as the legendarily intriguing gamma wave.

From wikipedia article:

Gamma rhythms are correlated with large scale brain network activity and cognitive phenomena such as working memory, attention, and perceptual grouping, and can be increased in amplitude via meditation or neurostimulation.

We are really talking about cool stuff here, attention, working memory, and memory processes. Also, the Binding problem.

The neuroscience of the auditory system

Hearing is cool because the neuronal neural information first goes into the brainstem and then goes up some nuclei up to the thalamus. And eventually to the temporal lobes of the cortex (the 2 arm things on the side of the brain).

This is different from the vision system where the info goes directly to the thalamus.

Quoting this paper:

Presenting two tones with a slight frequency mismatch to each ear separately creates a perception of a third tone, a binaural beat, that oscillates at the absolute difference between the tones (Oster, 1973; Moore, 2012). These beats are thought to originate subcortically in the medial nucleus of the superior olivary complex, the first nucleus in the auditory pathway to receive bilateral input (Wernick and Starr, 1968; Kuwada et al., 1979). This “illusory” third tone is lateralized between the left and right ear of the listener, making binaural beats useful for spatial sound research (Ross et al., 2014).

from the Wikipedia articles on Sound localization

The auditory system uses several cues for sound source localization, including time difference and level difference (or intensity difference) between the ears, and spectral information.

and Superior olivary complex

The superior olivary nucleus plays a number of roles in hearing. The medial superior olive (MSO) is a specialized nucleus that is believed to measure the time difference of arrival of sounds between the ears (the interaural time difference or ITD). The ITD is a major cue for determining the azimuth of sounds, i.e., localizing them on the azimuthal plane – their degree to the left or the right.

So there is sort of a clump of neurons in the brainstem that are specialized to integrate the timing information between the left and the right ear. I'm guessing those then start firing in the 40Hz frequency and that is interpreted as a third sound higher up.

I think this is ultra cool

You throw some energy at the brain in the right form (in this case some sound waves) and it reacts in some special way. If you put it like that, it is not surprising that some things eventually, end up doing something.

Now that the phenomenon has a name we can talk about it, similar to ASMR.

Thinking about how this was always part of how the brain works. Makes me wonder if some music composers and performers implicitly hit on this earlier, or not. I tried searching the web for

binaural beats in music

but I really get the same results mix as when I search for binaural beats.

Give me those binaural beats

I dint feel like opening a youtube video just to listen to some sounds. As an aspiring hacker and engineer, I just want to make my computer make some sounds that I want to listen to. So I decided to make a simple website that just makes binaural beats and nothing else.

The problem statement:

Make a sound on 1 ear (e.g. left headphone speaker)

Make a second sound on the other ear, with a +40Hz frequency.

End result

beats.cljs

Behold 46 lines of just get the shit done.

(ns beats)

(def binaural-beat-freq 40)
(defonce ctx (or (js/window.AudioContext.) (js/window.webkitAudioContext.)))
(def slider (js/document.getElementById "frequencyRange"))
(def display (js/document.getElementById "frequencyDisplay"))

(defn ->panner [left?]
  (let [panner (. ctx createStereoPanner)]
    (set! (.. panner -pan -value) (if left? -1 1))
    (.connect panner ctx.destination)
    panner))

(def panners
  (delay {:left (->panner true) :right (->panner false)}))

(defn update-display! [value]
  (set! (.-innerHTML display)
        (str "Base frequency: " value " Hz")))

(def get-oscillator
  (memoize
   (fn [panner]
     (let [o (ctx.createOscillator)]
       (set! (.- o type) "sine")
       (. o start)
       (. o (connect panner))
       o))))

(defn oscillate [panner hz]
  (let [o (get-oscillator panner)
        _ (set! (.. o -frequency -value) hz)])
  hz)

(defn update-app [frequency-value]
  (let [frequency-value (/ frequency-value 1000.0)]
    (-> @panners :right (oscillate (+ frequency-value binaural-beat-freq)))
    (-> @panners :left (oscillate frequency-value))
    (update-display! frequency-value)))

(defn start-app []
  (update-app (* 1000 152.74))
  (set! (.-value slider) (* 1000 152.74)))

(set! (.-update_app js/window) update-app)
(set! (.-start_app js/window) start-app)

index.html

  <!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Just 40Hz binaural beats</title>
    <script>var SCITTLE_NREPL_WEBSOCKET_PORT = 1340;</script>
    <script src="https://cdn.jsdelivr.net/npm/scittle@0.3.10/dist/scittle.js" type="application/javascript"></script>
    <!-- <script src="https://cdn.jsdelivr.net/npm/scittle@0.3.10/dist/scittle.nrepl.js" type="application/javascript"></script> -->
    <script type="application/x-scittle" src="beats.cljs"></script>

<style>
  body {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    gap: 1rem;
    margin: 0;
  }
</style>

  </head>
  <body>
    <h1>Just 40Hz binaural beats</h1>
    <audio></audio>
    <button onclick="start_app()"> play </button>
    <div class="slidecontainer">
      <input type="range" min="100000" max="450000" value="15274"
      class="slider" id="frequencyRange" onchange="update_app(this.value)"> </div>
    <div id="frequencyDisplay"> </div>
    <a href="https://github.com/benjamin-asdf/just-40hz-binaural-beats">code</a>
  </body>
</html>

scittle

I set up a beautiful hello world and connect with nrepl and get greeted with a friendly Isn't cool? this :).

It took me a moment to figure out I should open my index.html with a browser. Then do the cider-connect-cljs

1 ear, 1 sound

First I think let's start making a sound in 1 ear.

I am thinking a frequency that I can hear would be nice so I search the web for human auditory frequencies

1 kHz sounds like a good first thing to try. I am figuring something inside the speech area should sound natural (turned out that was a naive assumption).

The first playground

I have a file called playground.clj (Later I figured out I should rename to .cljs :) ).

Now getting heavily inspired by this code. Going 1 form by 1 like a usual lisp interaction dev experience, lulling it up:

(def ctx (js/window.AudioContext.))
(def ctx js/window.webkitAudioContext)

(def panner (ctx.createStereoPanner))
(set! (.. panner -pan -value) -1)
(.. panner (connect ctx.destination))

(def oscillators (atom []))
(let [o (ctx.createOscillator)
      _ (set! (.- o type) "sine")
      _ (set! (.. o -frequency -value) 1000)]
  (. o start)
  (. o (connect panner))
  (swap! oscillators conj o))

I connect my headphones via Bluetooth (a small feat on Linux, maybe another blog post), and lo and behold I hear a tone in my left ear. It is a really obnoxious sound, after a few seconds, it is so painful that I move the headphone to the side.

Playing Hz, first round

Here is a more endurable sound:

(set! (.. (@oscillators 0) -frequency -value) 200)

Playing around live at the REPL with sound is fun!

Now I am thinking, let's pick some music frequency, those are supposed to be crafted over hundreds of years to be beautiful frequencies, right?

Checking out a website. I pick D#3 / Eb3. Whatever that is.

(def a-note-freq 152.74)
(set! (.. (@oscillators 0) -frequency -value) a-note-freq)

binaural!

Let's do the thing where I add +40Hz and play that on the other ear. First I start generalizing how I make a panner:

(defn ->panner [ctx left?]
  (let
      [panner (. ctx createStereoPanner)
       _ (set! (.. panner -pan -value) (if left? -1 1))
       _ (.. panner (connect ctx.destination))]
    panner))

For some reason running this I get:

Failed to execute 'connect' on 'AudioNode': Overload resolution failed.

I went and asked in the nbb clojurians slack. I am sure one day we will figure out this error, in the meantime…

just put the second sound in the other ear, whatever

(ns beats)

(def a-note-freq 152.74)
(def binaural-beat-freq 40)
(def oscillators (atom []))
(defonce ctx (js/window.AudioContext.))

(def panners
  {:left
   (let [left? true]
     (let [panner (. ctx createStereoPanner)]
       (set! (.. panner -pan -value) (if left? -1 1))
       (.connect panner ctx.destination)
       panner))
   :right
   (let [left? nil]
     (let [panner (. ctx createStereoPanner)]
       (set! (.. panner -pan -value) (if left? -1 1))
       (.connect panner ctx.destination)
       panner))})

(defn oscillate [panner hz]
  (let [o (ctx.createOscillator)
        _ (set! (.- o type) "sine")
        _ (set! (.. o -frequency -value) hz)]
    (. o start)
    (. o (connect panner))
    (swap! oscillators conj o)))

(-> panners :right (oscillate (+ a-note-freq binaural-beat-freq)))
(-> panners :left (oscillate a-note-freq))

lol, it works.

Intermediate results

Artifacts

Every now and then there are artifacts in the sound with my headphones setup. Some are just rustling and crackling.

Others sound like some kind of metal being stretched far, far away, underwater, or in slow motion. It reminds me a bit of the Dungeon Keeper soundtrack. Dark and eerie, sort of slow mow.

Binaural beat?

Pretty sure I get the third-tone illusion. I would describe it as some kind of background or in between "airy" sound. It is supposed to seem to come from the middle of the brain. It is as if the fabric of the sound is richer. Like there is sound in more places.

Change the base frequency

It occurred to me, that if I get the 40Hz sound illusion, then I would predict that I can change the base frequency, keeping the 40Hz difference between left and right ear invariant. I should then constantly perceive a 3rd tone which is the binaural beat.

I really need a slider for frequencies in my life.

I did not do much web dev in my life and making a slider was a first.

I managed to put something together, drawing inspiration from w3 schools for the Html, scittle for how to export, and stackoverflow for how to add a function to "onchange".

nice dev experience, scittle really

  1. I did not need to restart my REPL at any point, adding those Html elements, then reloading my index.html - it handles that.
  2. I get auto completions with cider. E.g. js/.. does something.

    BTW here is a tip, for sci projects (babashka, nbb, scittle). Currently, you need to evaluate a namespace form first. Then you can get completions. At least with cider.

M8rSuZ3.png

Figure 2: A mouse with headphones typing on a laptop. Intelligent. Sparks of magic are flying around. There is lightning on the mouse and laptop. Computer programming source code. Colorful, playful.

The word scittle makes me think of a small mammal in the habitus of a mouse. I imagine it scurrying and curiously sniffing around.

code:

index.html

<!DOCTYPE html>
<html>
  <head>
    <script>var SCITTLE_NREPL_WEBSOCKET_PORT = 1340;</script>
    <script src="https://cdn.jsdelivr.net/npm/scittle@0.3.10/dist/scittle.js" type="application/javascript"></script>
    <script src="https://cdn.jsdelivr.net/npm/scittle@0.3.10/dist/scittle.nrepl.js" type="application/javascript"></script>
    <script type="application/x-scittle" src="beats.cljs"></script>
  </head>
  <body>
    <div class="slidecontainer">
      <input type="range" min="120000" max="250000" value="15274"
      class="slider" id="frequencyRange" onchange="update_app(this.value)"> </div>
    <div id="frequencyDisplay"> </div>
  </body>
</html>

beats.cljs

(ns beats)

(def binaural-beat-freq 40)
(defonce ctx (js/window.AudioContext.))
(def slider (js/document.getElementById "frequencyRange"))
(def display (js/document.getElementById "frequencyDisplay"))

(def panners
  {:left
   (let [left? true]
     (let [panner (. ctx createStereoPanner)]
       (set! (.. panner -pan -value) (if left? -1 1))
       (.connect panner ctx.destination)
       panner))
   :right
   (let [left? nil]
     (let [panner (. ctx createStereoPanner)]
       (set! (.. panner -pan -value) (if left? -1 1))
       (.connect panner ctx.destination)
       panner))})

(defn update-display! [value]
  (set! (.-innerHTML display)
        (str "Base frequency: " value)))

(def get-oscillator
  (memoize
   (fn [panner]
     (let [o (ctx.createOscillator)]
       (set! (.- o type) "sine")
       (. o start)
       (. o (connect panner))
       o))))

(defn oscillate [panner hz]
  (let [o (get-oscillator panner)
        _ (set! (.. o -frequency -value) hz)])
  hz)

(defn update-app [frequency-value]
  (let [frequency-value (/ frequency-value 1000.0)]
    (-> panners :right (oscillate (+ frequency-value binaural-beat-freq)))
    (-> panners :left (oscillate frequency-value))
    (update-display! frequency-value)))

(set! (.-update_app js/window) update-app)

(update-app (* 1000 152.74))

The fact that I export my function by setting this global window object is quite wild. And then how I write a string of js code in the 'onchange' of the Html - damn. As I said, I am new to the web. Now I know one reason why it is called the wild west. But it let me "just do" what I wanted without being in my way so I appreciate that.

On the scittle side, I updated the code to only get 1 oscillator per slider. Seems like stuff is working. Switching up the frequencies via the slider is satisfying. For some reason, those artifacts are also gone. Maybe sliders just ensure balance and harmony in the world?

I am pretty sure I can tell there is 1 sound that seems to stay the same across frequencies.

I do the float * 1000 trick because I want to work with 2 digits for my frequencies.

Btw I also quickly tried what a real 40Hz tone sounds like. Ultra-low. I almost feel like I can make out the single waves, maybe that are the headphones cracking? Not surprising as the lower bound of human hearing is 20hz. The sound of the highest achievement of human thought. Genius insights, ideas that come to you like lightning. Globally integrating patterns of the brain and mind.

xijojsT.png

Figure 3: Harry potter wearing headphones having spiritual enlightenment about magic. Glowing magic sparks fly around. Colorful, cinematic, video game concept art.

Notes

Images

Made with stable-diffusion. The captions are the prompts I used.

Update 1

Currently does not work on mobile. Seems like I have the opportunity for another web dev feat, then. Seems like the issue is isolated on the sound appearing because the slider value works fine (meaning that the scittle code is running fine).

Update 2

Now it also works on mobile! Somebody helpfully pointed out on slack that on mac there was an error about audio refusing to play before the first touch event. I fixed that by adding a play button. But mobile still did not work, ok. After checking stackoverflow I still sort of have the idea that it has to do with user interaction events and this AudioContext. I first wanted to delay the whole audio setup until you click the button. But read that you should make a single AudioContext at the beginning.

So I try to delay the 1 other thing in the code:

(def panners
 (delay {:left (->panner true) :right (->panner false)}))
(-> @panners :right (oscillate (+ frequency-value binaural-beat-freq)))
(-> @panners :left (oscillate frequency-value))

It works, haha!

Date: 2022-09-19 Mon 11:04

About
Contact
]]>
https://faster-than-light-memes.xyz/meta-the-meta.html Meta The Meta (Use JVM repl for bb script development) 2023-06-15T23:59:59+00:00 Meta The Meta (Use JVM repl for bb script development)

ftlm

Meta The Meta (Use JVM repl for bb script development)

If you desire a good Clojure nrepl development environment using Babashka… yea just use print-deps and start a jvm repl.

DIYt9os.jpg

Figure 1: Highway street at night. Rain. Need for Speed Underground 2. Fast car light pass by. colorful neon lights in the background, reminiscent of the atmosphere in A mechanical keyboard and a set of headphones complete the look of the ultimate hacker workstation.

Bb Nrepl

bb nrepl-server 0

Or just cider-jack-in. It will ask you for bb, if you have a bb.edn.

Babashkas nrepl is useful for interactively building scripts but the moment you like to use more complicated (cider) features, some stuff is not implemented.

Middleware

Nrepl middleware support compatible with cider middleware would potentially unlock things like

  • Cider inspector
  • Cider debugger
  • Keyword completions

I tried tricking Borkdude into solving the problems needed for cider middleware in bb. And I accidentally thought for 5 minutes it might be easy but yea, it is hard middlewares?.

It started brewing in me:

Just run a jvm nrepl for developing your script, it works.

Turns out the same was obvious to Martin Kavalar all along - [insert a link to babashka-conf talk].1

print-deps

The basic idea:

bb print-deps > deps.edn
(cider-jack-in-clj)

We are done, we have all the bb packages as dependencies, fs, process and friends. Now we develop our script as a usual Clojure jvm program with all our advanced Cider goodness.

  • Development: jvm nrepl with bb deps loaded. All the dev goodness from our editors and middlewares.
  • Production: bb process executing the code we have produced.

And since sci is a great Clojure implementation, everything just maps over.2

You are not trying to do anything serious like a production server with babashka anyway… right?3

Add an alias, if deps.edn already exists

Here be a script that might be useful:

#!/bin/bb

(ns bb_print_deps_or_alias
  (:require
   [clojure.java.shell :as shell]
   [clojure.pprint :as pp]
   [babashka.fs :as fs]))

(let [file (fs/file "deps.edn")
      deps (-> (shell/sh "bb" "print-deps") :out read-string)
      deps
      (if-not
          (fs/exists? file)
          deps
          (update-in
           (read-string (slurp file))
           [:aliases :bb :extra-deps]
           merge
           (:deps deps)))]
  (spit file (with-out-str (pp/pprint deps))))

In the basic case, it makes a deps.edn, else if there is a deps.edn already existing, it ads a bb alias.

Here is me using Emacs and Lispy to come up with the following:

(defun mm/cider-jack-in-babashka ()
  (interactive)
  (let ((cider-clojure-cli-aliases
         (concat
          cider-clojure-cli-aliases ":bb")))
    (cider-jack-in-clj nil)))

Conclusion

I give up on making the babashka nrepl even better. It is fine like this for my use cases. Instead, I will run a JVM repl for developing bb scripts.

This then means that more Clojure nrepl tooling efforts can flow into making the jvm nrepl good. And by doing so we get leverage into the bb development experience also.

Unfortunately, this does not help us when it comes to Nbb and Clojurescript. This is another front for tooling.

Since the door is open, would there be a way to have 1 jvm repl and develop Clojurescript?

middlewares?

Footnotes:

1

Or, maybe Jack Rusher's practical interactive development genius was an influence, see the recent Clojure Stream Podcast.

2

With exceptions like the clojure.core.async go macro uses virtual threads instead of a thread pool.

Assuming you are using babashka for its intended use case, some smallish user land Clojure script, I think it will turn out great to just assume the development jvm will do the exact same thing as the prod bb script.

3

Unless your production server is being implemented by simple scripts, that is legit of course.

Date: [2023-06-15 Thu 16:56]l

About
Contact
]]>
https://faster-than-light-memes.xyz/jacking-nbb.html jacking-nbb 2022-10-14T23:59:59+00:00 jacking-nbb

ftlm

jacking-nbb

A cider jack in command for nbb.

Introduction

Nbb is a beatiful parasite. A node script that is an interpreter for Clojurescript. You get the upsides of Node.js + it is Clojure.

Want to try out some npm packages? Sure, just make a package.json, works instant, is part of the design principles of nbb.

You get Clojures immutable data structures, polymorphism constructs, a repl, etc.

On the tooling side, you might be surprised how far nbb gets with very little.

Scratching (in space) is very usefull and nbb is a natural fit for a quick cljs repl.

Update 2:

You can jack in Nbb nowadays with cider-jack-in-cljs then select nbb.

Alternatively, if you have cider d292d8d7eea5d3a12c138687133761a7256b1705

We can define jack in commands like so:

(defun cider-jack-in-nbb-2 ()
  "Start a Cider nREPL server with the `nbb nrepl-server` command."
  (interactive)
  (cider-jack-in '(:jack-in-cmd "nbb nrepl-server")))

You can also set a project jack-in-cmd in .dir-locals by setting cider-jack-in-cmd locally.

((nil
  (cider-jack-in-cmd . "nbb nrepl-server")))

This is especially useful, if you have an nbb project. You can now call cider-jack-in-clj without the need to select anything further anymore.

Note: Commands and elisp below are workarounds we do not need anymore because Cider supports Nbb much better since recently!

Update:

This is no longer needed with cider 1.6. There is a dedicated nbb cider build tool now. I still also think my proposal here would be useful.

;; the equivalent of the proposed change
(advice-add
 'cider--update-jack-in-cmd
 :before-until
 (defun cider-dont-update-jack-in-cmd-when-given (params)
   (when (plist-get params :jack-in-cmd) params)))

;; now a nbb jack in command becomes:
(cider-jack-in '(:jack-in-cmd "nbb nrepl-server"))

Jack-in command

cider repl workaround

We currently need this workaround for sci-based cljs repls for cider:

(cider-register-cljs-repl-type 'nbb-or-scittle-or-joyride "(+ 1 2 3)")

(defun mm/cider-connected-hook ()
  (when (eq 'nbb-or-scittle-or-joyride cider-cljs-repl-type)
    (setq-local cider-show-error-buffer nil)
    (cider-set-repl-type 'cljs)))

(add-hook 'cider-connected-hook #'mm/cider-connected-hook)

Note: You do not need this workaround in recent versions of cider anymore.

the command

Unfortunately, cider does not have the concept of a nbb repl currently. I think cider-jack-in-resolve-command and similar functions could be thrown out and replaced with data.

So I went 1 level deeper to bypass ciders auto-detection of project type (+ jack in cmd):

(defun mm/cider-jack-in-nbb ()
  "Start a nbb nrepl process and connect."
  (interactive)
  (let* ((cider-allow-jack-in-without-project t)
         (orig-buffer (current-buffer))
         (params '(:jack-in-cmd "nbb nrepl-server :port 0"
                   :cljs-repl-type nbb-or-scittle-or-joyride))
         (params (cider--update-project-dir
                  params)))
    (nrepl-start-server-process
     (plist-get params :project-dir)
     (plist-get params :jack-in-cmd)
     (lambda (server-buffer)
       (with-current-buffer
           orig-buffer
         (cider-connect-sibling-cljs
          params
          server-buffer))))))

This is what I wanted to say:

(defun mm/cider-jack-in-nbb ()
  "Start an nbb nrepl process and connect."
  (interactive)
  (cider-jack-in-cljs
   '(:jack-in-cmd "nbb nrepl-server :port 0"
     :cljs-repl-type nbb-or-scittle-or-joyride)))

hack for the remaining issue

With this, you should get an error "ClojureScript is not available…". Until there is a fix in cider, you can hack it by redefining cider-verify-clojurescript-is-present:

;;; FIXME: https://github.com/clojure-emacs/cider/issues/3255
(defun cider-verify-clojurescript-is-present ()
  "Check whether ClojureScript is present."
  (unless (nrepl-dict-get (cider-sync-tooling-eval "cljs.core/inc") "value")
    (user-error "ClojureScript is not available.  See https://docs.cider.mx/cider/basics/clojurescript for details")))

Results

Hehe, insta nbb repl. Works good.

Date: 2022-10-14 Fri 10:22

About
Contact
]]>
https://faster-than-light-memes.xyz/clojure-function-psychology-quiz.html Which Clojure Core Function are You? 2023-02-18T23:59:59+00:00 Which Clojure Core Function are You?

ftlm

Which Clojure Core Function are You?

Which Clojure Core Function are You?

Introduction

Since Chat-Gtp generating a certain kind of text is starting to be a solved problem.

I sort of liked what it had to say about what it means to be a fire bender in Avatar.

For some reason, I decided to make Clojure core function psychology puzzle, along the lines of What Clojure Function Are You? but also combine it with Avatar elements.

Avatar is cool

Is it just me or is there some Avatar The Last Airbender affinity in the Clojure ecosystem?

There is a database called Asami and Stuart Halloway is cosplaying as Aang somewhere.

Anyway, I dig Avatar myself, the whole world and the bending fit together very organically. Plus it sort of comes with thoughtfulness. And the bending, the choreography and the philosophy of it. Fire is anger but also life, it is about control. And with the highest control and focus you can lightning bend.

The bending choreography is amazing. Yes, I mean the choreography of the animated characters. It is inspired by Thai-Chi and other disciplines. Every element (fire, water, earth, air) has its style. It is one of the best magic systems I have encountered. The animations + sounds have a physical oomph. I almost feel like I know how it would be to bend. Water is fluent and dynamic, earth is solid and unmoving, fire is pure power, air is flying around and seeing the big picture.

Prompt engineering, Chat-Gpt makes a quiz

prompt 1

Help me make a quiz in the style of "Find out your harry potter house by answering those 6 psychological questions".

The quiz asks psychological questions that establish some personality traits of the user.

But we tell the user in the end which clojure.core function fits their personality.

Say one of those:

  • conj
  • reduce
  • juxt
  • swap!
  • into

We also map those with elements and bender power to Avatar the last airbender. For the bender psychological traits there are the elements and then there is another one for characters like Asami, who uses technology instead of bending.

Map the clojure core functions to avatar elements with a short description why.

prompt 1 end

I then kept asking a little, to get hiccup formatted data etc.

Result

I went and made a scittle re-frame app.

Code and link to the quiz!.

Date: 2023-02-18 Sat 16:55

About
Contact
]]>