[ prog / sol / mona ]

prog


Favorite Lisp dialect?

28 2024-03-10 09:14

Symta.

my personal lisp dialect.

Symta is a paradigm shifting dialect of Lisp programming language. Symta features succinct syntax, geared towards list-processing. Symta is an acronym, standing for SYMbolic compuTAtion language. As a Lisp system, Symta supports macros, eval and everything one would expect from a Lisp based language.

Symta allows performing advanced operations on lists and trees in a few lines of code. Usually it takes more words to loosely describe the task than to precisely implement it. Symta allows very tight code. Something as complex as a macro-processor could be implemented in a few characters of Symta code:

Data name!\Nancy age!37 city!\Amsterdam
Form "Hi! My name is %name%, I'm %age% years old and I live in %city%."
say Form{@"%[V]%"=Data.V}

The code doing macros substitution `{@"%[V]%"=Data.V}` is minuscule 18 chars, yet does something which usually requires hundreds of fail-prone C/C++ lines. And List{Map} is not even a special syntax, compared to Perl's regexps, but something general purpose, used everywhere for looping over data, structured or unstructured.

If we just want to collect all the `%variables%` used in the form, we can simply write

Form{@"%[&~r._]%"=} //produces (name age city) list

Symta allows doing the reverse too, and data can easily be parsed out of text.

Form "Hi! My name is Nancy, I'm 37 years old and I live in Amsterdam."
say Form("Hi! My name is [~], I'm [~] years old and I live in [~].")

Or, if you want to be explicit,

say Form("Hi! My name is [Name], I'm [Age] years old and I live in [City]."
      =: Name Age City)

Or more complex repetitive processing:

[:9](0:N@R=N+R^r) //sum integers 1 to 9, using recursion
[:9]{&~s+?} same as above, but using the `map` operator
[:9](1:N@R=N*R^r) //multiply integers 1 to 9, using recursion
[:9]{&~s^1*?} //same as above, but using the `map` operator
E '((b*2)-4*a*c)*0.5'
E{n~:'('=n~+;')'=n~-} //parenthesis levels across the expression E
L: a 3 b 3 a 5
L{T~.?+>0!=} //remove duplicate elements from the list L (a 3 b 5)
T{d? = ~~} //In text T replace each digit's occurence with its position
           //'1-a, 2-b and 3-c'{d? = ~~} => '0-a, 5-b and 13-c'
Path(_:"[A]/[B]"=A@r^B) //split Path into '/'-delimited components
Tree(_:H@T=@H^r@T^r;=:) //flatten `Tree` into a leaf list
Tree(:H@T=H^r@T^r;=:;hate=\love) //replace all occurrences of `hate`
                                 //with `love`, recursively
Tree(:_^r@_^r;&~r._<{?>100}<int?) //walk Tree recursively
                                  //collect integers larger than 100 as list
qsort@r H,@T = @T{:<H}^r,H,@T{<H=}^r //Hoare's quick sort algorithm

The above ``qsort`` example beats in brevity even the kings of conciseness - APL and J:

qsort=: (($:@(<#[), (=#[), $:@(>#[)) ({~ ?@#)) ^: (1<#)

Yet, compared to J, Symta's ``qsort`` is arguably more readable than Haskell's version:

qsort [] = []
qsort (x:xs) = qsort [y | y <- xs, y < x] ++ [x] ++ qsort [y | y <- xs, y >= x]

Do you know "Fizzbuzz", a famous assignment from the programming interviews? For numbers up to 100,
- If the number is divisible by 3, print "Fizz"
- If the number is divisible by 5, print "Buzz"
- If the number is divisible by both 3 and 5, print "FizzBuzz"
- If the number is not divisible by 3 or 5, print the number

A good solution in Symta would be:

[:100]{~?%15=\FizzBuzz;~?%3=\Fizz;~?%5=\Buzz}

The solution in J again gets pretty close in brevity:

FB=:((0 i.~15 3 5|]){::'FizzBuzz';'Fizz';'Buzz';":)"0
FB i.100

I really love J, despite it being rather messy and unreadable. Yet write-only code can be designed in Symta too:

[:100]{?(\Fizz:?%3=)+?(\Buzz:?%5="")||?}

Or even:

[:100]{\Fizz*%(?%3)+\Buzz*%(?%5)||?}

Here we shaved a few characters, exploiting the fact that "FizzBuzz" is made out of "Fizz" and "Buzz", using the quirks in `||` and `+` behaviour, which weren't originally intended for that. If in future "FizzBuzz" changes to "HerpDerp", while "Fizz" and "Buzz" remain, we will have hard time modifying such convoluted code, compared to a straightforward case analysis. And it is easy to confuse somebody even with a single line of code. Consider the below version of FizzBuzz

[:100]{[\FizzBuzz\Buzz\Fizz ?][(?%5>0)*2+(?%3>0)]}

This one uses deep arithmetic wizardry for no good reason, so any reader with below savant math skills will stumble on it. And even savants wont immediately see that say `Fizz` corresponds to divisibility by 3. Fortunately the pattern matching nature of Symta guides programmer towards the plain and simple case analysis.

62


VIP:

do not edit these