The Emacs Series Exploring dash.el Further

0
1022

We will continue to look at the functions available in dash.el, which is a list library for Emacs, in this third part of the series of articles on it.

As stated in the previous article in this series, the dash.el library has been written by Magnar Sveen and the latest version is v2.16.0. The source code of dash.el is available at https://github.com/magnars/dash.el, and is released under the GNU General Public License v3.0. The dash.el package is available in Milkypostman’s Emacs Lisp Package Archive (MELPA) and in the Marmalede repo. You can install the package using the following command in GNU Emacs:

M-x package-install dash

You will require the function combinators, and hence you should also install the following:

M-x package-install dash-functional

The other method of installation is to copy the dash.el source file to your Emacs load path and use it.
In order to get syntax highlighting of dash functions in Emacs buffers, you can add the following to your Emacs initialisation settings:

(eval-after-load ‘dash ‘(dash-enable-font-lock))

Usage
In this article, we will explore more list operations, threading macros, binding constructs, destructive operations and function combinators.

List operations: The -rotate function shifts the elements to the right or left, based on an input count. A positive value of count shifts the elements to the right, while a negative number shifts them to the left. For example:

(-rotate count list) ;; Syntax

(-rotate 3 ‘(1 2 3 4 5))
(3 4 5 1 2)

You can repeat an element count a number of times to create a new list using the -repeat function. An example is given below:

(-repeat count list) ;; Syntax

(-repeat 3 :foo)
(:foo :foo :foo)

The -cons* function is used to create a new list from the arguments given to it. For example:

(-cons* arguments) ;; Syntax

(-cons* 1 2 3)

(1 2 . 3)

You can append an element to the end of a list using the -snoc function as shown below:

(-snoc list elements) ;; Syntax

(-snoc ‘(1 2 3) 4)
(1 2 3 4)

Given a separator character, you can create a new list with the separator inserted between the elements present in the input list using the -interpose function, as illustrated below:

(-interpose separator list) ;; Syntax

(-interpose “:” ‘(“p” “q” “r”))
(“p” “:” “q” “:” “r”)

The -interleave function returns a new list with the first element from each list, followed by the second elements and so on. For example:

(-interleave lists) ;; Syntax

(-interleave ‘(1 2) ‘(“a” “b”))
(1 “a” 2 “b”)

The -zip-with function takes three arguments — a function, and two lists. It applies the function pairwise between each element in the input lists. An example is shown below:

(-zip-with function list1 list2) ;; Syntax

(-zip-with ‘+ ‘(1 2) ‘(3 4))
(4 6)

The -zip function can be used to return a list of lists, as demonstrated below:

(-zip lists) ;; Syntax


(-zip ‘(1 2) ‘(4 5))
((1 . 4) (2 . 5))

If you have lists with different lengths, then you can use a character to fill the shorter lists using the -zip-fill function, as shown below:

(-zip-fill fill-value lists) ;; Syntax

(-zip-fill 0 ‘(1 2 3) ‘(4 5))
((1 . 4) (2 . 5) (3 . 0))

The -unzip function takes a list of lists, and does the opposite of the -zip function. For example:

(-unzip lists) ;; Syntax

(-unzip ‘((1 2) (3 4) (5 6)))
((1 3 5) (2 4 6))

The -cycle function returns an infinite copy of a list by repeating the elements in the list. In the following example, the -take function is used to return only three elements:

(-cycle list) ;; Syntax

(-take 3 (-cycle ‘(1 2)))
(1 2 1)

The -pad function accepts a fill character value and appends it to the end of the shorter list. For example:

(-pad fill-value lists) ;; Syntax

(-pad 0 ‘(1 2 3) ‘(4 5))
((1 2 3) (4 5 0))

You can compute the outer product of lists using the -table function. If you have two lists of dimensions n and m, then the outer product is the n x m matrix as shown below:

(-table function lists) ;; Syntax

(-table ‘* ‘(1 2 3) ‘(1 2 3))
((1 2 3) (2 4 6) (3 6 9))

If you want to specify a function for computing the outer product, then you can use the -table-flat function. An example is given below:

(-table-flat function lists) ;; Syntax

(-table-flat ‘* ‘(1 2 3) ‘(1 2 3))
(1 2 3 2 4 6 3 6 9)

The -first function takes a predicate function and a list, and returns the first element that matches the predicate. For example:

(-first predicate list) ;; Syntax

(defun even? (num) (= 0 (% num 2)))

(-first ‘even? ‘(3 4 5))
4

The -some function also takes a predicate function and a list, and returns True if there is at least one element that matches the predicate. Otherwise, it returns nil. A couple of examples are shown below:

(-some predicate list) ;; Syntax

(-some ‘even? ‘(1 2 3))
t
(-some ‘even? ‘(1 3 5))
nil

If you want to find the last element in the list that matches a predicate function, you need to use the -last function as follows:

(-last predicate list) ;; Syntax

(-last ‘even? ‘(1 2 3 4 5))
4

The first element of a list can be returned using the -first-item function, while the last element of a list can be returned using the -last-item function, as shown below:

(-first-item list) ;; Syntax
(-last-item list) ;; Syntax

(-first-item ‘(1 2 3))
1

(-last-item ‘(1 2 3))
3

If you wish to return everything but the last item in a list, you can use the -butlast function. For example:

(-butlast list) ;; Syntax


(-butlast ‘(1 2 3 4))
(1 2 3)

The -sort function sorts the elements of the list based on an input comparison function. An example is shown below:

(-sort comparator list) ;; Syntax

(-sort ‘< ‘(4 1 2 3))
(1 2 3 4)

The -list function takes a list of arguments and returns a new list. For example:

(-list arguments) ;; Syntax

(-list 1 2 3 4)

(1 2 3 4)

Threading macros
The threading macros take an argument x and thread the expression through the forms also passed as arguments. A few examples are shown below:

(-> x forms) ;; Syntax
(--> x forms) ;; Syntax
(->> x forms) ;; Syntax

(-> ‘(2 3 4) (append ‘(5 6)))
(2 3 4 5 6)

(defun square (n) (* n n))

(->> ‘(1 2 3) (-map ‘square))
(1 4 9)

(--> “ small” (concat “a” it “ world”))
“a small world”

The -some->, -some->> and some->> threading macros evaluate the expression x to check whether it is not nil, and then thread it through the first form. If the result is not nil, it continues to the next form, and so on. Examples are provided below for reference:

(-some-> x form) ;; Syntax
(-some->> x form) ;; Syntax
(-some--> x form) ;; Syntax

(-some-> 4 square)
16


(-some->> ‘(1 3 5) (-last ‘even?) (+ 5)) ;; Define even function
nil
(-some->> ‘(1 2 3) (-last ‘even?) (+ 5))
7
(-some->> ‘(1 2 3 4) (-last ‘even?) (+ 5))
9

(-some--> ‘(1 2 3) (-filter ‘even? it) (append it it) (-map ‘square it))
(4 4)

Binding constructs
The -when-let function accepts a variable-value expression, and evaluates it. If the value is non-nil, then it binds it to the variable and executes the body. In the following example, the character p is found in index 3, and the match-index gets the value of 3. It is then added to the number 2 to obtain the result 5 as shown below:

(-when-let variable-value body) ;; Syntax
(-when-let (match-index (string-match “p” “lisp”)) (+ match-index 2))
5

The -when-let* API binds all the values to the respective variables, and if all of them are evaluated as True, then the body is executed. For example:

(-when-let* variable-values body) ;; Syntax

(-when-let* ((x 1) (y 2) (z (+ y 4))) (+ x y z))
9

The -if-let function evaluates the variable-value and if the value returned is non-nil, the then expression is evaluated; otherwise, the else expression is executed as shown below:

(if-let variable-value then else) ;; Syntax

(if-let (match-index (string-match “x” “lisp”)) (+ match-index 2) 0)
0

The -if-let* function is similar to the -if-let function, except that it takes variable-values as arguments, along with the then and else expressions. An example is given below:

(-if-let* variable-values then else) ;; Syntax

(-if-let* ((x 1) (y 2) (z 3)) (+ x y z) “False”)
6

Destructive operations: The following two functions modify a list. The !cons function sets the cdr to the cons of the input arguments, as shown below:

(!cons car cdr) ;; Syntax

(let (x) (!cons 5 x) x)
(5)

The !cdr function sets the list to the cdr of the input list. For example:

(!cdr list) ;; Syntax


(let ((x ‘(1 2))) (!cdr x) x)
(2)

Function combinators: The dash-functional library is required to use the function combinators. You need to add the following to your Emacs initialisation configuration file in order to use the functions:

(require ‘dash-functional)

The -partial function takes a function and a list of arguments. The input function takes fewer arguments and returns a function that accepts and evaluates the additional arguments. An example is shown below:

(-partial function arguments) ;; Syntax


(funcall (-partial ‘- 5) 2)
3

The -rpartial function is similar to the -partial function, except that the additional arguments are used as the first argument on the returned partial function. For example:

(-rpartial function arguments) ;; Syntax

(funcall (-rpartial ‘- 5) 2)
-3

The -juxt function takes a list of functions and returns a function with the juxtaposition of those functions. In the following example, 2 + 5 is evaluated as part of the + function, and then 2 – 5 is evaluated as part of the – function:

(-juxt functions) ;; Syntax


(funcall (-juxt ‘+ ‘-) 2 5)
(7 -3)

The -compose function accepts a list of functions and returns a function with the composition of those functions. In the example given below, the arguments 4 and 3 are added together to get 7, and then squared to get the result 49.

(-compose functions) ;; Syntax

(funcall (-compose ‘square ‘+) 3 4)
49

The -applify function takes a function that modfies a n-arity function to a 1-arity function. For example:

(-applify function) ;; Syntax

(-map (-applify ‘+) ‘((1 2 3) (4 5 6)))
(6 15)

You can use the -on function with an operator and a transformer function on a list of lists. In the following example, the length of the lists is computed and then they are sorted in increasing order.

(-on operator function) ;; Syntax

(-sort (-on ‘< ‘length) ‘((1 2) (1) (1 2 3)))
((1) (1 2) (1 2 3))

The -flip function swaps the order of arguments and evaluates the input function. For example:

(-flip function) ;; Syntax

(funcall (-flip ‘<) 4 3)
t

(funcall (-flip ‘-) 4 3)
-1

The const function takes a value and returns it, ignoring the other arguments passed to it. A couple of examples are provided below:

(-const value) ;; Syntax

(funcall (-const 5) :a 3 “foo”)
5
(-map (-const 2) ‘(1 2 3 4))
(2 2 2 2)

The -not function takes a predicate and returns a unary predicate function that returns True if the predicate returns nil. For example:

(-not predicate) ;; Syntax
(funcall (-not ‘even?) 5)
t

If you want to perform an OR function with a list of unary predicates, then the -orfn function can be used. A couple of examples are shown below:

(-orfn predicates) ;; Syntax

(funcall (-orfn ‘even? ‘stringp) 4)
t
(funcall (-orfn ‘stringp ‘even?) 4)
t

The -andfn takes a list of unary predicates and returns True if all the predicates return non-nil. For example:

(-andfn predicates) ;; Syntax


(funcall (-andfn ‘stringp ‘even?) 4)
nil

The -iteratefn takes two arguments — a function and a count. It composes the function a count number of times. In the following example, the cdr function is applied twice on the input list (1 2 3 4) and it returns the list (3 4).

(-iteratefn function count) ;; Syntax

(funcall (-iteratefn ‘cdr 2) ‘(1 2 3 4))
(3 4)

You are encouraged to explore the other dash.el functions from its GitHub README file at https://github.com/magnars/dash.el.

LEAVE A REPLY

Please enter your comment!
Please enter your name here