Control Structures

What Are They?

So far, Elisp programs are all about setting variables, getting input, and spewing output. This is acceptable for only the most fundamental programs. We need some gears and moving parts to make programs work. We need to write programs that respond when an input is this or that, and we need to do it several times. Also, instead of copying and pasting code that does a particular job, we may want to make it into its own function so we can call the function rather than paste the function's code. Therefore, it's time for control structures.

A Word on Truth

Anyone who's touched a programming language knows that truth values are ambiguous. In C, anything that's non-zero is true. Java has its own boolean expressions for true and false. PHP has expected values of true based on type. It shouldn't surprise you, then, that Elisp has its own way of doing things. Anything non-nil is evaluated as true.

If...

One of the most important things a programming language can do is branch— if one expression is true, do one thing... otherwise, do something else. The easiest, most flexible way to do this in Elisp is cond. Here's an example; I've changed the colors of the matching parentheses to make the structure more obvious:

;Get some input:
(setq input (read-from-minibuffer "Type something: "))

;From the previous section, let's output whether input is a number:
(cond
( (string= input "0") (message "Yes.") )
( (> (abs (string-to-number input)) 0) (message "Yes.") )
( t (message "No.") )
)

To formalize this, after the word cond, there are a list of expressions in parentheses. Inside each element of this list are two expressions— if the first expression doesn't result in nil, the next gets evaluated and then execution jumps out of the cond statement. Otherwise, evaluation moves to the next element of the list. In the above example, "t" is simply a non-nil value, so if every other condition in the list is false, that one, at least, will be sure to be executed. In the parlance of other programming languages, it's our "else" block.

Another word about the example: why did I do it that way? User input is always handled as a string, even if it's a number. Also, string-to-number tries really, really hard to get a number out of a string, so it returns 0 when a non-number string is provided as a parameter. In that way, our example checks to see if it's zero, then it checks to see if the absolute value of the input is greater than zero. Otherwise, if it's not literally zero, and it's numerical value isn't greater-or-less-than zero, then string-to-number must have returned zero because its input wasn't a number. We didn't have to use numberp here at all.

That isn't to say numberp is useless. Elisp is "dynamically typed", so variables that start out as numbers might not stay that way. Consider this completely practical example (which also shows off some other control structures):

(setq yn (y-or-n-p "Set someValue to a string? "))

(setq someValue 4)

(when yn
(setq someValue "four")
)

(if
(numberp someValue)
(message "someValue is a number")
(message "someValue is not a number")
)

So, cond is far-and-away the most useful conditional statement, but there are shortcuts that are easier to read. In the above example, when simply takes two arguments: when the first is true (non-nil), it'll evaluate the second. if is similar, but with one more argument: when the first is true, evaluate the second, otherwise, evaluate the third. Lastly, but still worth mentioning, unless is the anti-when. It, too, takes two arguments, but the second argument is evaluated when the first argument is nil.

In Elisp, there are also and, or and not functions for combining conditions. If you're used to other programming languages, they work exactly the same except with Lisp's happier notation instead. If you haven't been tainted by other programming languages, examine this:

; Why was 6 afraid of varA?
(setq varA 7 varB 8 varC 9)

(when
(and (< varA varB) (< varB varC))
(message "varB is in the middle.")
)

If you think of conditional statements in their nil/non-nil form— that is, everything inside parentheses gets resolved to a value— then these functions work pretty simply: and reads its arguments (and there can be as many as you like) and if it sees no nils, it returns t. or works similarly, but returns t as soon as it sees a non-nil value.

In addition, there is a not function for inverting values. Passing a non-nil argument to not makes it nil and vice-versa. From the previous example:

(when (not (< varA varB))
(message "This won't get evaluated")
)

Looping

Common Lisp has a pretty ugly function for iteration called do. Feel free to look up documentation on this. Elisp has some added niceties, though, to make it a bit more intuitive for the likes of the Basic, C, or Java-heads. The most obvious type of loop is while:

(setq a 4)
(while (> a 0)
(message (concat "Test " (number-to-string a)))
(setq a (- a 1))
)

So, the form takes two or more arguments just like when, except the body of the loop keeps being executed while the first argument isn't nil. The above example will put messages in the message buffer starting with "Test 4" and ending with "Test 1".

The cousin to the while loop— which appears in every programming language since BASIC— is a "for" loop. Elisp calls it something different. In this case, it makes more English-sense. Let's look at a trivial, almost familiar example of dotimes:

(dotimes (i 4)
(message (concat "Test " (number-to-string i)))
)

This will count up from "Test 0" to "Test 3". i is called the loop's "iterator". dotimes sets it to zero, then evaluates the loop the number of times specified by the second element of the first argument... in this case, 4. There's one more optional argument here. Lispers generally hate "side-effects" (like setting some value outside the loop from inside the loop), so what if we wanted to have our dotimes loop return a value?

(setq twoI (dotimes (i 4 2*i)
(message (concat "Test " (number-to-string i)))
)
)

In the above example, twoI will equal 8 after the expression is evaluated. This can be pretty useful because we can mess with the value of the iterator inside the loop body to get it to execute early with weird values.

Defining Functions

Just like dotimes can return a value, we'll need an algorithm to process some data and churn out some value. The idea of a programming function comes directly from math. Given some inputs, let a function produce an output. Everything in Elisp operates like a function (even if it's technically a "macro" or "special form"), so it's about time we learned to write our own. The syntax is something like this:

(defun funcName (arguments) "Document string"
; Document body...
)

After evaluating this, Emacs will now have your function available to it for use elsewhere in your program. There's a special "gotcha"— if you want your function available to end-users (or available for key-bindings), you must call (interactive) somewhere in your function. Traditionally, this is done in the first line.

I'm not familiar with the rules surrounding function names, but be reasonable. They shouldn't have spaces, and use only numbers, letters, dashes, and underscores. arguments is a space-separated list of variable names. Special keywords &optional and &rest can let you make variable-length arugment lists. We've used these in Elisp without really thinking about. Docstrings are &optional, and cond uses &rest to accept any number of arguments. Order is important here, too. It'll make sense with experience, but optional arguments come after required ones, and &rest arguments have to come at the end.

There are no specifications on what type of input is recieved for each parameter because, remember, this is a dynamically-typed language. The document string is what will appear in the documentation of a user does C-h f for your function. This string is also available programmatically through (documentation 'funcName). If you look at documentation strings for built-in functions, you'll find that they're often more than just a few words. This is a single string. A function call won't be executed. If you need more than one line, close the quotes, hit enter, open a new set, and keep typing.

Calling your function is exactly like calling any built-in Elisp function:

(funcName arg1 arg2 ...)

Returning values are usually done by simply writing what you want to return on a line by itself since a value simply returns itself. Let's look at some examples, because most programs of any reasonable complexity will use some defuns.

(defun mb-destroy-ship (whichone &optional splashdamage)
"Prints the randomized sunder-message for ship called whichone.
Also, optionally does splashdamage back to the player's ship."
(let
((a (random 3)))

(cond
( (= a 0) (message (concat "The " whichone " is destroyed by your fiery rockets!")) )
( (= a 1) (message (concat "The treacherous " whichone " sunders under the pressure of your mighty ion cannon before evaporating into metallic mist!")))
( (= a 2) (message (concat "Hideously spinning and tearing apart, the " whichone " is transforming into a wrecked mockery of her prior glory!")))
)
)
(when (numberp splashdamage)
(setq main-ship-hp (- main-ship-hp splashdamage))
)
)

Now, every time I destroy a ship, I don't have to worry about copy/pasting the same code over and over to get randomized strings... just simple calls to:

(mb-destroy-ship "Argo")
(mb-destroy-ship "Thunder Child" 20) ; Splash damage discourages this
(mb-destroy-ship "Dawn Treader")

Using a couple letters at the start of your function name followed by a dash is a considerate way to make sure your functions don't replace functions loaded by other Elisp scripts.

Lambda

defun isn't the only way to create Elisp functions. I've mentioned variable types in previous sections— integers, decimals, and strings mainly. Functions can be variables, too. This was an original feature of Lisp that made it unique among the languages... something the C-class languages missed, including Java until very recently. These functions are called anonymous functions or, more commonly, lambda functions (after the Greek letter λ by Alonzo Church in his rather-famous mathematical works on lambda calculus).

We've seen this once in the I/O segment where we introduced buttons and needed a quick-and-dirty unnamed function. For folks coming into this from an object-oriented background, data structures that feature a function as one of their elements leads to very elegant polymorphism in Elisp. For the rest of us, these are especially handy in lists and arrays introduced in the next section. Let's look at generating and calling anonymous functions here, though:

(setq welcomeTo
(lambda (where) "Unnecessary example docstring!"
(goto-char (point-max))
(insert (concat "Welcome to... " where)) ))

Lambda functions assigned to a variable can be accessed with funcall... short for function call. Really original. Used in this context, though, it isn't much different from defun. Take a look:

(funcall welcomeTo "Funky Town")

This shows off function arguments (where) and the inner docstring. All that is optional. Where would you use this instead of defun is probably the most glaring question. This is what's eye-opening. In the above example welcomeTo is a variable! You can change it at any time and future times that funcall gets evaluated, the behavior will change accordingly. When used in conjunction with another function (read-from-string, mentioned in section 6), one can see why Lisp-derived languages are often used for artificial intelligence: code that can write and alter itself.

Progn

Imagine if the when function only evaluated the first statement following the condition and then spat out errors for subsequent function calls... or if any procedural kind of function mentioned in this section behaved like that. You can explictly tell Elisp to evaluate a bunch of statements in the order they're written with progn. It's a little vestigial, but worth mentioning. In Part 6, I mention and show how to construct a Read-Eval-Print Loop (REPL). The only way to daisy-chain Lisp statements together into one form is with progn. Also, being familiar with it comes in handy if you move onto Scheme, a minimalistic Lisp programming language. It's interesting to note that progn returns the last value passed to it.

(let ((i 5))
(while (> i 0)
(progn
(message (concat "Tee minus " (number-to-string i)))
(setq i (- i 1))
)
)
)

The above trivial example reduced the number of arguments to while down to two, for what it's worth... and while the entire let statement returns nil, the progn statement returns 0 (the one minus one that makes the while condition false to stop the loop from evaluating forever).

"Well, what's the difference between progn and lambda?"

If I use setq to set some value to a lambda function, I can call that function later... the function itself is assigned to the variable. When I use progn in a setq statement, I'm just setting the value of the variable to the value returned by progn.