I was recently playing around with Sibilant, a language that is stylistically similar to Lisp and compiles to JavaScript, and figured that I would give a brief overview of some of the interesting concepts. There are a whole bunch of languages that compile to JS, and even some others that are also stylistically similar to Lisp like ClojureScript.
Let’s see some example Sibilant code and see how it compiles to get a better understanding of how it works. You can also follow along by installing Sibilant and starting a REPL in your console. If you have Node.js installed, you can just install using npm
. You can check by running
node -v && \
npm -v
in the console. If both versions are not printed, then you will have to install. Alternatively, follow the instructions below to install and if you run into an error start backtracking. The official recommendation is to use a version manager for Node.js, and I would personally look into nvm.
So, assuming that you have npm
installed, you can install sibilant with
npm install -g sibilant
(The -g
will update if you already have it installed)
Since you may need to run as root, I would recommend double checking the official documentation before you run anything with sudo
. Who trusts running commands as root from random people?
Once you have successfully installed it, you can start the REPL with a simple
sibilant
In the following examples, we are running code in the sibilant interpreter. I have also included what the interpreter returns so that you can see how to Sibilant code is compiled into JS. s
If we want to print “Hello World!”, we can run
sibilant> (console.log "Hello World!")
console.log("Hello World!");
"Hello World"
Just as in JS, we can create variables to store data. It will look something like this
sibilant> (var i 10)
var i = 10;
It’s also possible to use strings
sibilant> (var myString "This is my string")
var myString = 'This is my string";
or undefined variables
sibilant> (var x)
var x = undefined;
If we have a lot to create at once, that is also okay
sibilant> (var a 1, b 2, c 3)
var a = 1,
b = 2,
c = 3;
If we need to modify a variable, we can use the assign
macro.
sibilant> (assign a 2000)
a = 2000;
Arrays and objects are supported. Instantiating an array is done like this
sibilant> [1 2 3 4 5]
[1, 2, 3, 4, 5]
result: [1, 2, 3, 4, 5]
It can also easily be set as a variable
sibilant> (var a [1 2 3 4 5])
var a = [1, 2, 3, 4, 5];
Similarly with objects,
sibilant> (var o {key1 101, key2 "my string", key3 [1 2 3]})
var o = {
key1: 101,
key2: "my string",
key3: [ 1, 2, 3 ]
};
To modify the structures, use set
or get
sibilant> (set o["key2"] "a new value")
result: 'a new value'
sibilant> (get a[1])
result: 2
functions can be defined as follows
sibilant> (def printName (myName)
(var intro (+ "Hello, my name is " myName ))
(console.log intro)
intro)
var printName = (function printName$(myName) {
/* printName eval.sibilant:1:0 */
var intro = ("Hello, my name is " + myName);
console.log(intro);
return intro;
});
If no parameters are passed to the function, the parenthesis can be left empty.
When you have conditionals, the macros if
, when
, and will come in handy.
For a single condition, we can use when
.
sibilant> (when (= clock.minutes zero)
clock.chime(clock.hour))
(function() {
if (clock.minutes === 0) {
return clock.chime(clock.hour);
}
}).call(this)
result: "Chirp chirp chirp"
If we have different conditions to consider where, we can use if
to create an “if/else if/else” pattern.
sibilant> (if coffeepot.full)
(drink size.large)
(coffeepot.half) # else if
(drink size.small)
(coffeepot.quarter) #else if
(do
(drink size.small)
(make coffee))
(coffeepot.refill) #else
Note the usage of the do
macro above when multiple statements are required.
We can iterate through arrays and other iterables
sibilant> (each (value) [1 2 3 4 5]
(console.log value))
[ 1, 2, 3, 4, 5 ].forEach((function(value) {
/* eval.sibilant:1:0 */
return console.log(value);
}))
1
2
3
4
5
Since this is Lisp-like, all functions are done using prefix notation, where the operator (+, -, =, etc) come before the operands.
To put it all together, we could set up a tiny exercise.
Let’s say that the goal of the exercise is to take an array of strings and print a list of each distinct phrase and the number of occurrences in decreasing frequency. I will link to my answer so that you can think of how to do it or implement it without spoilers.
(def sort (input_strings)
;; Code goes here
)
;; Here we can test some edge cases
(sort ["single string test"])
(sort [])
(sort [1 1 2 3 4])
(sort ["a" "a" "c" "b" "c" "b" "e" "a" "z" "e" "e" "e" "e" "e"])
You can find the link to a solution that I implemented here and you can find more documentation at the official website.