Defining & naming things

While a lot can be achieved by simply writing and using expressions directly in the code, sooner or later the ability to assign names to values and signals becomes an indispensable ally, helping us structure and organise our thoughts and code.

Naming things allows us to easily reference them in many different parts of the code, so that any changes made to them get automatically propagated to every other part of the code that uses them. It also encourages us to come up with useful and informative names that make it easier to remember, understand, and reason about our thought process and the resulting code.

In ModuLisp, there are four main ways of defining and naming things: define/def, defun/defn, set, and let. They all behave slightly differently, so understanding these differences is important in deciding which one is more appropriate to be used in any given situation.

define <name> <expression> (alias: def, variation: defs)

Parameter Description Type
name The name to assign to the expression Symbol
expression The expression to associate with the name Any

Creates a new signal definition, essentially assigning a name to a (potentialy time-varying) expression. These definitions are accessible from anywhere in the code, unless they are shadowed by a local binding with higher precedence (e.g. when inside a let expression body).

(define my-number-three (+ 1 2))

(+ my-number-three 1) ;; => 4

Variables created with define will always have the value that their expression evaluates to at the time of usage, not just at the time of definition.

(define a 1)
(define b 2)
(define my-number-three (+ a b))

;; Check the value first
my-number-three ;; => 3 - as expected

;; Let's Redefine one of the variables used in its definition
(define a 101)

;; Check the value again
my-number-three ;; => 103 - it's been updated!

TIP: if you need to find out the definition of a variable that was created by define, you can use get-expr.

A variation by the name defs works the same way, but can be used to define multiple pairs of names and expressions at the same time:

(define my-ramp          (slow 2 beat))
(define my-inverted-ramp (- 1 my-ramp))

;; the two expressions above are equivalent to this one:

(defs my-ramp          (slow 2 beat)
      my-inverted-ramp (- 1 my-ramp))

set <name> <expression>

Parameter Description Type
name The name to assign to the expression’s current value Symbol
expression The expression to evaluate Any

Creates a new static definition, essentially assigning a name to the value that an expression evaluates to at the moment that the definition is executed. These definitions are accessible from anywhere in the code, unless they are shadowed by a local binding with higher precedence (e.g. when inside a let expression body).

(set my-current-point-in-bar bar) ;; => stores the current 

;; Let's compare the value that `bar` had at the moment
;; of definition with the value that it has now:

(= bar my-current-point-in-bar) ;; => most likely false, as time has moved on

TIP: A handy mnemonic trick to remember the difference between set and define is “set in stone”: set will evaluate its expression only once and then set its value in stone, so that the name always refers to the same value, even if later on the expression evaluates to something different (either as a result of time-varying behaviour or simply by being redefined).

(set a 1)
(set b 2)
(set my-number-three (+ a b))

;; Check the value
my-number-three ;; => 3 - as expected

;; Change one of the variables used in its definition
(set a 99999)

;; Check the value again 
my-number-three ;; => 3 - nothing has changed!

defun <name> <params> & <body> (alias: defn)

Parameter Description Type
name The name of the function Symbol
params A list of the function’s parameter names List/Vector of Symbols
body The body of code to run when called Any

Creates and names a function. The body can be any number of forms, and the function’s return value will always be the return value of the last expression.

(defun my-no-args-function []
  (println "Hello from inside the function!"))
  
(my-no-args-function) ;; => prints the message

;; if the function name is long, you can put each of the other arguments
;; on their own lines

(defn my-addition-function 
  [a b] 
  (+ a b))

(my-addition-function 3 5) ;; => 8

let <bindings> & <body>

Parameter Description Type
bindings A list of binding pairs List/Vector of pairs of symbols and expressions
body Any number of expressions to evaluate within the local context Any

Creates a number of locally-named variables that are only accessible within the body of the let expression.

(let [a 1
      b 2]
  (+ a b)) ;; => 3

Local variables can also be defined in terms of other local variables defined earlier in the let:

(let [a "Hello"
      b (str a ", World!")]
  (println b)) ;; => "Hello, World!"

If any of the names clash with variables that have been named outside the let expression, the local names will “shadow” (i.e. overwrite) the external ones:

(define a 10000)
(define b 99999)
(define c 10)

(let [a 1
      b 2]
  (+ a b c)) ;; => 13

Working with lists

NOTE: since lists and vectors are so similar, the terms are often used interchangeably.

list <args>

Parameter Description Range
args Any number of values any

Creates a list containing the given arguments.

(list 1 2 3) ; => (1 2 3)

(list 'a 'b 'c) ; => (a b c)

NOTE: list will evaluate its arguments, unlike quote, which is often the desired behaviour:

(list 1 2 (+ 1 2)) ;; => (1 2 3)
(quote (1 2 3))           ;; => (1 2 3)
'(1 2 3) ;; => (1 2 3)

;; but, here the addition does not get evaluated first:
'(1 2 (+ 1 2) ;; => (1 2 (+ 1 2))

vec <args>

Parameter Description Range
args Any number of values any

Creates a vector containing the given arguments.

(vec 1 2 3) ; => [1 2 3]

(vec 'a 'b 'c) ; => [a b c]

index <list> <index> (alias: nth)

Parameter Description Range
list A list of values any
index The index of the element to retrieve integer

Returns the element at the given index in the list.

(index [1 2 3] 1) ; => 2
(index '(a b c) 0) ; => a

first <list> (alias: head)

Parameter Description Range
list A list of values any

Returns the first element of the list.

(first [1 2 3 4]) ;; => 1

(first '(a b c d)) ;; => a

rest <list> (alias: tail)

Parameter Description Range
list A list of values any

Returns all but the first element of the list.

(tail [1 2 3 4]) ; => [2 3 4]
(tail '(a b c d)) ; => (b c d)

zeros <length>

Parameter Description Range
length The number of zeros to generate any integer >= 0

Creates a list of zeros of the specified length.

(zeros 5) ; => [0 0 0 0 0]
(zeros 0) ; => []

range <low> <high>

Parameter Description Range
low The lowest number in the series any integer
high The highest number in the series any integer

Create a vector of numbers from low to high.

(range 1 3) ; => [1 2 3]
(range -10 0) ; => [-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0]

insert <list> <position> <value>

Parameter Description Type
list The list to insert in List/Vector
position Where in the list to put the value any integer >= 0
value The value to insert Any

Inserts a value in a specific position in the list.

(insert '(a b c) 1 "hi") ;; => (a "hi" b c)
(insert [0 1 2 3] 3 0.5) ;; => [0 1 2 0.5 3]

remove <list> <index>

Parameter Description Type
list The list to remove from List/Vector
index The index of the item to remove any integer >= 0 and <= list length

Removes from the list the value at the specified index (if it exists).

(remove '(a b c) 0) ;; => (b c)
(remove [0 1 2 3 4] 2) ;; => [0 1 3 4]

len <list>

Returns the length of a list or vector.

(len [1 2 3 4 5]) ;; => 5

push <list> <value>

Parameter Description Type
list The list to push to List/Vector
value The value to push any

Pushes a value to the top of a list.

(push '(b c d) 'a) ;; => (a b c d)
(push [1 2 3] 5) ;; => [5 1 2 3]

pop <list>

Removes the last/top element of the list (non-destructive)

Parameter Description Type
list The list to pop a value from List/Vector
(pop [812 38 92 99]) ;; => 99

(pop '(X Y Z)) ;; => Z

(define x [1 2 3])
(pop x) ;; => 3
(println x) ;; => [1 2 3]

slice <list> <from> <to>

Parameter Description Type
list The list to push to List/Vector
from Index of the first value in the slice Integer
to Index of the value following the end of the slice Integer

Extract a subset of a list or vector.

This supports negative indexing, where negative indexes are counted backwards from the end.

(slice [1 2 3 4 5] 0 3) ;; => [1 2 3]
(slice '(1 2 3 4 5) 0 -1) ;; => (1 2 3 4)
(slice [1 2 3 4 5] 1 -3) ;; => [2]
(slice [1 2 3 4 5] -3 -1) ;; => [3 4]

flatten <list>

Parameter Description Type
list The list to push to List/Vector

Take a list with multiple dimensions and return a single dimensional list of the value, in sequence.

(flatten [[1 2] 2 3 [1 2 3]]) ;; => [1 2 2 3 1 2 3]
(flatten '(0 1 '(2 3))) ;; => (0 1 2 3)
(flatten [[0 [1 [3 [4 5]]]]]) ;; => [1 1 3 4 5]

Functional Programming

lambda <params> & <body> (alias: fn)

Parameter Description Type
params A list of the function’s parameter names List/Vector of Symbols
body The body of code to run when called Any

Creates an anonymous function that’s ready to use. It works more-or-less exactly the same as defun/defn, except it doesn’t expect a name.

TIP: lambda/fn is often used with higher-order functions, such as map, to create a local/temporary function:

(map (lambda [x] (* x x)) 
  [1 2 3 4]) ; => [1 4 9 16]

map <function> <list>

Parameter Description Range
function A function to apply to each element function
list A list of values any

Applies a function to each element of a list and returns a list of the results.

(map (fn [x] (* x x)) 
  [1 2 3 4]) ; => [1 4 9 16]

filter <test function> <list>

Parameter Description Range
test function A function to apply to each element 1-arg Function that returns a Bool
list A list of values Any

Filters a list using a single-argument function that should return true or false. Any value that’s in the input list and for which the test function returns false will be excluded from the resulting list.

(filter (fn [x] (> x 2)) 
  [1 2 3 4]) ; => [3 4]

reduce <function> <list>

TODO

Evaluation control

do & <expressions>

Parameter Description Range
args Any number of expressions any

Evaluates the given arguments in order and returns the value of the last argument.

(do (print "Hello") 
    (print "World!") 
    (+ 1 2)) ;; => 3

TIP: This is useful for applying changes to a many outputs at the same time:

(do
  (d1 (sqr bar))
  (d2 (sqr (slow 2 bar))))
 
;; silence all analog outs
(do
  (a1 0)
  (a2 0)
  (a3 0))

if <test> <then> <else>

Parameter Description Range
test An expression that evaluates to either true or false any
then An expression to evaluate if the test returns true any
else An expression to evaluate if the test returns false any

Conditionally executes one of two branches depending on the outcome of a boolean test.

(if (> 3 2)
  (println "Three is greater than two")
  (println "Two is greater than three!?!?"))

for <name> <list> & <body>

Parameter Description Type
name The name to assign to each value Symbol
list The list of values to iterate over List/Vector
body Any number of expressions any

Iterates over a list of values, assigns each value to a name, and then evaluates a number of expressions in which that name can be used to refer to each of the iterated values.

(for my-number [1 2 3]
  (println "My number is: ")
  (println my-number)
  my-number) ;; => prints each number in order, returns 3

while <condition> & <body>

Parameter Description Type
condition Any expression that evaluates to a boolean Bool
body Any number of expressions to evaluate any

Executes a number of expressions repeatedly until the condition evaluates to false.

(set i 0)

(while (< i 10)
  (println "i is now = ")
  (println i)
  (set i (+ i 1)))

Arithmetic & Maths

+ [<numbers>] (alias: sum)

Returns the sum of all its arguments, which are expected to evaluate to numbers.

(+ 1 2 3) ;; => 6

- <value1> <value2>

Subtracts the second number from the first.

Parameter Description Range
value1 The value to subtract from number
value2 The value to subtract number

* [<numbers>]

Returns the result of multiplying all of its arguments, which are expected to evaluate to numbers.

(* 2 2 2) ;; => 8

/ <value1> <value2>

Divides the first value by the second value.

Parameter Description Range
value1 The value to be divided number
value2 The value to divide by (non-zero) number

% <value1> <value2>

Parameter Description Range
value1 The value to be divided number
value2 The value to divide by number

Calculates the remainder of the division of the first value by the second value.

floor <value>

Parameter Description Range
value The value to round down number

Rounds a number down to the nearest integer.

ceil <value>

Parameter Description Range
value The value to round up number

Rounds a number up to the nearest integer.

= <value1> <value2>

Parameter Description Range
value1 The first value to compare any
value2 The second value to compare any

Checks if two values are equal.

!= <value1> <value2>

Parameter Description Range
value1 The first value to compare any
value2 The second value to compare any

Returns true if the two values are not equal, false otherwise.

(!= 1 2) ; => true

(!= 'a 'a) ; => false

> <value1> <value2>

Parameter Description Range
value1 The first value to compare any
value2 The second value to compare any

Checks if the first value is greater than the second value.

< <value1> <value2>

Parameter Description Range
value1 The first value to compare any
value2 The second value to compare any

Checks if the first value is less than the second value.

>= <value1> <value2>

Parameter Description Range
value1 The first value to compare any
value2 The second value to compare any

Checks if the first value is greater than or equal to the second value.

<= <value1> <value2>

Parameter Description Range
value1 The first value to compare any
value2 The second value to compare any

Checks if the first value is less than or equal to the second value.

usin <phasor>

Parameter Description Range
phasor A value between 0 and 1 0-1

Generates a unipolar sine wave from the given phasor.

(usin 0.5) ; => 1.0

(usin 0.25) ; => 0.8535533905932737

floor <number>

Parameter Description Range
number A floating-point number any

Rounds a number down to the nearest integer.

(floor 3.7) ; => 3

(floor -1.2) ; => -2

ceil <number>

Parameter Description Range
number A floating-point number Any

Rounds a number up to the nearest integer.

(ceil 3.7) ; => 4

(ceil -1.2) ; => -1

scale <in min> <in max> <out min> <out max> <val>

Parameter Description Type
in-min The minimum input range Number
in-max The maximum input range Number
out-min The minimum output range Number
out-max The maximum output range Number
val The value to scale Number

Scales a value from one range to another.

(scale 0 1 0 100 0.5) ;; => 50.0
(scale 0 100 0 1 25) ;; => 0.25

scale <out min> <out max> <val>

Parameter Description Type
out-min The minimum output range Number
out-max The maximum output range Number
val The value to scale Number

Scales a value from one range to another, assuming val is in the range 0 to 1

(scale 0 100 0.5) ;; => 50.0

bi->uni <value> (alias: b->u)

Parameter Description Type
value The bipolar value to convert Number

Converts a bipolar value (-1 to 1) to a unipolar value (0 to 1).

(bi->uni -1) ;; => 0.0
(bi->uni 0) ;; => 0.5
(bi->uni 1) ;; => 1.0

uni->bi <value> (alias: u->b)

Parameter Description Type
value The unipolar value to convert Number

Converts a unipolar value (0 to 1) to a bipolar value (-1 to 1).

(uni->bi 0) ;; => -1.0
(uni->bi 0.5) ;; => 0.0
(uni->bi 1) ;; => 1.0

pow <exponent> <value>

Parameter Description Type
exponent The exponent value Number
value The base value Number

Returns the result of raising the base to the power of the exponent.

(pow 3 2) ;; => 8.0
(pow 2 5) ;; => 25.0

sine <value> (alias: sin, variation: usine/usin)

TODO

cosine <value> (alias: cos, variation: ucosine/ucos)

TODO

tan <value>

TODO

abs <value>

TODO

min <value>

TODO

max <value>

TODO

sqrt <value>

TODO

Utility functions

range

Parameter Description Type
low The starting value of the range integer or float
high The ending value of the range integer or float

Generates a list of numbers from the low value up to (but not including) the high value.

(range 0 5) ; => [0 1 2 3 4]

(range 1 4) ; => [1 2 3]

IO

dw <pinNumber> <value>

Parameter Description Range
pinNumber The pin number to write to integer
value The value to write (HIGH or LOW) integer

Writes a HIGH or LOW value to a digital pin.

(dw 13 1) ; => sets pin 13 to HIGH

(dw 13 0) ; => sets pin 13 to LOW

dr <pinNumber>

Parameter Description Range
pinNumber The pin number to read from integer

Reads the value from a specified digital pin.

(dr 13) ; => returns the value from pin 13 (HIGH or LOW)

perf

Reports the most recent performance metrics.

(useq_perf) ; => prints performance metrics

Metaprogramming

eval <expression>

Parameter Description Type
expression The expression to evaluate Any

Evaluates an expression at runtime, allowing for dynamic execution of code.

(eval (+ 1 2 3)) ;; => 6

type <expression>

Parameter Description Type
expression The expression to get the type of Any

Returns the type of the given expression.

(type 42) ;; => Number

get-expr <name>

Parameter Description Type
name The name whose definition to return Symbol

Returns the expression that was used to define a variable.

NOTE: this will only return something if the variable was defined using define.

(define my-foo (+ 1 2 3))

(get-expr my-foo) ;; => (+ 1 2 3)