Atoms

The cloest analog in LFE to what most Lisp dialects call symbols is the Erlang atom. Just as with Lisps do with symbols, LFE uses atoms for its variable and function names. Atoms are literals and constants, which means that their value is the same as their name and once created, cannot be changed.

Some basic examples of atoms:

lfe> 'zark
zark
lfe> 'zarking-amazing
zarking-amazing

Slightly less straight-forward examples which start with non-alphanumeric characters:

lfe> ':answer
:answer
lfe> '42answer
42answer
lfe> '42.0e42answer
42.0e42answer
lfe> '42°C
42°C

Standard LFE atom names may be comprised of all the latin-1 character set except the following:

  • control character
  • whitespace
  • the various brackets
  • double quotes
  • semicolon

Of these, only |, \, ', ,, and # may not be the first character of the symbol's name (but they are allowed as subsequent letters).

Non-standard atom names may be created using atom quotes:

lfe> '|symbol name with spaces|
|symbol name with spaces|
lfe> '|'with staring quote!|
|'with staring quote!|
lfe> '|| ; <-- empty atoms are supported too
||
lfe> '|really weird atom: '#[]{}()<>";\||
|really weird atom: '#[]{}()<>";\||

In this case the name can contain any character of in the range from 0 to 255, and even no character at all.

In the case of atoms, it is important to understand a little something about their internals. In particular, Erlang and LFE atoms are global for each instance of a running Erlang virtual machine. Atoms are maintained by the VM in a table and are not garbage collected. By default, the Erlang atom table allows for a maximum of 1,048,576 entries.

Danger!

Uncontrolled autoamtic creation of atoms can crash the VM!

See the "Caveats" section below for more details.

As Symbols

The following code shows LFE's use of atoms in variable names. First, let's use a function for a slighly different purpose than designed: calling list_to_existing_atom on a string will only return a result if an atom of the same name already exists in the atom table. Otherwise, it will return an error:

lfe> (list_to_existing_atom "zaphod")
** exception error: bad argument
  in (erlang : list_to_existing_atom "zaphod")

This confirms that there is no zaphod atom in the atom table.

Now let's create a variable, assigning a value to it, and then use our indirect means of checkihg the atom table:

lfe> (set zaphod "frood")
"frood"
lfe> (list_to_existing_atom "zaphod")
zaphod

And here's an example showing LFE's use of atoms in function names using the same approach as above:

lfe> (list_to_existing_atom "beez")
** exception error: bad argument
  in (erlang : list_to_existing_atom "beez")

lfe> (defun beez (x) x)
beez
lfe> (list_to_existing_atom "beez")
beez

Converting

Atoms may be converted to strings and bitstrings, and vice versa.

(atom_to_binary 'trisha)
#"trisha"
(atom_to_binary 'mcmillan 'latin1)
#"mcmillan"
lfe> (atom_to_list 'trillian)
"trillian"

Note that the first one above is only available in Erlang 23.0 and above.

Some more examples for encoding:

lfe> (atom_to_binary '42°C 'latin1)
#B(52 50 176 67)
lfe> (atom_to_binary '42°C 'utf8)
#"42°C"

Functions that convert atoms only if they already exist in the atom table:

lfe> (binary_to_existing_atom #"trisha")
trisha
lfe> (binary_to_existing_atom #"trisha" 'latin1)
trisha
lfe> (list_to_existing_atom "trisha")
trisha

Operators

The only operators you may use on atoms are the comparison operators, e.g.:

lfe> (> 'a 'b)
false
lfe> (< 'a 'b)
true
lfe> (=:= 'a 'a)
true

Predicates

To test if a value is an atom, we will first include some code:

lfe> (include-lib "lfe/include/cl.lfe")

That adds Common Lisp inspired functions and macros to our REPL session.

lfe> (atomp 'arthur)
true
lfe> (atomp 42)
false
lfe> (atomp "forty-two.forty-two")
false

If the atom in question has been used in a function name definition:

If you prefer the Clojure-style of predicates:

lfe> (include-lib "lfe/include/clj.lfe")
lfe> (atom? 'dent)
true
lfe> (atom? "Ford")
false

Of course there is always the Erlang predicate, usable without having to do any includes:

lfe> (is_atom 'arthur)
true
lfe> (is_atom "forty-two.forty-two")
false

Caveats

As mentioned above (and documented), one needs to take care when creating atoms. By default, the maximum number of atoms that the Erlang VM will allow is 1,048,576; any more than that, and the VM will crash.

The first rule of thumb is not to write any code that generates large numbers of atoms. More explicitly useful, there are some handy functions for keeping track of the atom table, should you have the need.

lfe> (erlang:memory 'atom_used)
244562
lfe> (erlang:system_info 'atom_count)
9570
lfe> (erlang:system_info 'atom_limit)
1048576

Note that support for easily extracting the current atom data from system_info -- as demonstrated by the last two function calls above -- were added in Erlang 20; should you be running an older version of Erlang, you will need to parse the (system_info 'info) bitstring.

The default atom table size may be overridden during startup by passing a value with the +t options:

$ lfe +t 200000001
lfe> (erlang:system_info 'atom_limit)
200000001