Scheming a mise-en-abîme in BQN

2 days ago 1

Our goal is to adhere to the Revised\(^5\) Report on the Algorithmic Language Scheme (R5RS). However, seasoned schemers will quickly notice that our implementation still has quite some distance to cover in reaching full compliance.

Let's start by defining some utilities. One aspect I don't like about Scheme is that it uses special values for Booleans, so we unfortunately need the 1-modifier. The function, on the other hand, is a fine example of the minimalistic OOP features BQN provides. It is used to create a class for the environment used in the Scheme interpreter.

_bool ← {𝔽◶"#f"‿"#t"} C ← {𝕨𝕊p‿v: o‿h ⇐ 𝕨 ⋈ p •HashMap v F ⇐ {h.Has 𝕩 ? h; @≢o ? o.F 𝕩; 0} }

We then define a global environment (instance of the C class) with the Scheme primitives of the target subset, expressed as BQN functions:

env ← @ C ⟨ "sin", "cos", "tan", "asin", "acos", "atan" "log", "+", "-", "*", "/", ">", "<", ">=", "<=", "=" "abs", "append", "apply", "begin", "car", "cdr", "cons" "eq?", "expt", "equal?", "length", "list", "list?" "map", "max", "min", "not", "null?", "number?" "print", "round", "symbol?", "nil", "pi" ⟩ ⋈ ⟨ ⋆⁼, +´, -˜´⌽, ×´, ÷˜´⌽, >´, <´, ≥´, ≤´, =´ |, ∾´, {𝕎𝕩}´, {∾𝕩}, ⊑∘∾, 1⊸↓∘∾, <⊸∾´ ≡´_bool, ⋆´, =´_bool, ≠∘∾, ⊢, (0=•Type∘⊑)_bool {𝕎∘⋈¨𝕩}´, ⌈´, ⌊´, 0⊸≠_bool¬, @⊸=_bool, (1=•Type∘⊑)_bool {𝕩}, ⌊0.5+⊢, 2⊸=_bool{•Type⊑∾𝕩}, @, π ⟩ ∾˜ •math •ns.Get¨ "sin"‿"cos"‿"tan"‿"asin"‿"acos"‿"atan"

The interpreter is defined as a 1-modifier. This gives us the flexibility to create different subsets of the language by changing the input global environment:

_sch ← { T ← " "⊸≢¨⊸/·(-⟜1·+`·¬⊸∧⟜»⊸∨·+˝"( )"=⌜⊢)⊸⊔(⊢+22×@=10-˜⊢) R ← { 𝕊⟨⟩: "Empty program"!0; 𝕊𝕩: { "("≡⊑𝕨 ? l←⟨⟩ ⋄ l⋈1↓{t‿ts: ts⊣l∾↩<t}∘R•_while_(")"≢⊑) 𝕩; ")"≡⊑𝕨 ? "Unexpected )"!0 ; 𝕩 ⋈˜ •ParseFloat⎊⊢ ⊑𝕨 }´ 1(↑⋈↓)𝕩 } E ← 𝕗⊸{ 0≠𝕨.F 𝕩 ? (𝕨.F 𝕩).Get 𝕩; 1=•Type⊑⟨𝕩⟩ ? 𝕩; 𝕨𝕊"quote"‿arg: arg; 𝕨𝕊"quasiquote"‿arg: 𝕨{"unquote"≡⊑𝕩 ? 𝕗𝔾1↓𝕩; (2≤≠)◶⊢‿(𝕊¨)𝕩}𝕊arg; 𝕨𝕊"if"‿tst‿cnd‿alt: 𝕨(⊣𝕊𝕊◶alt‿cnd)tst; 𝕨𝕊"define"‿var‿val: ⟨⟩ ⊣ var 𝕨.h.Set 𝕨𝕊val; 𝕨𝕊"lambda"‿par‿bod: 𝕨{bod 𝕘˜ 𝕗 C par‿𝕩}𝕊; f ← 𝕨𝕊⊑𝕩 ⋄ F 𝕨⊸𝕊¨1↓𝕩 }∘⊑ P ← "-(@ )" {'@'⊸≠⊸/·(⊢+˝(𝕗-𝕘)×𝕘=⌜⊢)∘•Repr·1⊸=∘≠◶⊢‿⊑(0<≠¨)⊸/⎊⊢} "¯⟨"",‿⟩" P∘E∘R∘T 𝕩 }

And now for the climax. Our interpreter inherits all the limitations of the one in the reference essay, the most critical being the lack of proper error handling. Additionally, as the names of the functions inside the modifier suggest, an L is missing to complete the Read → Eval → Print loop. In terms of golfing statistics, lispy has 117 non-comment non-blank lines, whereas Scheme has only 43. Ours, however, is a larger subset, because we include the basic metaprogramming building blocks.

Read Entire Article