LISP Syntax Problem of Piping Functions

By Xah Lee. Date: . Last updated: .

One of the annoying problem of the lisp nested syntax is the problem of creating a function chain (aka unix pipe).

Here is a example i frequently encounter in emacs lisp. I want to apply several different functions to a string, one after the other. Example:

(setq fname (file-name-sans-extension (file-name-nondirectory fname)))
(setq fname (replace-regexp-in-string "-" " " fname))
(setq fname (replace-regexp-in-string "_002d" "-" fname))
fname

Note the repeated reset of a variable. I don't want that. I want to avoid mutable variables; it's bad in functional programing. [see Avoiding Variables in Functional Programing] So, one should do like this:

(replace-regexp-in-string "_002d" "-"
 (replace-regexp-in-string "-" " "
  (file-name-sans-extension
   (file-name-nondirectory fname))))

Here is a actual example, from https://github.com/ergoemacs/ergoemacs-mode/blob/master/ergoemacs-menus.el, by Matthew Fidler (he knows emacs lisp macro very well, since he implemented a whole key system in ergoemacs-mode using macros.).

(defun ergoemacs-kbd-to-key (key)
  "Convert key Emacs key code to ergoemacs-key-code."
  (let ((case-fold-search nil))
    (replace-regexp-in-string
     " " "  "
     (replace-regexp-in-string
      "<" ""
      (replace-regexp-in-string
       "?" ""
       (replace-regexp-in-string
        "\\bRET\\b" "ENTER"
        (replace-regexp-in-string
         "\\bprior\\b" "PgUp"
         (replace-regexp-in-string
          "\\bnext\\b" "PgDn"
          (replace-regexp-in-string
           "<f\\([0-9]+\\)>" "F\\1"
           (replace-regexp-in-string
            "\\b-\\b" "+"
            (replace-regexp-in-string
             "\\b[[:lower:]]\\b" 'upcase
             (replace-regexp-in-string
              "\\b\\([[:upper:]]\\)\\b" "Shift+\\1"
              (replace-regexp-in-string
               "\\bC-" "Ctrl+"
               (replace-regexp-in-string
                "\\bS-" "Shift+"
                (replace-regexp-in-string
                 "\\bM-" "Alt+"
                 key t) t) t) t) t) t) t) t) t) t) t) t) t)))

But that has several problems. It's hard to debug. Hard to re-order. Hard to manage. Also, in conventional lisp code formatting, “emacs-lisp-mode” would format it like this (expand your window width to prevent line wrapping):

(replace-regexp-in-string "_002d" "-"
                          (replace-regexp-in-string "-" " "
                                                    (file-name-sans-extension
                                                     (file-name-nondirectory fname))))

Alternative is a one-liner formatting, but it's hard to read and edit:

(replace-regexp-in-string "_002d" "-" (replace-regexp-in-string "-" " " (file-name-sans-extension (file-name-nondirectory fname))))

If lisp has adopted its early concept of meta-expression as syntax wrapper, then it could be written like this using a postfix syntax like unix's pipe |. Here, i show it using Mathematica syntax:

fname //
file-name-nondirectory //
file-name-sans-extension //
Function[replace-regexp-in-string["-", " ", #]] //
Function[replace-regexp-in-string["_002d", "-", #]]

The // is similiar to unix's pipe | operator.

or in Wolfram Language's prefix notation, with @ as the prefix operator:

Function[replace-regexp-in-string["_002d", "-", #]] @
Function[replace-regexp-in-string["-", " ", #]] @
file-name-sans-extension @
file-name-nondirectory @
fname

Note that in functional languages (For example, OCaml), they have both prefix and postfix operators. Usually the space character is used as context-sensitive implicit prefix operator. For example, f x means “f” applied to “x”, and f a b c means (((f a) b) c).

[see OCaml Tutorial: Function]

In object oriented programing languages, usually their syntax for calling object's methods is effectively a postfix operator. In Perl, it's ->, in {Ruby, JavaScript}, it's just a dot. [see OOP Dot Notation, Dot Before Data or After?]

For detail, see:

Also note, although Haskell and OCaml have postfix and prefix operators, but these lang's syntax in general are syntax soups. That is, it's one bunch of ad hoc designs with no consistency, no systematic grammar, no mathematical foundation. They are not much better than the syntax soup of C-like langs. The symbols are used promiscuously (see: Problems of Symbol Congestion in Computer Languages; ASCII Jam vs Unicode) and the forms are idiosyncratic, example: i++, ++i, for(;;){}, while(){}, 0x123, expr1 ? expr2 : expr3, sprint(…%s…,…), etc.

The only language i know that has syntax approaching a systematic grammar is Mathematica. See:

Lisp Solutions

Scheme lisp scsh has this solution: (| f g h).

Clojure lisp has this solution (-> f g h). Clojure calls it “threading”. [see Clojure: Function Chaining]