The "elisp" Practice

Table of Contents

Turing completeness makes you diving in your digital world.

Noting new, but quote for creation.

1. Introduction

Emacs Lisp is a programming language as "elisp" to write the most of GNU Emacs, as well as its extensions. The elisp is a full computer programming language and it has capabilities for scanning and parsing text through editing commands.

These editing commands are elisp functions that can conveniently be called from Lisp programs, and parameters for customisation are ordinary Lisp variables to handle files, buffers, displays, subprocesses, and so on. The elisp has Turing completeness to facilitate Emacs become a platform for coordinating the information flow.

2. Hacking Emacs with elisp

First things first. How to make a elisp program running in Emacs?

(message "Hello World ~ ")
            
  • In lispinteraction-mode
    LispInteractionMode is the mode of the buffer first selected when Emacs starts. The first buffer is called the ‘*scratch*’ buffer. It is useful for evaluating EmacsLisp expressions. Typing ‘C-j’ after an expression will print the result on the next line.

    Switch to the buffer \*scratch\* and set the mode as "lisp-interaction-mode" by M-x lispinteraction-mode.

    Put the cursor at the end of "(message "Hello World ~ ")" and press "C-j". Then, the results will be shown in the following.

    "C-x C-e" the make the code run and echo the results in the mini-buffer area.

  • ielm mode
    IELM (Interactive Emacs Lisp Mode) is a nice mode that gives you an interactive Emacs Lisp shell that you can use for exploratory programming. You can start it by pressing M-x ielm. You'll be taken to a buffer named "ielm", where Emacs Lisp expressions you've entered will be evaluated on pressing the Return key. IELM offers some nice features like tab autocompletion of symbol names and the ability to span an expression over several lines.

    * Welcome to IELM * Type (describe-mode) for help.
    ELISP> 1
    1
    ELISP> (+ 1 1)
    2
    ELISP> ;; comment here
    ELISP> (defun fac (n)
    (if (= 0 n)
    1
    (* n (fac (- n 1)))))
    fac
    ELISP> (fac 4)
    24
    ELISP>

    y default, IELM evaluates complete expressions automatically as soon you as you press Enter. So one thing to remember is that if you want to have multi-line expression (like above), you must make sure that after each line the expression is not complete (i.e., the brackets are not balanced) – otherwise the expression will be evaluated too early. That makes modes like autopair or paredit a bit inconvenient for this. If you don't like that behavior, you can do:

    (setq ielm-dynamic-return nil)
                    

    which will allow you to Enter as much as you want and only evaluate things when you press C-j. But then you might as well use scratch I suppose. Personally, I use IELM mostly as a calculator.

3. The practice of elisp

3.1. Basic

3.1.1. Simple elisp examples:

  • Hello world
  • wrap markup region in a block <b></p>
  • mark a word, a line, a paragraph, a region
  • replace a string in a region
  • Text Editing:
    • Insert text:
      • insert
    • Delete text:
      • char: delete-char
      • region: delete-region
      • buffer: erase-buffer
      • kill-ring: delete-and-extract-region

3.1.2. nil and t \\

In Emacs Lisp, the symbol nil has three separate meanings: it is a symbol with the name ‘nil’; it is the logical truth value false; and it is the empty list—the list of zero elements. When used as a variable, nil always has the value nil. As far as the Lisp reader is concerned, ‘()’ and ‘nil’ are identical: they stand for the same object, the symbol nil. The different ways of writing the symbol are intended entirely for human readers. After the Lisp reader has read either ‘()’ or ‘nil’, there is no way to determine which representation was actually written by the programmer. In this manual, we write () when we wish to emphasize that it means the empty list, and we write nil when we wish to emphasize that it means the truth value false. That is a good convention to use in Lisp programs also. (cons 'foo ()) ;Emphasize the empty list (setq foo-flag nil) ; Emphasize the truth value false In contexts where a truth value is expected, any non-nil value is considered to be true. However, t is the preferred way to represent the truth value true. When you need to choose a value that represents true, and there is no other basis for choosing, use t. The symbol t always has the value t. In Emacs Lisp, nil and t are special symbols that always evaluate to themselves. This is so that you do not need to quote them to use them as constants in a program. An attempt to change their values results in a setting-constant error.

3.1.3. List \\

  1. Cons Cell \\

    A cons cell, such as (1 . 2), is an object that consists of two slots, called the CAR slot and the CDR slot. Each slot can hold any Lisp object. We also say that the CAR of this cons cell is whatever object its CAR slot currently holds, and likewise for the CDR.

    • How to create a Cons pair (cons val1 val2)
    • A list is made of cons (list "a" "b" "c") which is equal to: (cons "a" (cons "b" (cons "c" nil)))
  2. list \\

    A list is a series of cons cells, linked together so that the CDR slot of each cons cell holds either the next cons cell or the empty list. The empty list is actually the symbol nil. Because most cons cells are used as part of lists, we refer to any structure made out of cons cells as a list structure.

    • How to create a list
      • Create a list
      • Create a list of number sequence
    1. association list
    2. Property list
      • set it as symbol
      • get from a symbl
      • add an element pair
      • get a key and value
      • get a key's value
      • change a key's value
      • check whether a key exists
  3. TODO Operate lists \\
    • Test whether a list
    • Get the length of a list
      (length (list 1 2 3 4 5))
    • Get an specific element from a list (car (list 1 2 3 4 5)) #+endsrc

      (cdr (list 1 2 3 4 5))
                              
    • Get a sublist from a list
    • Add an elements to a list
    • Append lists
    • Modify a list
    • Convert a list to a string
  4. Processing a list
    • mapc
    • dolist
    • do times

3.1.4. programming

  • sequence
  • selection
  • looping while

3.2. medium

3.2.1. Data Structure

  1. Sequence
    1. Vector
      • How to create a vector
        • Nested a vector
      • Get the length of a vector
      • To fill a vector
      • Get an element(s)
      • Modify a vector
      • concat vectors
      • From a vector(s) to a list
    2. Type and Functions
    3. Apply mapcar to derive a list
      • by functions

        (mapcar '1+ '(1 2 3)
                                    
      • with lambda

        (mapcar
                                      (lambda (x) (+ x 1))
                                      (list 1 2 3 )
                                      )
                                    
    4. dolist or mapc
      (setq alphabets '(a b c d e))
                                (dolist (element alphabets)
                                (print element))
                              
    5. dotimes: as for to loop with fixed times
      (let (s) (-dotimes 3 (lambda (n) (!cons n s))) s)
                              
    6. loop

      we could find the constructs available in the loop.el library: https://github.com/Wilfred/loop.el

      (let ((x 0)
                                (sum 0))
                                ;; sum of 0..5
                                (loop-while (< x 5)
                                (setq sum (+ sum x))
                                (setq x (1+ x)))
                                sum)
                              
  2. Hash Table
    • Create a hash table

    The printed representation for a hash table consists of ‘#s’ followed by a list beginning with ‘hash-table’.

    #s(hash-table size 30 data (key1 val1 key2 300))
                        
    (setq ahashtable #s(hash-table size 30 data (key1 val1 key2 300)))
                        
    • add a key&value
    • remove a key&value
    • get a key value
    • count the number of entries
    • Get all keys
    • operate all keys with a function
    • Clear all entries

3.2.2. Function

  1. Define: Hello-world
    • define a function

      (defun function-name (arguments-list)
                                "document string"
                                body)
                              

      example:

      (defun hello-world (name)
                                "Say hello to user whose name is NAME."
                                (message "Hello, %s" name))
                              
  2. With Parameters
    • with optional parameters

                                (defun ops (aa bb &optional cc dd)
                                "with optional parameters"
                                (message "The first two default parameter: %s, %s; the other two optional parameters: %s, %s." aa bb cc dd))
                              
    • with rest parameters

      (defun rp (aa bb &rest cc)
                                "with rest arguments"
                                (message "%s" cc) ; This "cc" is a list
                                )
                              
  3. As a command
  4. Types
    • Types
      • primitive
      • Lambda
      • Special Form
      • Macro
      • Command how to write a command?

        (defun hello-elisp-world()
                                      "Insert "Hello elisp World" at cursor position."
                                      (interactive)
                                      (insert "Hello elisp World"))
                                    

        You could also specify parameters and local variables, see the following template from Xah Lee:

        (defun my-command (p1, p2, $optional, p3, p4, $rest, p5)
                                      "One sentence summary of what this command do. Less than 70 chars, try.
        
                                      More details here. Be sure to mention the return value if relevant.
                                      Lines here should not be longer than 70 chars,
                                      and don't indent them."
                                      (interactive)
                                      (let (var1 var2 etc)
                                      ;; do something here
                                      ;; last expression is returned
                                      ))
                                    
  5. Advanced topics:
    1. Lambda
      • lambda

        (funcall (lambda (name)
                                      (message "Hello, %s!" name)) "Emacser")
                                    

        It is equal to the following:

        (setq foo (lambda (name)
                                      (message "Hello, %s!" name)))
                                      (funcall foo "Emacser")
                                    
    2. Doc String
    3. variables & Scops
    4. mapcar & mapc

3.2.3. Error Handling in Emacs lisp

How does Emacs handle errors in elisp?

  • throwing the exception
  • catch the exception
  • handling clean-up

(error …) throws the most basic exception as signals. If an error were popping up, emacs will enter the debugger.

  • unwind-protect unwind-protect is the emacs way of implementing a finally clause. In the following code, when you exit from the debugger, "Hello" will be inserted in the buffer:

    (unwind-protect
                          (error "error popping up")
                          (insert "Hello, we are handling errors"))
                        
  • condition-case Try/Catch is spelled "condition-case". there is one handler per signal we want to handle. All error signals are derived from the popup error, so catching error catches any remaining signals.

    (condition-case var bodyform &rest handlers)
                        
    (unwind-protect
                          (let (retval)
                          (condition-case ex
                          (setq retval (error "Hello"))
                          ('error (message (format "Caught exception: [%s]" ex))))
                          retval)
                          (message "Cleaning up..."))
                        

    It can be wrapped in a macro:

    (defmacro safe-wrap (fn &rest clean-up)
                          `(unwind-protect
                          (let (retval)
                          (condition-case ex
                          (setq retval (progn ,fn))
                          ('error
                          (message (format "Caught exception: [%s]" ex))
                          (setq retval (cons 'exception (list ex)))))
                          retval)
                          ,@clean-up))
                        

    An example as follows:

    (safe-wrap (error "Hello") (message "Unwinding..."))
                          Caught exception: [(error Hello)]
                          Unwinding...
                          (exception (error "Hello")) ;; return value
    
                          ;; (safe-wrap (message "Hello") (message "Unwinding..."))
                          Hello
                          Unwinding...
                          "Hello" ;; return value
    
                          (safe-wrap (/ 1 0))
                          Caught exception: [(arith-error)]
                          (exception (arith-error)) ;; return value
                        

To be added. https://www.gnu.org/software/emacs/manual/html_node/elisp/Handling-Errors.html

3.2.4. Get User input

  • file name: read-file-name
  • directory: read-directory-name
  • string: read-string
  • regex: read-regexp
  • number: read-number
  • from a list: completing-read

3.2.5. Mark and Region

3.3. Advance

  • Regular Expression

4. Create your own applications

5. Reference

Date: 2022-10-14 Fri 00:00

Author: Dr YF Lin

Created: 2022-11-07 Mon 09:54

ThingsEngine