Skip to content

Commit 5ed3435

Browse files
authored
Scittle Repl example (#206)
* intro hybrid show expression * scittle repl * fix Gamma
1 parent f36af67 commit 5ed3435

File tree

4 files changed

+307
-60
lines changed

4 files changed

+307
-60
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
^{:kindly/hide-code true
2+
:clay {:title "scittle repl"
3+
:quarto {:type :draft
4+
;; :sidebar "emmy-fdg"
5+
:date "2025-11-12"
6+
:image nil
7+
:category :libs
8+
:tags [:emmy :physics]}}}
9+
(ns mentat-collective.emmy.appendix-scittlerepl
10+
(:refer-clojure :exclude [+ - * / zero? compare divide numerator
11+
denominator
12+
time infinite? abs ref partial =])
13+
(:require [scicloj.kindly.v4.api :as kindly]
14+
[scicloj.kindly.v4.kind :as kind]))
15+
16+
^:kindly/hide-code
17+
(kind/hiccup
18+
[:div
19+
[:script "var SCITTLE_NREPL_WEBSOCKET_PORT = 1340"]
20+
;;[:script {:src "https://cdn.jsdelivr.net/npm/scittle-kitchen/dist/scittle.js"}]
21+
;;[:script {:src "https://cdn.jsdelivr.net/npm/scittle-kitchen/dist/scittle.nrepl.js"}]
22+
[:script {:src "https://cdn.jsdelivr.net/npm/[email protected]/dist/scittle.js"}]
23+
[:script {:src "https://cdn.jsdelivr.net/npm/[email protected]/dist/scittle.nrepl.js"}]
24+
])
25+
26+
;; make sure that, in your local directory, there exists the file [browser_server](https://clojurecivitas.github.io/mentat_collective/emmy/browser_server.html)
27+
28+
;; Then in the terminal start babashka in the following way
29+
^:kindly/hide-code
30+
(kind/code
31+
"bb -cp . -e \"(require '[browser-server :as nrepl]) (nrepl/start! {:nrepl-port 1339 :websocket-port 1340}) (deref (promise))\"")
32+
33+
34+
;; then open a (not .clj but) .cljs file in your editor
35+
36+
;; In Cider, choose cider-connect-cljs, select localhost, port 1339, followed by the REPL type nbb .
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
(ns browser-server
2+
;;original name: sci.nrepl.browser-server
3+
;;original file https://github.com/babashka/sci.nrepl/blob/main/src/sci/nrepl/browser_server.clj
4+
5+
(:require
6+
[bencode.core :as bencode]
7+
[clojure.edn :as edn]
8+
[clojure.string :as str]
9+
[org.httpkit.server :as httpkit])
10+
(:import
11+
[java.io PushbackInputStream EOFException BufferedOutputStream]
12+
[java.net ServerSocket]))
13+
14+
(set! *warn-on-reflection* true)
15+
16+
(defn- update-when [m k f]
17+
(if-let [v (get m k)]
18+
(assoc m k (f v))
19+
m))
20+
21+
(defn- coerce-bencode [x]
22+
(if (bytes? x)
23+
(String. ^bytes x)
24+
x))
25+
26+
(defn- read-bencode [in]
27+
(try (let [msg (bencode/read-bencode in)
28+
msg (zipmap (map keyword (keys msg))
29+
(map coerce-bencode (vals msg)))]
30+
msg)
31+
(catch Exception e
32+
#_(def e e)
33+
(throw e))))
34+
35+
(defonce ^:private !last-ctx
36+
(volatile! nil))
37+
38+
(defn send-response [{:keys [out id session response]
39+
:or {out (:out @!last-ctx)}}]
40+
(let [response (cond-> response
41+
id (assoc :id id)
42+
session (assoc :session session))]
43+
(bencode/write-bencode out response)
44+
(.flush ^java.io.OutputStream out)))
45+
46+
(defn- handle-clone [ctx]
47+
(let [id (str (java.util.UUID/randomUUID))]
48+
(send-response (assoc ctx
49+
:response {"new-session" id "status" ["done"]}))))
50+
51+
(defonce nrepl-channel (atom nil))
52+
53+
(defn- response-handler [message]
54+
(let [{:as msg :keys [id session]} (edn/read-string message)]
55+
(send-response {:id id
56+
:session session
57+
:response (dissoc msg :id :session)})))
58+
59+
(defn- websocket-send! [msg]
60+
(when-let [chan @nrepl-channel]
61+
(httpkit/send! chan (str msg))))
62+
63+
(defn- handle-eval [{:as ctx :keys [msg session id send-fn] :or {send-fn websocket-send!}}]
64+
(vreset! !last-ctx ctx)
65+
(let [code (get msg :code)]
66+
(if (or (str/includes? code "clojure.main/repl-requires")
67+
(str/includes? code "System/getProperty"))
68+
(do
69+
(send-response (assoc ctx :response {"value" "nil"}))
70+
(send-response (assoc ctx :response {"status" ["done"]})))
71+
(send-fn {:op :eval
72+
:code code
73+
:id id
74+
:session session}))))
75+
76+
(defn- handle-load-file [ctx]
77+
(let [msg (get ctx :msg)
78+
code (get msg :file)
79+
msg (assoc msg :code code)]
80+
(handle-eval (assoc ctx :msg msg))))
81+
82+
(defn- handle-complete [{:keys [id session msg send-fn] :or {send-fn websocket-send!}}]
83+
(send-fn {:op :complete
84+
:id id
85+
:session session
86+
:symbol (get msg :symbol)
87+
:prefix (get msg :prefix)
88+
:ns (get msg :ns)}))
89+
90+
(defn- generically-handle-on-server [{:keys [id op session msg send-fn] :or {send-fn websocket-send!}}]
91+
(send-fn (merge msg
92+
{:op op
93+
:id id
94+
:session session})))
95+
96+
(defn- handle-describe [ctx]
97+
(vreset! !last-ctx ctx)
98+
(generically-handle-on-server (assoc ctx :op :describe)))
99+
100+
(defn- session-loop [in out {:keys [opts]}]
101+
(loop []
102+
(when-let [msg (try
103+
(let [msg (read-bencode in)]
104+
msg)
105+
(catch EOFException _
106+
(when-not (:quiet opts)
107+
(println "Client closed connection."))))]
108+
(let [ctx (cond-> {:out out :msg msg}
109+
(:send-fn opts)
110+
(assoc :send-fn (:send-fn opts)))
111+
id (get msg :id)
112+
session (get msg :session)
113+
ctx (assoc ctx :id id :session session)
114+
op (keyword (get msg :op))]
115+
(case op
116+
:clone (handle-clone ctx)
117+
:eval (handle-eval ctx)
118+
:describe (handle-describe ctx)
119+
:load-file (handle-load-file ctx)
120+
:complete (handle-complete ctx)
121+
(generically-handle-on-server (assoc ctx :op op))))
122+
(recur))))
123+
124+
(defn- listen [^ServerSocket listener {:as opts}]
125+
(println (str "nREPL server started on port " (:port opts) "..."))
126+
(let [client-socket (.accept listener)
127+
in (.getInputStream client-socket)
128+
in (PushbackInputStream. in)
129+
out (.getOutputStream client-socket)
130+
out (BufferedOutputStream. out)]
131+
(future
132+
(session-loop in out {:opts opts}))
133+
(recur listener opts)))
134+
135+
(defonce !socket (atom nil))
136+
137+
(defn start-nrepl-server! [{:keys [port] :as opts}]
138+
(let [port (or port 1339)
139+
inet-address (java.net.InetAddress/getByName "localhost")
140+
socket (new ServerSocket port 0 inet-address)
141+
_ (reset! !socket socket)]
142+
(future (listen socket opts))))
143+
144+
(defn stop-nrepl-server! []
145+
(when-let [socket @!socket]
146+
(.close ^ServerSocket socket)
147+
(reset! !socket nil)))
148+
149+
(defn- create-channel [req]
150+
(httpkit/as-channel req
151+
{:on-open (fn [ch]
152+
(reset! nrepl-channel ch))
153+
:on-close (fn [_ch _reason] (prn :close))
154+
:on-receive
155+
(fn [_ch message]
156+
(prn :msg message)
157+
(response-handler message))}))
158+
159+
(defn- app [{:as req}]
160+
(when (:websocket? req)
161+
(case (:uri req)
162+
"/_nrepl"
163+
(create-channel req))))
164+
165+
(defonce ^:private !server
166+
(atom nil))
167+
168+
(defn halt! []
169+
(when-let [{:keys [port stop-fn]} @!server]
170+
(stop-fn)
171+
(println (str "Webserver running on " port ", stopped."))
172+
(reset! !server nil)))
173+
174+
(defn start-websocket-server! [{:keys [port]}]
175+
(let [port (or port 1340)]
176+
(halt!)
177+
(try
178+
(reset! !server {:port port :stop-fn (httpkit/run-server #'app {:port port})})
179+
(println (str "Websocket server started on " port "..."))
180+
(catch Exception #_java.net.BindException e ;; TODO, add BindException to bb, done for 0.8.3
181+
(println "Port " port " not available, server not started!")
182+
(println (.getMessage e))))))
183+
184+
(defn start!
185+
[{:keys [nrepl-port websocket-port]
186+
:or {nrepl-port 1339
187+
websocket-port 1340}}]
188+
(start-nrepl-server! {:port nrepl-port})
189+
(start-websocket-server! {:port websocket-port}))

src/mentat_collective/emmy/fdg_ch01.clj

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
:category :libs
99
:tags [:emmy :physics]}}}
1010
(ns mentat-collective.emmy.fdg-ch01
11+
(:refer-clojure :exclude [+ - * / zero? compare divide numerator
12+
denominator
13+
time infinite? abs ref partial =])
1114
(:require [scicloj.kindly.v4.api :as kindly]
1215
[scicloj.kindly.v4.kind :as kind]
13-
[emmy.env :as e :refer :all :exlude [Cartan]]
14-
[emmy.mechanics.lagrange :as lg]
16+
[mentat-collective.emmy.scheme :refer [define-1 let-scheme]]
1517
[civitas.repl :as repl]))
1618

1719
;; The code snippets are executable, copy-paste them to the sidebar of the page.
@@ -39,32 +41,56 @@
3941
[:script {:type "application/x-scittle" :src "scheme.cljc"}]])
4042

4143
^:kindly/hide-code
42-
(kind/scittle
43-
'(require '[emmy.env :as e :refer :all :exclude [F->C]]))
44+
(defmacro define [& b]
45+
(list 'do
46+
(cons 'mentat-collective.emmy.scheme/define b)
47+
(list 'kind/scittle (list 'quote (cons 'define b)))))
4448

4549
^:kindly/hide-code
46-
(kind/scittle
47-
'(def velocities velocity))
50+
(define emmy-require
51+
'[emmy.env :as e :refer :all :exclude [F->C Cartan]])
52+
53+
^:kindly/hide-code
54+
(require emmy-require)
4855

4956
^:kindly/hide-code
5057
(kind/scittle
51-
'(def coordinates coordinate))
58+
'(require emmy-require))
59+
60+
^:kindly/hide-code
61+
(define show-exp (comp str simplify))
5262

5363
^:kindly/hide-code
5464
(kind/scittle
55-
'(def show-expression (comp simplify)))
65+
'(def show-expression show-exp))
5666

5767
^:kindly/hide-code
58-
(defmacro show-expression [& b]
59-
(list 'kind/reagent [:tt (list 'quote (cons 'show-expression b))]))
68+
(defmacro show-expression [b & c]
69+
(case b
70+
:calc-on-server
71+
(list 'simplify (first c))
72+
:browser
73+
(list 'kind/reagent
74+
[:tt (list 'quote
75+
(list 'show-exp (first c)))])
76+
(let [serg (show-exp (eval b))]
77+
(list 'kind/reagent
78+
[:div (list 'quote
79+
(list 'let ['a (list 'show-exp b)]
80+
(list 'if (list '= serg 'a)
81+
[:tt 'a]
82+
[:div
83+
;; [:tt 'a] ;; comment this in prod
84+
[:tt serg]])))]))))
6085

6186
^:kindly/hide-code
62-
(defmacro define [& b]
63-
(list 'kind/scittle (list 'quote (cons 'define b))))
87+
(define velocities velocity)
88+
89+
^:kindly/hide-code
90+
(define coordinates coordinate)
6491

6592
^:kindly/hide-code
66-
(def md
67-
(comp kindly/hide-code kind/md))
93+
(define time first)
6894

6995
;; > Philosophy is written in that great book which ever lies before our eyes---I
7096
;; > mean the Universe---but we cannot understand it if we do not learn the language
@@ -272,11 +298,6 @@
272298
(define ((L2 mass metric) place velocity)
273299
(* 1/2 mass ((metric velocity velocity) place)))
274300

275-
^:kindly/hide-code
276-
(defn L2 [mass metric]
277-
(fn [place velocity]
278-
(* 1/2 mass ((metric velocity velocity) place))))
279-
280301
;; This program gives the Lagrangian in a coordinate-independent, geometric way. It
281302
;; is entirely in terms of geometric objects, such as a place on the configuration
282303
;; manifold, the velocity at that place, and the metric that describes the local
@@ -293,12 +314,6 @@
293314
(e (coordinate-system->vector-basis coordsys)))
294315
((L2 mass metric) ((point coordsys) x) (* e v))))
295316

296-
^:kindly/hide-code
297-
(defn Lc [mass metric coordsys]
298-
(let [e (coordinate-system->vector-basis coordsys)]
299-
(fn [[_ x v]]
300-
((L2 mass metric) ((point coordsys) x) (* e v)))))
301-
302317
;; The manifold point $\mathsf{m}$ represented by the coordinates $x$ is given by
303318
;; =(define m ((point coordsys) x))=. The coordinates of $\mathsf{m}$ in a
304319
;; different coordinate system are given by =((chart coordsys2) m)=. The manifold
@@ -312,19 +327,13 @@
312327

313328
(define the-metric (literal-metric 'g R2-rect))
314329

315-
^:kindly/hide-code
316-
(def the-metric (literal-metric 'g R2-rect))
317-
318330
;; The metric is expressed in rectangular coordinates, so the coordinate system is
319331
;; =R2-rect=.[fn:5] The component functions will be labeled as subscripted ~g~s.
320332

321333
;; We can now make the Lagrangian for the system:
322334

323335
(define L (Lc 'm the-metric R2-rect))
324336

325-
^:kindly/hide-code
326-
(def L (Lc 'm the-metric R2-rect))
327-
328337
;; And we can apply our Lagrangian to an arbitrary state:
329338

330339
(show-expression
@@ -341,9 +350,6 @@
341350

342351
(define gamma (literal-manifold-map 'q R1-rect R2-rect))
343352

344-
^:kindly/hide-code
345-
(def gamma (literal-manifold-map 'q R1-rect R2-rect))
346-
347353
;; The values of $\gamma$ are points on the manifold, not a coordinate
348354
;; representation of the points. We may evaluate =gamma= only on points of the
349355
;; real-line manifold; =gamma= produces points on the $\mathbb{R}^2$ manifold. So
@@ -358,10 +364,6 @@
358364
(define coordinate-path
359365
(compose (chart R2-rect) gamma (point R1-rect)))
360366

361-
^:kindly/hide-code
362-
(def coordinate-path
363-
(compose (chart R2-rect) gamma (point R1-rect)))
364-
365367
(show-expression
366368
(coordinate-path 't))
367369

@@ -372,10 +374,6 @@
372374
(define Lagrange-residuals
373375
(((Lagrange-equations L) coordinate-path) 't))
374376

375-
^:kindly/hide-code
376-
(def Lagrange-residuals
377-
(((Lagrange-equations L) coordinate-path) 't))
378-
379377
;; ## Geodesic Equations
380378

381379
;; Now we get deeper into the geometry. The traditional way to write the geodesic
@@ -472,7 +470,7 @@
472470
the-metric
473471
(coordinate-system->basis R2-rect)))
474472

475-
(simplify
473+
(show-expression :calc-on-server
476474
(- Lagrange-residuals
477475
(* (* 'm (metric-components (gamma ((point R1-rect) 't))))
478476
geodesic-equation-residuals)))

0 commit comments

Comments
 (0)