Ale is a dialect of Lisp which means that it’s infinitely extensible out of the box. Hygienic macros and syntax quoting make this possible. But what if you really, really need to start making deep changes? Like at the compiler level? Well, I’ve designed that to be a relatively straight-forward process as well! 
 From the Ale REPL, type the word if (no parens) and you’ll see…
Ale is designed to be hosted within a Go process. And
while it’s not particularly difficult to embed or even
 extend Ale, getting data structures from Go into Ale (and vice
versa) has historically been a high-friction activity. There’s a reason for this
and it all comes down to equality. 
 Equality in Ale ≠ Equality in Go 
 Ale employs what’s called…
Ale is a Lisp dialect that takes syntactic cues from both Scheme and Clojure. 
 Where Scheme treats parentheses and square brackets as being one in the same. Ale treats them the way that Clojure does – meaning parentheses are used to delimit lists, while square brackets are used to delimit vectors. Ale takes advantage of this by incorporating the syntactic difference into its language…
When I first saw Lisp code, my immediate reaction was “Wow, that’s a lot of parentheses!” I then backed up from the computer, turned around, walked away, and didn’t look at it again for years. I had convinced myself that any language that doesn’t have rich grammatical productions and super clean syntax couldn’t possibly be of any value. But that’s because…
Functional programming languages introduce a different set of compilation problems than do imperative languages. They’re simpler in many ways, but when it comes to accommodating the functional style of problem solving, the compiler author has a duty to the programmer that cannot be ignored. 
 So where my Clojure people at? Fire up the Leiningen REPL, type the following code into it, and…

 
 
 
 
 This article can be considered obsolete. Ale no longer exposes its internal encoding interface. Instead, the recommended way to extend Ale is by using the (asm*) special form. See this article for more information. 
 
 
 Ale is a dialect of Lisp which means that it’s infinitely extensible out of the box. Hygienic macros and syntax quoting make this…
Ale is designed to be hosted within a Go process, but with a catch – each environment you create is isolated from the others, meaning namespace modifications are not shared. So what could have been as simple as calling eval.String("(+ 1 2 3)") suddenly becomes a few lines of code more complicated. 
 I should explain why this is. 
 Namespaces and Their Environments 
 Namespaces…
They say you’re not a great JavaScript programmer until you’ve written your own framework – hence all of the half-assed attempts. Similarly you can’t really say you understand programming language design until you’ve implemented your own Lisp. So here we are. 
 But why Lisp? The answer is straight-forward. Because even though over sixty years have come and gone…
Looks up method from target using get , then calls the resulting value as a procedure with any remaining arguments. It expands to a normal get followed by a call. 
 An Example 
 (: {:inc (lambda (x) (+ x 1))} :inc 41)
 
 This example returns 42 .
Calculates the sum of a set of numbers. 
 An Example 
 (+ 9 10 23) ;; returns 42
(+ 50 12.6 34.8) ;; returns 97.4
(+) ;; returns 0

Evaluates forms from left to right. As soon as one evaluates to false, will return that value. Otherwise, it will proceed to evaluate the next form. 
 An Example 
 (and (+ 1 2 3)
 false
 "not returned")
 
 Will return #f (false), never evaluating “not returned” , whereas: 
 (and (+ 1 2 3)
 true
 "returned")
 
 Will return the string…
Returns a new sequence with value appended to the end of seq . The target must satisfy appendable? . 
 An Example 
 (append [1 2 3] 4)
 
 This example returns [1 2 3 4] .
Evaluates the provided sequence and applies the provided function to its values and any explicitly included arguments. 
 An Example 
 (define x '(1 2 3))
(apply + x)
 
 This example will return 6 .
Provides direct access to Ale’s assembler syntax. This form is primarily used by the core library and low-level code that needs explicit control over VM instructions. 
 An Example 
 (asm
 const 99)
 
 This example emits code that returns the literal value 99 .
Returns a new mapper sequence wherein the specified key/value pairs are associated. If a key already exists, the value replaces the one previously stored; otherwise the pair is added to the sequence. 
 An Example 
 (define robert {:name "Bob" :age 45})
(assoc robert (:age . 46))
 
 This example returns a copy of robert wherein the value associated with :age has been replaced by…
A form is considered to be atomic if it cannot be further evaluated and would otherwise evaluate to itself. 
 An Example 
 (atom? '() :hello "there")
 
 This example will return #t (true) because each value is atomic. 
 Like most predicates, this function can also be negated by prepending the ! character. This means that all the provided forms must not be atomic. 
 (!atom?…
These macros build on if to cover common control-flow patterns. case compares a value using eq , and a clause test may be either a single value or a list of values. if-let binds a value and tests whether it is truthy. when-let is the body-only form of if-let . If no case clause matches, it raises an error. 
 An Example 
 (if-let [x (get {:name "ale"} :title false)]
 (str "hello "…
Creates a byte sequence from numeric values. Byte sequences also have reader syntax using #b[...] . 
 An Example 
 (bytes 65 66 67)
 
 This example returns the byte sequence representing ABC .
If all forms evaluate to byte sequences, this function returns #t (true). Otherwise it returns #f (false). 
 An Example 
 (bytes? (bytes 65 66) #b[1 2 3])

This function will return the first element of the specified pair or sequence, or the empty list if the sequence is empty. 
 An Example 
 (define x '(99 64 32 48))
(car x) ;; will return 99

(define y (100 . 200))
(car y) ;; will return 100

These are compound sequence accessors that combine multiple car and cdr operations. The name of each function describes which operations to perform from right to left. a represents car , which gets the first element. d represents cdr , which gets the rest of the list. 
 Examples 
 Given a nested list structure: 
 (define x '((1 2) (3 4)))
(caar x) ; gets car of (car x) ->…
This function will return the portion of a pair or sequence that excludes its first element. For sequences, this will be the remainder of the sequence. For cons pairs, this will be the cdr portion. 
 An Example 
 (define x '(99 64 32 48))
(cdr x) ;; will return (64, 32, 48)

(define y (100 . 200))
(cdr y) ;; will return 200

A channel is a data structure used to generate a lazy sequence of values. The result is a hash-map consisting of an emit function, a close function, and a sequence. Depending on the size of the channel’s buffer, retrieving an element from the sequence may block , waiting for the next value to be emitted or for the channel to be closed. Emitting a value to a channel will also block until the…
Returns a new function based on chained invocation of the provided functions, from left to right. The first composed function can accept multiple arguments, while any subsequent functions are applied with the result of the previous. 
 An Example 
 (define mul2Add5 (comp (partial \* 2) (partial + 5)))
(mul2Add5 10)
 
 This example will return 25 as though (+ 5 (\* 2 10)) were…
Creates a lazy sequence whose content is the result of concatenating the elements of each provided sequence. To immediately materialize a complete concatenated sequence, use the concat! function. 
 An Example 
 (seq->list (concat [1 2 3] '(4 5 6)))
 
 This will return the list (1 2 3 4 5 6)
For each pred-then clause, the predicate will be evaluated, and if it is truthy (not false), the then form is evaluated and returned, otherwise the next clause is processed. 
 An Example 
 (define x 99)

(cond
 [(< x 50) "was less than 50" ]
 [(> x 100) "was greater than 100"]
 [:else "was in between" ])
 
 In this case, “was in between” will be…
Like -> , but each form is paired with a test condition. The form is only applied if the test evaluates to true. Each test is evaluated with the current threaded value in scope. 
 An Example 
 (cond-> 10
 [true (+ 5)] ; Always applies: 10 + 5 = 15
 [(> 12) (\* 2)] ; Applies if > 12: 15 \* 2 = 30
 [(< 25) (/ 3)] ; Doesn't apply (30 is not < 25)
 [true (- 1)]) ; Always…
Like ->> , but each form is paired with a test condition. The form is only applied if the test evaluates to true. Each test is evaluated with the current threaded value in scope. 
 An Example 
 (cond->> [1 2 3 4 5]
 [seq? (map (lambda (x) (\* x 2)))] ; doubles: [2 4 6 8 10]
 [!empty? (filter even?)] ; keeps evens: [2 4 6 8 10]
 [(> 2) (take 3)] ; takes first 3: [2 4 6]
…
Adds elements to a conjoinable sequence. This behavior will differ depending on the concrete type. A list will prepend, a vector will append, while an object makes no guarantees about ordering. 
 An Example 
 (conj [1 2 3 4] 5 6 7 8)
 
 Will return the vector [1 2 3 4 5 6 7 8] .
When cdr is an ordered sequence, such as a list or vector, the result is a new list or vector with the car value prepended to the original. With an unordered sequence, such as an object array, there is no guarantee regarding position. If cdr is not a sequence, then a new cons cell will be constructed. 
 The name cons is a vestige of when Lisp implementations constructed new lists or cells by…
Returns #t if coll can resolve key , otherwise #f . This works with mapped lookup types such as objects and sets. 
 An Example 
 (contains? #{:name :age} :name)
 
 This example returns #t .
These names are predefined literal values. true and false are Ale’s boolean values. null is the empty list or null value. +inf , -inf , and nan are special floating-point values. 
 An Example 
 [true false null +inf -inf nan]

If all forms evaluate to a valid sequence than can report its length without counting, then this function will return #t (true). The first non-counted sequence will result in the function returning #f (false). 
 An Example 
 (counted? '(1 2 3 4) [5 6 7 8])
 
 This example will return #t (true). 
 Like most predicates, this function can also be negated by prepending the !…
Returns the system’s current time, measured in nanoseconds since January 1, 1970 UTC. 
 An Example 
 (current-time) ;; returns 1554720691499809478

Forward declares bindings. This means that the names will be known in the current namespace, but not yet assigned. This can be useful when two functions refer to one another. 
 The private variant makes the binding private to the current namespace. 
 An Example 
 (declare is-odd-number)

(define (is-even-number n)
 (cond [(= n 0) true]
 [:else (is-odd-number (- n…
Binds a value to a global name. All bindings are immutable and result in an error being raised if an attempt is made to re-bind them. This behavior is different from most Lisps, as they will generally fail silently in such cases. 
 An Example 
 (define x
 (map
 (lambda (y) (\* y 2))
 seq1 seq2 seq3))
 
 This example will create a lazy map where each element of the three…
Bind a function by name to the current namespace. 
 An Example 
 (define-lambda (fib i)
 (cond
 [(= i 0) 0]
 [(= i 1) 1]
 [(= i 2) 1]
 [:else (+ (fib (- i 2)) (fib (- i 1)))]))
 
 This example performs recursion with no tail call optimization, and no memoization. For a more performant and stack-friendly fibonacci sequence generation example, see the…
Binds a macro to a global name. The reader expands a macro to alter the source code’s data representation before it is evaluated. 
 An Example 
 (define-macro (cond . clauses)
 (when (seq clauses)
 (if (= 1 (length clauses))
 (clauses 0)
 (list 'ale/if
 (clauses 0) (clauses 1)
 (cons 'cond (rest (rest clauses)))))))

delay returns a promise that evaluates its body the first time it is forced. That result is cached, so later calls return immediately. force resolves one promise layer, or returns non-promises unchanged. force! keeps forcing until the result is no longer a promise. delay-force delays a computation whose result should be forced once before being cached. 
 An Example 
 (define p (delay
…
Returns a new mapper sequence wherein the associations identified by the provided keys are removed. If the keys don’t exist, the original sequence is returned. 
 An Example 
 (define robert {:name "Bob" :age 45})
(dissoc robert :age)
 
 This example returns a copy of robert from which the :age association has been removed. The original sequence is unaffected.
Calculates the collective quotient of a set of numbers. 
 An Example 
 (/ 10 3) ;; returns 10/3
(/ 10 3.0) ;; returns 3.3333333333333335
(/ 20 2.0 4) ;; returns 2.5

Return a lazy sequence that excludes the first count elements of the provided sequence. If the source sequence is shorter than the requested count, an empty list will be returned. 
 An Example 
 (define x '(1 2 3 4))
(define y [5 6 7 8])
(drop 3 (concat x y))
 
 This example will return the lazy sequence (4 5 6 7 8) .
If all forms evaluate to empty sequences, then this function will return #t (true). The first evaluation that is not an empty sequence will result in the function returning #f (false). 
 An Example 
 (empty? '(1 2 3 4) [] {})
 
 This example will return #f (false) because the first form is a list with four elements. 
 Like most predicates, this function can also be negated by…
If all forms evaluate to false ( #f ), then this function will return #t (true). The first non-false will result in the function returning #f (false). 
 An Example 
 (false? (< 3 2) (> 5 10))
 
 This example will return #t (true) because all the equalities result in #f (false). 
 Like most predicates, this function can also be negated by prepending the ! character. This means…
Creates a lazy sequence whose content is the result of applying the provided function to the elements of the provided sequence. If the result of the application is truthy (not false), then the value will be included in the resulting sequence. 
 An Example 
 (filter (lambda (x) (< x 3)) [1 2 3 4])
 
 This will return the lazy sequence (1 2)
Iterates over a sequence, reducing its elements to a single resulting value. The function provided must take two arguments. The first and second sequence elements encountered are the initial values applied to that function. Thereafter, the result of the previous calculation is used as the first argument, while the next element is used as the second argument. 
 An Example 
 (fold-left + 5…
These forms reduce a sequence from right to left. fold-right uses reverse , while fold-right! uses reverse! and can therefore work with non-reversible sequences by materializing them first. foldr is an alias for fold-right . 
 An Example 
 (fold-right cons '() [1 2 3])