On this page:
12.1 Expressions
12.2 Evaluating an expression
12.2.1 List expressions
12.2.2 Quoted expressions
12.3 Functions
12.4 Applying operators to operands
12.5 Truth and falsehood
12.6 Conditional evaluation
12.6.1 if
12.6.2 case
12.7 Temporary names using let
12.8 Recursive functions

12 A Gentle Introduction to muSE

muSE stands for muvee Symbolic Expressions - a scripting language used in the specification of muvee Reveal’s styles. A style package’s data.scm file is a muSE script that decides what the various aspects of a style should be.

This document introduces muSE to those who are may not be familiar with it or the language Scheme on which it is based. It is intended to be a gentle as well as a quick introduction to the underlying scripting language.

For detailed information about muSE, see its project page.

We strongly recommend that you use the DrScheme program to view and edit muSE files. You can use DrScheme to try out the expressions that we present in this quick tutorial.

12.1 Expressions

A muSE data file typically ends with the extension .scm for “Scheme”. However, we like to call them “scum” files. A scm file consists of a sequence of muSE expressions which are evaluated in the order in which they appear.

An expression can be a simple thing such as -
  • simple decimal numbers : 1, 2, -15, 355, 113, etc.

  • fractional numbers : 1.2, 3.1415, -100.0, etc.

  • hexadecimal numbers : 0xbabe, 0xF00D, etc.

  • strings : "Hello muSE", "Louis sang 'What a wonderful world!'", etc.

  • symbols : mumboJumbo, MuveeTechnologies, nuclear-cockroach, dont-sw^%$#-in-code?, etc. Symbols start with a letter and can contain any character other than quotes, brackets, and period.

  • lists : A list of items is notated within parentheses. For example -
    • (1 2 3 4 5)

    • (1 "He" 2 "HeHee" 3 "HeHeHeee")

    • (Name "Willy Wonka" Age 30 Height 5.8)

    Lists can consist of simple data such as shown above or consist of other lists. Here is an example of a list that mixes them up -
    ("List demo" 1 2.34 five
           (Name "Willy Wonka")
           (Age 30)
           (Height 5.8))

12.2 Evaluating an expression

12.2.1 List expressions

List expressions are evaluated using a special method -
  • The first item in the list is evaluated to determine an operator

  • The operator is then applied to the rest of the list in order to derive the value of the expression.

For example, when

(+ 1 2 3 4)

is evaluated, the symbol + is interpreted as the operator that performs addition and this addition operator is applied to the rest of the list

(1 2 3 4)

in order to obtain the value

10

So the value of the expression (+ 1 2 3 4) is 10.

The rest-of-the-list can itself consist of other expressions which are evaluated to determine intermediate values. For example, the expression

(/ (- 100 30) (+ 100 30))

computes the value (100-30) / (100+30). You can determine the result of the above expression using the following sequence of reduction steps -
(/ (- 100 30) (+ 100 30))
(/ 70 (+ 100 30))
(/ 70 130)
0.538462

muSE includes a host of built-in operators that perform various computations on their operands. It also includes facilities to define your own operators within muSE itself (see [#Functions] below).

12.2.2 Quoted expressions

If you do not wish to interpret the first item of a list as an operator, you can quote it using the single quote character as follows -

'(+ 1 2 3 4)

The above is a literal list of 5 items where the first item is the symbol +, and the rest of the items are the numbers 1, 2, 3 and 4 respectively.

You may read the single quote in the above expression as the word “literally”, so that the above expression becomes -

Literally the list of items +,1,2,3,and 4.0

You can also quote symbols to prevent them from evaluating to their defined values as follows -

'pie

will always be the symbol pie even if you had defined it to a numerical value like this -

(define pie 3.141592654)

On the other hand, if you just use the symbol pie, it will be substituted by the defined value 3.141592654.

Hence, you use the quoting to escape evaluation.

12.3 Functions

Suppose we wish to evaluate several expressions with similar forms, such as these -
(/ (- 100 30) (+ 100 30))
(/ (- 10 5) (+ 10 5))
(/ (- 31 7) (+ 31 7))
(/ (- 355 113) (+ 355 113))

You notice first that only two numbers are involved in the expressions above, so the expressions all have the general form

(/ (- a b) (+ a b))

which computes (a-b) / (a+b).

You can define a function that will expand to the full expression when given two numbers, as follows -
(fn (a b)
    (/ (- a b) (+ a b)))
(Notice the carefully matched parentheses.)

Since all expressions are ultimately values in muSE, the above function expression is itself a value, so we can give it a name using define as follows -

(define f (fn (a b) (/ (- a b) (+ a b))))

After the above definition is evaluated, the symbol f now stands for the function (fn (a b) (/ (- a b) (+ a b))). So we can simplify our original expressions to -
(f 100 30)
(f 10 5)
(f 31 7)
(f 355 113)
respectively.

* Note that we use the terms function and operator interchangeably.

The expression immediately following the symbol fn is a list of unknowns which will be bound to particular values when using the function. In the above example, the list of unknowns is (a b). Thus when evaluating the expression

(f 31 7)

the list of unknowns

(a b)

is compared with the list of knowns

(31 7)

to determine the values that the unknowns must be bound to. The effect of such a binding is to replace the unknown symbols with their bound values throughout the body of the function, resulting in the following expression -

(/ (- 31 7) (+ 31 7))

That’s what we want.

12.4 Applying operators to operands

If the operands to be passed to an operator are available as the value of another symbol and not explicitly at the time of writing the expression, the operator can be applied using the apply operator. This is possible because operators (aka functions) themselves are values that can be operands to other higher order operators.

For example, lets assume we have the numbers from one to ten given as a list and the bound to the symbol one-to-ten -

(define one-to-ten '(1 2 3 4 5 6 7 8 9 10))

If we want to add the numbers 1 to 10, we’ll have to write -

(+ 1 2 3 4 5 6 7 8 9 10)

, but since we only have the list of numbers as the value of a symbol, we need to apply the operator + to the value of the symbol one-to-ten as follows -

(apply + one-to-ten)

The above apply expression will evaluate correctly to 55.

The relationship between apply and eval can be summarized as -

(eval x) = (apply (eval (first x)) (map eval (rest x)))

You can imagine the apply operator to work like this -
(apply + one-to-ten)
(apply + '(1 2 3 4 5 6 7 8 9 10))
(eval '(+ 1 2 3 4 5 6 7 8 9 10))
(+ 1 2 3 4 5 6 7 8 9 10)
55

12.5 Truth and falsehood

muSE uses the empty list () to represent falsehood. Any other value can be used to represent truth. This convention is used in evaluating comparison expressions such as -
(> 3 2)
(< 30 13)
(>= 5.3 3.5)
(and (>= 5.3 3.5) (< 314 355))
(or (= 2 3) (= 3 2))

The symbol T is commonly used by such comparison operators to represent truth.

12.6 Conditional evaluation

12.6.1 if

The expression

(if condition yes-value no-value)

evaluates to yes-value if the condition evaluates to something other than the empty list, otherwise it evaluates to no-value.

For example -
(if (< 2 3)
    "muSE knows numbers"
    "muSE doesn't know numbers")
will always evaluate to the string - "muSE knows numbers" because the expression (< 2 3) will always evaluate to T.

We can use the if conditional in our example function body to compute an absolute fraction - |(a-b)/(a+b)| as follows -
(define f
        (fn (a b)
            (/ (if (< a b)
                   (- b a)
                   (- a b))
               (+ a b))))

If a and b are known to be positive numbers, then the expression -
(if (< a b)
    (- b a)
    (- a b))
is guaranteed to be >= 0.

Notice how we’re using the fact that if expressions evaluates to a single value and is not a control statement like in other languages.

12.6.2 case

When you need to check for a few discrete values of a given expression, case proves effective. For example -
(case N
    (1 "one")
    (2 "two")
    (3 "three"))
will evaluate to "one", "two", "three" or () (the empty list) depending on whether N evaluates to 1, 2, 3, or some other value.

12.7 Temporary names using let

It is often the case that the value of a complex sub-expression is needed in several places in an expression. Either that, or we may wish to give the value of a sub-expression a name for the sake of clarity in reading the expression. For example, the sub-expression to compute the absolute difference of two numbers -

(if (< a b) (- b a) (- a b))

in our example function can be named difference. We introduce such local definitions using the let notation as follows -
(fn (a b)
    (let ((difference (if (< a b)
                          (- b a)
                          (- a b)))
          (sum (+ a b)))
         (/ difference sum)))

The let notation has the general form -
(let ((name1 value1)
      (name2 value2)
      ....
      (nameN valueN))
  expression)
where expression makes use of the newly introduced name1, name2, etc.

12.8 Recursive functions

The term recursion is used to talk about ways to inductively specify a computation on some data in terms of a computation on a subset of the data. (That’s a simplified view, but it is sufficient for introductory purposes.)

For example, say we want to add up all numbers from m to n, we can describe the calculation as follows -

To calculate the sum of numbers from m to n, you add m to the sum of numbers from m + 1 to n, unless m is greater than n, in which case the sum is taken to be 0.

Note that sum of numbers is specified in terms of itself. This is characteristic of recursive or inductive specifications. You can write such a summing function in muSE as follows -
(define (sum-of-numbers m n)
  (if (> m n)
      0
      (+ m (sum-of-numbers (+ m 1) n))))

You can also define two functions in terms of each other. In such a case, the functions are said to be mutually recursive. For example, the even and odd functions below are defined mutually recursively.

(define (even n)) ; Declare even function
(define (odd n)) ; Declare odd function
 
(define (even n)
  (if (= n 0)
      'yes
      (odd (- n 1))))
 
(define (odd n)
  (if (= n 0)
      'no
      (even (- n 1))))

The above code says “ a number is even if its predecessor is odd and a number is odd if its predecessor is even”. The definitions work for all n >= 0.