elisp.org 16 KB

The 3 fundamental Lisp Syntax things

Flow Control/Conditionals

    1) =( )= means to run command with arguments 1-3. 2) ='( )= is like a quoted string. The computer does NOT run any command. 3) =;= is a comment. Anything after the =;= on that line will not be interpreted as code by the elisp interpreter.
  • cond is the most powerful of the conditionals, which is essentially a switch statement. It looks like this:
  • #+BEGIN_SRC emacs-lisp (cond ((condition 1) body-forms) ((condition 2) body-forms) ((condition 3) body forms) (t default-body-forms)) #+END_SRC

    Buffers

    • when is a simple if statement with no attached else.
    • When =COND= is true evaluate the body forms and return the value of the last one, but if COND is not true, return nil. #+BEGIN_SRC emacs-lisp (when COND BODY...) #+END_SRC A buffer is a region of memory that contains the contents of a file. The current buffer is what is displayed in an Emacs window. However, it is possible to manipulate buffers without displaying them to the screen.
    • (buffer-name) is the name of the current buffer
    • (buffer-file-name) is the full path of the current buffer
    • (current-buffer) returns the full text in the current buffer.
    • (other-buffer) returns the full text from the last visited buffer.
    • You can switch to another buffer in emacs with
    • #+BEGIN_SRC emacs-lisp (switch-to-buffer (other-buffer)) #+END_SRC

    OR

    defuns

      #+BEGIN_SRC emacs-lisp (switch-to-buffer ()) #+END_SRC
    • (buffer-size) tells you the size of the current buffer
    • (point) tells you how many chars are between the beginning of the buffer and point
    • a function definition,

      #+BEGIN_SRC emacs-lisp (defun FUNCTION-NAME (ARGUMENTS…) "OPTIONAL-DOCUMENTATION…" (interactive ARGUMENT-PASSING-INFO) ; optional BODY…)

      #+END_SRC

      Sharpquoting

      The cool thing about lisp is that you DO NOT need to use (return) to return a value. Instead, the last evaluated form in a defun is what is returned!

      Here is a sharp quote:

      
      (add-hook 'org-mode-hook #'toggle-word-wrap)
      

      You should only sharp quote something when you are quoting a named function. This gives the byte compiler more useful information in case of future debugging. BUT there's no need to sharp quote a lambda expression. So don't do this:

      
        (add-hook 'org-mime-html-hook #'(lambda ()
                                          (org-mime-change-element-style
                                           "pre" (format "color: %s; background-color: %s; padding: 0.5em;"
                                                         "#E6E1DC" "#232323"))))
      

      Data Types

      Lisp Date types are a way of categories each lisp object. Every object has at least one type, but it can also have more than 1 type. This is why you can ask if an object is of type a, but you cannot ask what type an object is.

      Each object in lisp not only stores that object's value, it also stores that objects type/s. This means that if an object is of type number, then you cannot concatenate a string to it, because lisp knows that that object is a number, NOT a string, which is why this will fail:

      
        (setq number 5)
        (print (concat "5" number))
      

      Programming types

      Sequence Types

      A sequence in lisp is a set of elements. It can either be a "list" or a "array".

      A list can hold elements of any type. ie: #+BEGIN_SRC emacs-lisp '(hello '(I am supper cool) '(hello again ) yup that is me) #+END_SRC

      So a list is like a digital shopping list. The shopping list can have items from ANY store, and it can be nearly infinitely long!

      An array is a fixed length sequence. If the array can hold any element, then it is a vector array. A vector array is like a written shopping list, because a written shopping list can only hold soo many items on it.

      Strings just hold characters. So string arrays are like a written novel w/o pictures. The book can only have words and a written book cannot be infinitely long.

      Bool-vectors are a sequence of t and nil in any order. So bool vector are like lie detector games where you can only answer yes or no.

      info:elisp#Sequence Type Char tables are like strings, except they can hold any valid character code like =?\C-t=

      Lists string and arrays share some similiarities. ie: all have a length L, and all are indexed from 0 to L minus one.

      integer

      Also take Integers are whole numbers lacking any fractional part. So the following are all -1

      
      -1
      -1.
      

      and the following are all 1

      
      1
      +1
      1.
      +1.
      

      some interesting ints

        [[https://www.reddit.com/r/emacs/comments/48je3z/understanding_numbers_in_emacs_internals_int/][reddit]]
      • int is a C signed integer type.
      • EMACS_INT is a typedef in lisp.h - signed integer wide enough to hold an Emacs value. Either int, or long int, or long long int.
      • XINT is a macro for extracting integer. Something like # define lisp_h_XINT(a) (XLI (a) >> INTTYPEBITS). And XLI is # define lisp_h_XLI(o) ((o).i).
      • make_number is # define lisp_h_make_number(n) XIL ((EMACS_INT) (((EMACS_UINT) (n) << INTTYPEBITS) + Lisp_Int0))
      • CHARPOS is #define CHARPOS(POS) (POS).charpos.

      float

      Floats are for storing numbers that contain fractional parts. They can be written with an explicit decimal part, or they can be written in scientific notation to be considered a float.

      All of the following numbers represent 150.0.

      
      150.0
      15e1
      .15e3
      1500e-1
      
      150.0
      

      character types

      character

      A character is a letter. Internally elisp stores chars as an integer, so "R" has the value:

      
      ?R
      
      82
      

      You can also discover the syntax for the special characters.

      
      ?\(
      ?\\
      
      92
      
      
        ?\a ⇒ 7                 ; control-g, ‘C-g’
        ?\b ⇒ 8                 ; backspace, <BS>, ‘C-h’
        ?\t ⇒ 9                 ; tab, <TAB>, ‘C-i’
        ?\n ⇒ 10                ; newline, ‘C-j’
        ?\v ⇒ 11                ; vertical tab, ‘C-k’
        ?\f ⇒ 12                ; formfeed character, ‘C-l’
        ?\r ⇒ 13                ; carriage return, <RET>, ‘C-m’
        ?\e ⇒ 27                ; escape character, <ESC>, ‘C-[’
        ?\s ⇒ 32                ; space character, <SPC>
        ?\\ ⇒ 92                ; backslash character, ‘\’
        ?\d ⇒ 127               ; delete character, <DEL>
      

      Boolean Values: nil and t

      nil has 3 separate values. It can mean false, a symbol, and it is the empty list ().

      t is the boolean value true. Technically any non-nil value will evaluate to a true value.

      For example (when 5 BODY) will evaluate the body forms because 5 is non-nil.

      
        (when 5 (print "hello"))
      
      
      hello
      

      BUT =(booleanp 5) produces no output, because 5 is not boolean.

      
      (booleanp 5)
      

      cons

      A cons cell is an object has two slots the CAR and the CDR. Each slot can "hold" any lisp object.

      (CAR . CDR)

      Emacs uses cons cells to link together elements to form lists.

      So the list (a b c d)

      internally looks like =(a . (b . (c . (d . nil))))

      associated lists

      Associated lists are also alists is a lisp list, where each element is a cons cell. So for example:

      
        (setq fibanocci-alist
              '((0 . 1) (1 . 1) (2 . 2) (3 . 3) (4 . 5) (5 . 8)))
      

      In the fibanocci-alist, the CARs of each element in the list, are reference points to each respective values of the list.

      If you have experience with C-like languages this is like an array.

      
        char string[5] = "hello";
        printf ("%d", string[3]);
      
      108
      

      symbol

      string

      foo and Foo are symbols

      vector

      hash-table

      A good example of a hash-table is buffer objects like (current-buffer). Lisp tells you this when you evaluate

      
      (current-buffer)
      
      #
      

      The syntax # lets you know that that buffer is a hash table.

      subr

      other special types

      Editing Types

      let

      • byte-code function
      • Let is a special lisp form that lets you define local variables. Since all variables in lisp are global, you need to be able to say HEY, these variables are local to this defun

      Let looks like

      #+BEGIN_SRC emacs-lisp (let VAR-LIST BODY) #+END_SRC

      http://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding An interesting example:

      A ‘let’ expression is indeed just “syntatic sugar”, a convenience, for the corresponding ‘lambda’ form:

      #+BEGIN_SRC emacs-lisp (let ((a 1) (b 3)) (+ a b))

      #+END_SRC

      is equivalent to:

      #+BEGIN_SRC emacs-lisp

      ((lambda (a b) (+ a b)) 1 3)

      frames

      #+END_SRC When you create a new frame, it is set up with the "default-frame-alist".

      Functional Programming

      Lisp is a functional programming language. It's not a truly functional language like Haskell, but it is close. Functional programming, in the simplest terms does not use loops: no for loops, no while loops, no do until loops, etc. Instead any sort of looping construct is done via recursion. Recursion occurs when a function calls itself to evaluate some result. Recursive functions tend to be extremely short and remarkably easy to understand. 1 However, any recursive function can be written non-recursively.

      Emacs Lisp does have loops. This is why it is not a purely functional programming language. BUT here is a functional method to add all the numbers less than a number n.

      
        (defun sum-numbers-less-than-n (n)
          "sum all the numbers less than n"
          ;; if the number is 1, then return 1
          (cond ((eq n 1) 1)
          ;; if the number is greater than 1, then return the sum of n plus sum-numbers-less-than-n (- n 1)
                ((+ n (sum-numbers-less-than-n (- n 1))))))
      
      sum-numbers-less-than-n
      

      Suppose we wish to sum all the integers less than 3. The function call would look like this

      
         (sum-numbers-less-than-n 3)
             |
             |
            \ /
      
         (+ 3 (sum-numbers-less-than-n 2))
             |
             |
            \ /
      
         (+ 3 (+ 2 (sum-numbers-less-than 1)))
             |
             |
            \ /
      
         (+ 3 (+ 2 1))
             |
             |
            \ /
      
        (+ 3 3)
             |
             |
            \ /
             6
      

      Here is another recursive example that will create the nth fibonacci number.

      
        (setq fibonacciCalls 0)
        (defun fibonacci (n)
          "generate the nth fibonacci number"
          (setq fibonacciCalls (+ 1 fibonacciCalls))
          (cond
           ((eq n 0) nil)
           ((eq n 1) 0)
           ((eq n 2) 1)
           ((+ (fibonacci (- n 1)) (fibonacci (- n 2)) ))))
      
      fibonacci
      

      (fibonacci 30)

      
      (fibonacci 5)
        |
        |
        |
       \ /
      (+ (fibonacci 4) (fibonacci 3))
        |
        |
        |
       \ /
      (+ (+ (fibonacci 3) (fibonacci 2)) (+ (fibonacci 2) (fibonacci 1)))
        |
        |
        |
       \ /
      (+ (+ (+ (fibonacci 2) (fibonacci 1)) (fibonacci 2)) (+ (fibonacci 2) (fibonacci 1)))
      

      To calculate the 11th fibonacci number, it has to call fibonacci 177 times. To calculate the 30th fibonacci number, there are 1,664,079 recursive function calls!

      But maybe we can rewrite the elisp fibonacci generator, so that it doesn't have to calculate numerous fibonacci numbers multiple times. So every time that it calculates a fibonacci number, it'll add it to a hash table. Then when calculating another fibonacci (n) it'll check to see if that number is already in the hash table.

      
        (defvar fibonacciCalls 0 "How many times fibonacci has been called.")
        (defvar fibonacciHashTable (make-hash-table :weakness t))
        (defun fibonacci (n)
          "generate the nth fibonacci number"
          (setq fibonacciCalls (+ 1 fibonacciCalls))
          (cond
           ((eq n 0) nil)
           ((eq n 1.0) 0)
           ((eq n 2.0) 1)
           ((if (gethash n fibonacciHashTable)
                (gethash n fibonacciHashTable)
              (puthash n (+ (fibonacci (- n 1)) (fibonacci (- n 2)) ) fibonacciHashTable)))))
      
      fibonacci
      

      Strings

      Some functions

      (fibonacci 110)

      This function returns true, if the object is a string

      
      (stringp "hello")
      
      t
      

      This function will return true if the object is a string or a char.

      
      (char-or-string-p "t")
      
      t
      

      If a function returns a type looking like #("point" 0 5 (fontified t)), then that is a string with some text properties.

      Lists

      A list is a sequence of 0 or more elements. You can insert or delete any of the elements in a list.

      A list is made of up cons cells, which are a pair of elements. The first is the CAR and the second is the CDR. So

      
      '("CAR" . nil)
      
      CAR

      That cons cell is a list, whose only element is the string "CAR".

      In a true list, the CDR refers to the next element in the list and the last CDR in the last cons cell is the symbol 'nil'. For example

      
        '("This" . ("is" . ("a" . ("true" .  ("list" . nil)))))
      
      This is a true list

      If the list's last CDR is not the symbol 'nil', then it is a dotted list. Like this:

      
      '("this" . ("is" . ("a" . ("dotted" . "list"))))
      
      (this is a dotted . list)
      

      Functions on lists

      A list can also be circular, which means the last CDR refers to another item in the list.

      • consp object
      • returns t if the object is a cons cell #+BEGIN_SRC emacs-lisp (consp '("hello" . nil)) #+END_SRC

      #+RESULTS:

      t
      

      #+BEGIN_SRC emacs-lisp (consp '(nil)) #+END_SRC

      #+RESULTS:

      t
      

      #+BEGIN_SRC emacs-lisp (consp '("list" . ("item" . nil))) #+END_SRC

      #+RESULTS:

      t
      
      • atom object
      • return true if the object is an atom. The symbol 'nil' is an atom and a list. #+BEGIN_SRC emacs-lisp (consp '(5)) #+END_SRC

      #+RESULTS:

      t
      
      • listp
      • returns true if the object is a cons cell

      (listp '("cons" . ("cell" . nil))

      
      
      t
      
      • add to list
      • Here is an example of how to add an element to a list. Add to list, only adds one element at a time. #+BEGIN_SRC emacs-lisp (add-to-list 'auto-insert-alist '(org-mode . ["default-org-file.txt"])) (add-to-list 'auto-insert-alist '(web-mode . ["default-web-file.txt"])) #+END_SRC

      Nice Elisp libraries for programming

      • push
      • push is very like add-to-list. It may even be a better version.

      String manipulation library (use-package f)

      String manipulation library (use-package dash)

      edebug

      String manipulation library (use-package s)

      macros

      tweaking my .emacs.d

      how can I lazy load my init file?

      It would be nice to show the agenda w/o having to load org-mode.

      When you begin an edebug session, you can press "?" to see a list of available commands. A macro expands a bit of code into another bit of code.

      I should be able to lazy load projectile and helm

      I should really revamp my init-org.org file

      init-editing-utils.el is now being really slow!

      Footnotes

      When I press "q" to exit the agenda, then org-mode and evil-mode would be loaded.


      1. It is important to understand that in languages not optimized for recursion, recursion uses for memory than non-recursive methods. Recursion is usually slightly slower too.