Emmy, the Algebra System: Infix Notation
The Clojure code below is taken from a previous Civitas entry (the picture from Emily’s parser game).
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 given attention to.
First Example
(kind/scittle
'(defn Lfree [mass]
(fn [[_ _ v]] (* 1/2 mass (square v)))))The following infix notation indeed compiles to Clojure code and is equivalent to the above.
[
defn LFree(mass):
fn([_ _ v]): 1/2 * mass * square(v)
#'mentat-collective.emmy.fdg-ch01-ys/LFree
]
Another one
(kind/scittle
'(defn sphere->R3 [R]
(fn [[_ [theta phi]]]
(up (* R (sin theta) (cos phi))
(* R (sin theta) (sin phi))
(* R (cos theta))))))[
defn sphere-to-R3(R):
fn([_ [theta phi]]):
up:
R *: sin(theta) * cos(phi)
R *: sin(theta) * sin(phi)
R *: cos(theta)
#'mentat-collective.emmy.fdg-ch01-ys/sphere-to-R3
]
The *: means nothing but normal multiplication.
Higher order functions
For the next infix, I introduce call (with obvious behaviour) and two aliases.
(do
(defn call [f x] (f x))
(def of call)
(def at call))#'mentat-collective.emmy.fdg-ch01-ys/at[
defn F-to-C(F):
fn(state):
up:
time: state
F: state
partial(0).of(F).at(state) +:
partial(1).of(F).at(state) *
velocity(state)
#'mentat-collective.emmy.fdg-ch01-ys/F-to-C
]
Again, the above +: is normal addition. With of and at like that, the above partial(0).of(F).at(state) (which means “take the partial derivative with respect to the first variable of the function F at point state”) translates into what are higher order functions in the Clojure version.
(kind/scittle
'(defn F->C [F]
(fn [state]
(up (time state)
(F state)
(+ (((partial 0) F) state)
(* (((partial 1) F) state)
(velocity state)))))))Another one
[
defn Lsphere(m R):
compose:
LFree: m
F-to-C: sphere-to-R3(R)
#'mentat-collective.emmy.fdg-ch01-ys/Lsphere
]
(kind/scittle
'(defn Lsphere [m R]
(compose (Lfree m)
(F->C (sphere->R3 R)))))The proof is in the pudding
Emmy is a symbolic algebra system, s:q as well as q: s produce the symbol named s. Below we have m:q and q: t to produce the symbols m and t.
[
simplify:
Lsphere(m:q R:q):
up:
q: t
up: theta:q phi:q
up: thetadot:q phidot:q
(+ (* 1/2 (expt R 2) m (expt phidot 2) (expt (sin theta) 2)) (* 1/2 (expt R 2) m (expt thetadot 2)))
]
(kind/reagent
(vector :tt
'(simplify
((Lsphere 'm 'R)
(up 't
(up 'theta 'phi)
(up 'thetadot 'phidot))))))Indeed the results are the same which proves the infix technically works.
Intermission
The infix notation I use here is YS (say “wise”), also called YAMLScript. 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.
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”.
I’d like to put into light a related project of mine. With 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.
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:
yamlscript/core {:git/url "https://github.com/yaml/yamlscript"
:git/sha "ed7adfbf90a39f379d5a7193bb2e4bdd7f0eecf8"
:deps/root "core"}Some more examples
1
[
defn L2(mass metric):
fn(place velocity):
1/2 *:
mass *
metric(velocity velocity).at(place)
#'mentat-collective.emmy.fdg-ch01-ys/L2
]
(kind/scittle
'(defn L2 [mass metric]
(fn [place velocity]
(* 1/2
mass
((metric velocity velocity) place)))))2
[
defn Lc(mass metric coordsys):
e =: coordinate-system-to-vector-basis(coordsys)
fn([_ x v]):
L2(mass metric): point(coordsys).at(x), (e * v)
#'mentat-collective.emmy.fdg-ch01-ys/Lc
]
The , above is a matter of taste and can also be omitted
(kind/scittle
'(defn Lc [mass metric coordsys]
(let [e (coordinate-system->vector-basis coordsys)]
(fn [[_ x v]]
((L2 mass metric) ((point coordsys) x) (* e v))))))3
[
the-metric =: literal-metric(g:q R2-rect)
#'mentat-collective.emmy.fdg-ch01-ys/the-metric
]
(kind/scittle
'(def the-metric (literal-metric 'g R2-rect)))4
[
L =: Lc(m:q the-metric R2-rect)
#'mentat-collective.emmy.fdg-ch01-ys/L
]
(kind/scittle
'(def L (Lc 'm the-metric R2-rect)))and the pudding
[
simplify:
L:
up:
q: t
up: x:q y:q
up: vx:q vy:q
(+ (* 1/2 m (expt vx 2) (g_00 (up x y))) (* m vx vy (g_01 (up x y))) (* 1/2 m (expt vy 2) (g_11 (up x y))))
]
(kind/reagent
(vector :tt
'(simplify
(L (up 't
(up 'x 'y)
(up 'vx 'vy))))))