hestia is a small, functional language built for scripting. This is a short, incomplete walkthrough of the language. Feel free to try things in the REPL below:
hestia has no user-defined data types. While this limits expressive power, it also limits complexity. Here are the current data types in hestia, with examples:
145, 0, -58212
145.0, 0.18248, -58212.888
"hello", "", "---\n----"
'aaaa, 'test-symbol, 'HELLO
true, false
[], [1 2 3], [true 'a [1]]
{}, {a: 23}, {'a: 23}, {2: "a" "test": true}
{|x| (add 1 x)}, {||}, {|a b c| (add a b c) }
(none), (some 123), (some [1 2 true])
Note that commas in hestia are whitespace, so (eq? 1000 1,000)
, (eq? {a: 1 b: 2} {a: 1, b: 2})
, and (eq? [1, 2, 3] [1 2 3])
all evaluate to true
.
Also note that {a: 1}
is syntactic sugar (shorthand) for {'a: 1}
, a map that contains one key-value pair: from the symbol 'a
to the integer 1
.
All data structures in hestia are immutable.There are no functions that modify data structures in-place. Instead, functions return NEW data structures.
To globally name a value, use (def x 100)
. Later code can then reference x
.
To locally name a value, use (let ([x 100]) x)
. Only code within the let
expression can reference such names.
Global functions are just named functions, e.g., (def add1 {|x| (add 1 x)})
Probably the most important functions in hestia are lmap
and lreduce
. The l
stands for list. For instance, to add 1 to every integer in a list, you call (lmap add1 [1 2 3])
which evaluates to [2 3 4]
. lmap
takes a function and a list as its arguments, and is defined as follows:
(def lmap { |f list|
(if (empty? list)
[]
(cons (f (first list))
(lmap f (rest list))))})
Let's break this down. First, an (if test then else)
expression checks if test
is true
and if so, evaluates to then
; if false, it evaluates to else
.
Thus, lmap
first checks if the given list is empty, and if so, it simple returns an empty list. If not, it calls the given function on the first element of the list, and then recursively calls lmap
on the rest of the list. It then uses cons
, which takes a value and a list and returns a new list with that value prepended to the list, to return a new updated list.
However, all functions in hestia are automatically curried. To show what this means, consider that we could write the adding lmap
equivalently as follows: (lmap (add 1) [1 2 3])
because add
requires at least 2 arguments---when it receives 1 argument, it returns a function that takes (at least) 1 argument and adds 1 to it.
To sum all integers in a list, use (lreduce 0 add [1 2 3])
. lreduce
takes a "base" value, a function, and a list. Here's the source:
(def lreduce { |acc f list|
(if (empty? list)
acc
(lreduce (f acc (first list)) f (rest list)))})
In our case, lreduce
adds "pairs" of integers, with the first pair being 0
and 1
, the second pair being the sum of the first pair and 2
, and so on.
© semaj 2023