1 Introduction

1.5 The LFE REPL

1.5.1 Using the REPL

We covered basic REPL usage in the quick start. We'll provide some information about the REPL in the document you are currently reading :-)

1.5.1.1 Starting the REPL

If you don't have LFE installed system-wide, you need to tell it (Erlang, really) where the LFE .beam files are. Here are the three ways to start up LFE in this case. The easiest is lfetool:

    $ lfetool repl lfe

If you're project was built with lfetool, then you'll have a make target called shell; calling this will first compile your source files and then call out to lfetool. Due to the compiling, this is a little slower way to start the REPL (though better for debugging!):

    $ make repl

The nice thing about both of these is that they automatically add your dependencies to ERL_LIBS so you have access to everything you need in the REPL itself.

Here are some more ways to start the REPL that don't take care of these niceties for you:

    $ ./bin/lfe -pa ./ebin
    LFE Shell V5.10.4 (abort with ^G)
    >
    $ erl -user lfe_boot -pa /path/to/lfe/ebin
    LFE Shell V5.10.4 (abort with ^G)
    >

You can also start an LFE REPL from the Erlang shell. First hop into erl:

    $ erl -pa /path/to/lfe/ebin
    Eshell V5.10.4  (abort with ^G)
    1>

then fire up LFE:

    1> lfe_shell:server().
    LFE Shell V5.10.4 (abort with ^G)
    >

If you do have LFE installed system-wide, then starting the shell can be done in the ways listed below.

    $ lfe

You can also start the LFE REPL by passing options directly to erl. Again, assuming that you have LFE installed system-wide, from any directory you may do this:

    $ erl -user lfe_boot

Also, if you happen to be running an Erlang shell already, you can start the LFE REPL with the following:

14> lfe_shell:server().
LFE Shell V5.9.3.1 (abort with ^G)
>

1.5.1.2 Running Commands

Once you're in the REPL, it's just a matter of entering code:

> (+ 1.5 3 4 5 6 7)
28
>

Unlike the Erlang shell, the LFE REPL supports defining functions:

> (defun exp (x y)
    (trunc (math:pow x y)))
exp
>

Let's put that exp to use:

> (exp 2 6)
64
>

You could take the lambda route too:

> (set exp
    (lambda (x y)
        (trunc (math:pow x y))))
#Fun<lfe_eval.15.132627770>
>

And, if you want to get nuts:

> (lists:map
    (lambda (z)
      (funcall exp (car z) (cadr z)))
    (list (list 1.5 0) (list 3 4) (list 5 6)))
(1 81 15625)
>

1.5.1.3 Quitting the REPL

Just as there are multiple ways in which you can start the REPL, there are a couple ways you can leave it. You can jump into the JCL from the LFE prompt by hitting ^g and then entering q:

> ^g
User switch command
 --> q
$

or you can call the Erlang shell's quit function:

> (c:q)
ok
>
$

Or simplest of all, the LFE exit command:

> (exit)
ok
>
$

1.5.2 Special Functions

There are some functions specially defined in LFE for use from the REPL. These are listed below with information about their use.

  • (c File [Options]) - Compile and load an LFE file. Assumes default extension .lfe.

  • (l Module ...) - Load modules.

  • (m Module ...) - Print out module information, if no modules are given then print information about all modules.

  • (ec File [Options]) - Compile and load an Erlang file.

  • (slurp File) - Slurp in a source LFE file and makes all functions and macros defined in the file available in the shell. Only one file can be slurped at a time and slurping a new file removes all data about the previous one.

  • (unslurp) - Remove all function and macro definitions except the default ones.

  • (set Pattern Expr) - Evaluate Expr and match the result with Pattern binding variables in it. These variables can then be used in the shell and also rebound in another set.

  • (c:command-arg ...) - All the commands in the Erlang shell's Command Interface Module can be reached in this way.

1.5.3 Special Variables

LFE also provides some convenience variables similar to what Lisp has defined for +, ++, +++, , *, ***, and -. Additionally, LFE also provides the $ENV variable.

  • +/++/+++ - The three previous expressions input.
  • */**/*** - The values of the previous 3 expressions.
  • - - The current expression input.
  • $ENV - The current environment (accessible in the REPL as well as in macros).

These probably warrant some examples.

Let's say you had just entered the following in the REPL:

> (+ 1.5 1.5)
3
> (c:memory)
(#(total 10026672)
 #(processes 1656528)
 #(processes_used 1656528)
 #(system 8370144)
 #(atom 153321)
 #(atom_used 147399)
 #(binary 1338560)
 #(code 3255239)
 #(ets 290544))
> (set my-func (lambda () (io:format "Hello, Zaphod!~n")))
#Fun<lfe_eval.23.86468545>
>

Then you can get the previous expressions you input with the following commands:

> +++
(+ 1.5 1.5)
> +++
(c:memory)
> +++
(set my-func (lambda () (io:format "Hello, Zaphod!~n")))
> ++
+++
> +
++
>

Most of us will actually use the arrow keys, thanks to the readline library. However, the classic, pre-readline approach is still available, should you choose to use it.

Similarly, you can get the results returned by using the variabels from the second bullet item. If you're following along in the REPL, go ahead and re-enter the commands we typed above to reset the last three items in your command history. Then do the following:

> ***
3
> ***
(#(total 9976496)
 #(processes 1606688)
 #(processes_used 1606688)
 #(system 8369808)
 #(atom 153321)
 #(atom_used 147399)
 #(binary 1338096)
 #(code 3255239)
 #(ets 290544))
> ***
#Fun<lfe_eval.23.86468545>
> (funcall *)
Hello, Zaphod!
ok
>

There's another, called the "dash" varibale. It is bound to the actual expression that is currently being evaluated. Here's an example of this being used:

> (io:format "Evaluating the expression '~p' ...~n" (list -))
Evaluating the expression '[':',io,format,
                            [quote,"Evaluating the expression '~p' ...~n"],
                            [list,'-']]' ...
ok
>

We've saved one of the more archane special variables to last: $ENV. When you first start up a shell, the $ENV variable holds pristine state data:

> $ENV
#(env
...
>

We can define a few variables and then check them out with another display of the environment:

> (set my-var 42)
42
> $ENV
#(env
...
   #(my-var 42)
...

If you slurp a file in the REPL, your environment will be updated with all the definitions in that file:

> (slurp "../lfe/examples/joes-fav.lfe")
#(ok joes-fav)
> $ENV
...
  (#(MODULE #(macro (match-lambda (((list) $ENV) `'joes-fav))))
...
  #(run-it
...
  #(universal-server
...

There is, as you might have guessed, much more to that ellided output (for that particular example, nearly all the rest of it is macro definitions).

Making use of $ENV can be very helpful when debugging include files, loading Erlang header files, or when creating macros. Furthermore, when spending a great deal of time in the REPL prototyping code for a project, it can be quite useful to refresh one's memory as to what functions and variables are currently available in $ENV.

Looking at the output for $ENV can be a bit overwhelming, however. As you might imagine, there is an easy answer to this: filter it! The following makes use of the Erlang lists module as well as patterns in an anonymous function, both of which will be covered in more detail later. This will prevent most nested variables from being displayed:

> (defun filter-env (env)
    (lists:foreach
      (lambda (section)
        (lists:foreach
          (match-lambda
            ((`#(* ,_)))
            ((`#(** ,_)))
            ((`#(*** ,_)))
            ((`#(+ ,_)))
            ((`#(++ ,_)))
            ((`#(+++ ,_)))
            ((`#(- ,_)))
            ((`#(,name ,value))
              (lfe_io:format "~s: ~p~n" `(,name ,value)))
            ((x) (filter-env `#(env ,x))))
          section))
      (lists:nthtail 1 (tuple_to_list env))))
filter-env

Now, as one hacks away in the REPL, slurping away at various modules, getting a list of what's defined in the current environment is a piece of cake:

> (filter-env $ENV)
my-var: 42
my-func: #Fun<lfe_eval.23.86468545>
script-args: ()
script-name: "lfe"
MODULE: #(macro (match-lambda (((list) $ENV) `'joes-fav)))
c: #(macro (lambda (args $ENV) `(: lfe_shell c ,@args)))
ec: #(macro (lambda (args $ENV) `(: lfe_shell ec ,@args)))
exit: #(function (#(0 #(dynamic_expr (lambda () (: lfe_shell exit))))))
factorial: #(function
...
ok
>

1.5.4 Getting Out of Trouble

Every once in a while you may find that you do something which causes the REPL to crash, presenting you with something that looks like this:

    >
      =ERROR REPORT==== 17-Feb-2013::15:39:33 ===
      ...

You don't have to quit and restart the REPL, if you don't want to! There are a couple of steps that you can take instead.

1.5.4.1 Interrupting a Shell Process

When you get an error as seen above, type ^g. This will put you into JCL (Job Control Language) mode. At the JCL prompt, type ? to see a list of options:

    User switch command
     --> ?
      c [nn]            - connect to job
      i [nn]            - interrupt job
      k [nn]            - kill job
      j                 - list all jobs
      s [shell]         - start local shell
      r [node [shell]]  - start remote shell
      q        - quit erlang
      ? | h             - this message

Let's see what's running:

    --> j
      1* {lfe_shell,start,[]}

Our shell process is still alive, though not responding. Let's interrupt it and then connect to it again:

    --> i 1
    --> c 1
    exception error: function_clause
     in (: lists sublist #(error interrupted) 1)
     in (lfe_scan string 4)
     in (lfe_io scan_and_parse 3)

    >

Once we interrupted the job, our error messages were printed to the REPL and we were placed back at the LFE prompt.

1.5.4.2 Starting a New Shell

Sometimes, though, there is no shell process any more. Here's how to start up a new shell process if the one that you're using dies:

    --> s lfe_shell
    --> j
          2* {lfe_shell,start,[]}
    --> c 2
    LFE Shell V5.9.3.1 (abort with ^G)
    >

And you're back up!