Skip to content

Commit c6133c9

Browse files
Merge pull request #180 from kloimhardt/infix
Emmy Infix Notation with YAMLScript
2 parents e609ee8 + 4b2d6b6 commit c6133c9

File tree

2 files changed

+251
-0
lines changed

2 files changed

+251
-0
lines changed

deps.edn

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@
4949
org.soulspace/qclojure {:mvn/version "0.22.0"}
5050
org.babashka/http-client {:mvn/version "0.4.22"}
5151
com.github.danielsz/bioscoop {:mvn/version "1.0.5"}
52+
yamlscript/core {:git/url "https://github.com/yaml/yamlscript"
53+
:git/sha "ed7adfbf90a39f379d5a7193bb2e4bdd7f0eecf8"
54+
:deps/root "core"}
5255
}
5356

5457
:aliases
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
^{:kindly/hide-code true
2+
:clay {:title "Emmy, the Algebra System: Infix Notation"
3+
:quarto {:author :kloimhardt
4+
:type :draft
5+
:date "2025-11-15"
6+
:category :libs
7+
:tags [:emmy :infix :yamlscript]}}}
8+
9+
(ns mentat-collective.emmy.fdg-ch01-ys
10+
(:refer-clojure :exclude [+ - * / zero? compare divide numerator denominator
11+
time infinite? abs ref partial =])
12+
(:require [emmy.env :refer :all :exclude [D F->C]]
13+
[yamlscript.compiler :as ys]
14+
[scicloj.kindly.v4.api :as kindly]
15+
[scicloj.kindly.v4.kind :as kind]))
16+
17+
^:kindly/hide-code
18+
(def +++ identity)
19+
20+
^:kindly/hide-code
21+
(def mul+ *)
22+
23+
^:kindly/hide-code
24+
(def add+ +)
25+
26+
^:kindly/hide-code
27+
(defn ysc [s]
28+
(ys/compile (str "!ys-0\n" s)))
29+
30+
^:kindly/hide-code
31+
(defmacro ys [s]
32+
(list 'kindly/hide-code
33+
[(list 'kind/code s)
34+
(read-string (ysc s))]))
35+
36+
^:kindly/hide-code
37+
(kind/hiccup
38+
[:div
39+
[:script {:src "https://cdn.jsdelivr.net/npm/scittle-kitchen/dist/scittle.js"}]
40+
[:script {:src "https://cdn.jsdelivr.net/npm/scittle-kitchen/dist/scittle.emmy.js"}]
41+
[:script {:src "https://cdn.jsdelivr.net/npm/scittle-kitchen/dist/scittle.cljs-ajax.js"}]
42+
[:script {:src "https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js", :crossorigin ""}]
43+
[:script {:src "https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js", :crossorigin ""}]
44+
[:script {:src "https://cdn.jsdelivr.net/npm/scittle-kitchen/dist/scittle.reagent.js"}]
45+
[:script {:type "application/x-scittle" :src "scheme.cljc"}]])
46+
47+
48+
^:kindly/hide-code
49+
(kind/scittle
50+
'(require '[emmy.env :refer :all :exclude [D F->C]]))
51+
52+
^:kindly/hide-code
53+
(def time first)
54+
55+
^:kindly/hide-code
56+
(kind/scittle
57+
'(def time first))
58+
59+
^:kindly/hide-code
60+
(kind/scittle
61+
'(def D partial))
62+
63+
;; The Clojure code below is taken from the snippets of [a previous entry](https://clojurecivitas.github.io/mentat_collective/emmy/fdg_ch01.html). Please make pull requests to [the source file of this notebook](https://github.com/kloimhardt/clojurecivitas.github.io/blob/infix/src/mentat_collective/emmy/fdg_ch01_ys.clj). It is not necessary to understand what the Clojure code does. As this semantics does not matter here, it is rather the syntax, the notation, that should be compared and critiqued vis-a-vis the proposed infix notation.
64+
65+
;; ## First Example
66+
67+
(kind/scittle
68+
'(defn Lfree [mass]
69+
(fn [[_ _ v]] (* 1/2 mass (square v)))))
70+
71+
;; The following infix notation indeed compiles to Clojure code and is equivalent to the above.
72+
73+
(ys "
74+
defn LFree(mass):
75+
fn([_ _ v]): mass * 1/2 * square(v)
76+
")
77+
78+
;; ## Another one
79+
80+
;; I proceed to the next infix snippet
81+
82+
(ys "
83+
defn sphere-to-R3(R):
84+
fn([_ [theta phi]]):
85+
up:
86+
=>: R * sin(theta) * cos(phi)
87+
=>: R * sin(theta) * sin(phi)
88+
=>: R * cos(theta)
89+
")
90+
91+
;; which is the following in Clojure
92+
93+
(kind/scittle
94+
'(defn sphere->R3 [R]
95+
(fn [[_ [theta phi]]]
96+
(up (* R (sin theta) (cos phi))
97+
(* R (sin theta) (sin phi))
98+
(* R (cos theta))))))
99+
100+
;; ## Higher order functions
101+
102+
;; For the next infix, I introduce `call` (with obvious behaviour) and two aliases.
103+
104+
(do
105+
(defn call [f x] (f x))
106+
(def of call)
107+
(def at call))
108+
109+
(def D partial)
110+
111+
(ys "
112+
defn F-to-C(F):
113+
fn(state):
114+
up:
115+
time: state
116+
F: state
117+
=>: D(0).of(F).at(state) + ( D(1).of(F).at(state) * velocity(state) )
118+
")
119+
120+
;; With `of` and `at` like that, the above `D(0).of(F).at(state)` (which means "take the zeroth derivative of the function F at point state") translates into what are higher order functions in the Clojure version.
121+
122+
(kind/scittle
123+
'(defn F->C [F]
124+
(fn [state]
125+
(up (time state)
126+
(F state)
127+
(+ (((D 0) F) state)
128+
(* (((D 1) F) state)
129+
(velocity state)))))))
130+
131+
;; ## Another one
132+
133+
(ys "
134+
defn Lsphere(m R):
135+
compose:
136+
LFree: m
137+
F-to-C: sphere-to-R3(R)
138+
")
139+
140+
(kind/scittle
141+
'(defn Lsphere [m R]
142+
(compose (Lfree m) (F->C (sphere->R3 R)))))
143+
144+
;; ## The proof is in the pudding
145+
146+
;; In order to quote symbols, I introduce a macro called `q`
147+
148+
(defmacro q [f] (list 'quote f))
149+
150+
(ys "
151+
simplify:
152+
Lsphere(m:q R:q):
153+
up:
154+
=>: t:q
155+
up: theta:q phi:q
156+
up: thetadot:q phidot:q
157+
")
158+
159+
(kind/reagent
160+
(vector :tt
161+
'(simplify
162+
((Lsphere 'm 'R)
163+
(up 't (up 'theta 'phi) (up 'thetadot 'phidot))))))
164+
165+
;; Indeed the results are the same which proves the infix technically works.
166+
167+
;; ## Intermission
168+
169+
;; The infix notation I use here is `YS` (say "wise"), also called [YAMLScript](https://yamlscript.org/). I have read one opinion that says "I do not want my program to run through a YAML parser". This is a vantage point which is not applicable to Clay notebooks. A Clay notebook is not about maintaining a codebase, it is about conveying ideas and concepts. It is not about the writing of code, it is about the audience which reads the code.
170+
171+
;; And concerning reading code, YS has a lot to offer for the Clojure community. Namely that with YS we can communicate the idea of functional+immutable to that particular audience which is not even willing to read code that has the "parens in the wrong place".
172+
173+
;; I'd like to put into light a related project of mine. With [LisRoot](https://github.com/kloimhardt/LisRoot) I try to lure away the HEP-community from Python towards Clojure/jank. And here YS is the single best way to show code snippets that are functional+immutable. Those people who like that concept will eventually learn to install a code editor for slurp/barfing and then also cherish the nrepl as we all do.
174+
175+
;; I did not find the appropriate YS-compiler on Clojars (for Clay I do not want a dependency on the shared compiled library) , so to deps.edn, I added the following git-sha:
176+
177+
^:kindly/hide-code
178+
(kind/code "
179+
yamlscript/core {:git/url \"https://github.com/yaml/yamlscript\"
180+
:git/sha \"ed7adfbf90a39f379d5a7193bb2e4bdd7f0eecf8\"
181+
:deps/root \"core\"}
182+
")
183+
184+
;; ### Some more examples
185+
186+
;; ## 1
187+
(ys "
188+
defn L2(mass metric):
189+
fn(place velocity): mass * 1/2 * metric(velocity velocity).at(place)
190+
")
191+
(kind/scittle
192+
'(defn L2 [mass metric]
193+
(fn [place velocity]
194+
(* 1/2 mass ((metric velocity velocity) place)))))
195+
196+
197+
;; ## 2
198+
199+
^:kindly/hide-code
200+
(def coordinate-system-to-vector-basis coordinate-system->vector-basis)
201+
202+
(ys "
203+
defn Lc(mass metric coordsys):
204+
e =: coordinate-system-to-vector-basis(coordsys)
205+
fn([_ x v]):
206+
L2(mass metric): point(coordsys).at(x) (e * v)
207+
")
208+
209+
(kind/scittle
210+
'(defn Lc [mass metric coordsys]
211+
(let [e (coordinate-system->vector-basis coordsys)]
212+
(fn [[_ x v]]
213+
((L2 mass metric) ((point coordsys) x) (* e v))))))
214+
215+
;; ## 3
216+
217+
(ys "
218+
the-metric =: literal-metric(g:q R2-rect)
219+
")
220+
221+
(kind/scittle
222+
'(def the-metric (literal-metric 'g R2-rect)))
223+
224+
225+
;; ## 4
226+
(ys "
227+
L =: Lc(m:q the-metric R2-rect)
228+
")
229+
230+
(kind/scittle
231+
'(def L (Lc 'm the-metric R2-rect)))
232+
233+
234+
;; ## and the pudding
235+
236+
(ys "
237+
simplify:
238+
L:
239+
up:
240+
=>: t:q
241+
up: x:q y:q
242+
up: vx:q vy:q
243+
")
244+
245+
(kind/reagent
246+
(vector :tt
247+
'(simplify
248+
(L (up 't (up 'x 'y) (up 'vx 'vy))))))

0 commit comments

Comments
 (0)