Elisp: thread-first, thread-last (Function Chain, Pipe)

By Xah Lee. Date: .

emacs lisp thread-first thread-last

emacs lisp has thread-first and thread-last.

new in Emacs 25 (date 2016)

they are in the experimental lib subr-x, which is not loaded by default.

Example

;; -*- coding: utf-8; lexical-binding: t; -*-

;; example of using thread-last

(provide 'subr-x)

(thread-last
  "abc"
  (concat "1")
  (concat "2")
  (concat "3"))
;; "321abc"


;; s------------------------------
;; example of using thread-last
(thread-last
  "abc"
  (string-replace "a" "1")
  (string-replace "b" "2")
  (string-replace "c" "3"))
;; "123"

;; s------------------------------
;; example of using thread-last
(thread-last
  "c++ file.txt"
  (string-replace " " "_")
  (string-replace "+" "p")
  )
;; "cpp_file.txt"

problems of thread-first thread-last

they are in the lib subr-x, not loaded by default.

thread-last manipulate elisp syntax by adding an element to inside parenthesis, as the last item (last parameter). This is bad, because function forms are no longer valid function forms. E.g.

(string-replace find-string replace-string string)

now needs to be

(string-replace find-string replace-string)

, which is not a valid function form.

similarly for thread-first.

This means, they stop being function programing. The fundamental principle of functional programing of composing functions is broken. Instead, you are starting to do term-rewrite programing, aka lisp macros programing. But emacs lisp is not a full term-rewriting system, as Wolfram Language is.

;; -*- coding: utf-8; lexical-binding: t; -*-

;; example of thread-last does not work on functions

;; we want to bracket string 123, first by square brackets, then by round brackets.
;; expected result is "([123])"
(thread-last
  "123"
  (lambda (x) (concat "[" x "]"))
  (lambda (x) (concat "(" x ")")))
;; #[(x) ((concat "(" x ")") #'(lambda (x) (concat "[" x "]") "123")) (t)]

emacs lisp, function chaining, piping functions