Skip to content

Golfing with ShinyLisp

Silvio Mayolo edited this page Dec 5, 2019 · 9 revisions

Golfing with ShinyLisp

This page is for those who are familiar with basic Lisp syntax and would like to learn what this language is all about. If you've never seen Lisp code before, I suggest you start with Intro to Lisp and come back here after that.

The basic need-to-know form in ShinyLisp is =, which is used for assignment.

(= a 3)

This assigns the value 3 to the variable a. If the variable does not exist, it is created. If we want to make a function, we simply quote a list.

(= f '(print "You called the function f"))
(f)

This will define a function called f which prints a predefined string. We then call the function. The list of arguments passed to a function are stored in the special variable !!. For convenience, the first three individual arguments can be accessed using x, y, and z. The latter arguments also have special names, but they are not used very often.

For those who have seen Lisp, you might understandably pose the question: With all of those parentheses, how can Lisp possibly act as a golfing language? The answer is quite simple: We find creative ways to tell the code where our parentheses are. Let's take an ungolfed Lisp expression.

(= abs '(cond (less-than x 0) (& x \1) x))

This defines a function abs. The cond special form is a more powerful form of the familiar if-statement from other languages. The literal \1 is the value -1. A backslash before a number makes the value negative. Finally, & is the multiplication function. So the line reads "If the argument is less than 0, return the argument times -1, otherwise return the argument". Right now, it's not very golfed. First, we use short names of functions.

(= f '(i (c, x 0) (& x \1) x))

Most functions in ShinyLisp have a one- or two- character variant. Now, you may notice that, up to this point, we've been using only lowercase letters. The reason for that is this: if we put a capital letter in an identifier, it starts a new identifier in the chain, so (alphaBetaGamma) is the same as (alpha beta gamma) but shorter. We can eliminate quite a few spaces using this rule.

(=F'(i(c,X0)(&X\1)x))

This is already looking better, but there are still quite a few parentheses. There are a few other chain rules that come in handy, and they can be summarized as follows.

"1. Capital letters in identifiers form a chain"
(alphaBetaGamma) ==> (alpha beta gamma)
"2. A capital letter at the beginning of an identifier opens a new list"
(AlphaBetaGamma) ==> ((alpha beta gamma))
"3. A tilde forces the end of one or more chains"
(AlphaBeta~Gamma) ==> ((alpha beta) (gamma))
"4. A colon in front of a character treats the next character as capital"
(:alphaBetaGamma) ==> ((alpha beta gamma))
"5. A backslash followed by a capital letter treats the next character as lowercase"
(alphaBeta\Gamma) ==> (alpha betaGamma)

That fourth rule may not seem useful, but it can be stacked with the second rule.

FABG1 ==> (f a b g 1)
FAB:G1 ==> (f a b (g 1))

So we can create sublists in the middle of an expression. Now, with that in mind, let's look at our previous expression.

(=F'(i(c,X0)(&X\1)x))

For many symbolic characters, the "capital" form is simply the character you get when you hit the same key on your keyboard while holding SHIFT. So a "capital" comma is a less-than sign. However, since numbers are treated specially by the syntax, all of the symbols on the number line of a keyboard have unusual capital mappings. The "capital" form of & is *. Further, let's switch the order of the conditionals so we can use our colon rule. While c, is the short form of less-than, c; is the short form of greater-than.

+F'(i(c;X0)X:*X\1)

Now, remember that the quote marker is just shorthand for quote, and quote too has an abbreviated name. It can be called simply with q. We can use our substitution rules to further eliminate more parentheses.

+F:Q:I(c;X0)X:*X\1

So we've gone from 42 to 18 characters, which is not bad. We could shave off a few more using some higher-order functions that are built in to the standard library, but for didactic purposes this is far enough.

Clone this wiki locally