Skip to content

fosskers/cl-nonempty

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nonempty

(This README is best viewed on Codeberg.)

Non-empty collections for Common Lisp.

Non-emptiness can be a powerful guarantee. There are often times when a collection is passed to a function, but must contain some value to be useful. In these cases we must manually check, or otherwise account for NIL in our logic. This can muddle the clarity of the code, especially when emptiness is not a valid state. It is here that non-empty variants of the usual collection types can be used to great effect.

(in-package :nonempty)

;; Always succeeds, never nil.
(car (nel 1 2 3))
1

API

The examples here use (in-package :nonempty) for brevity, but it is assumed that you will set a nickname in your own code:

(defpackage foo
  (:use :cl)
  (:local-nicknames (:ne :nonempty)))

Non-empty Lists

Functions specific to non-empty lists.

nel, cons

Construct a non-empty list from at least one input argument.

(in-package :nonempty)
(nel 1 2 3)
#S(NELIST :HEAD 1 :TAIL (2 3))

Append a new item onto the front of a non-empty list.

(in-package :nonempty)
(cons 0 (nel 1 2 3))
#S(NELIST :HEAD 0 :TAIL (1 2 3))

car, cdr

The first element of a non-empty list. Always succeeds.

(in-package :nonempty)
(car (nel 1 2 3))
1

The possibly-empty tail of a non-empty list.

(in-package :nonempty)
(cdr (nel 1 2 3))
(2 3)

map, filter, fold

Map some FN over the given ITEMS, yielding a new non-empty list.

(in-package :nonempty)
(map #'1+ (nel 1 2 3))
#S(NELIST :HEAD 2 :TAIL (3 4))

Keep ITEMS for which a PRED function succeeds. Not guaranteed to be non-empty.

(in-package :nonempty)
(filter #'evenp (nel 1 2 3 4 5 6))
(2 4 6)

Reduce some ITEMS via a 2-arity FN.

(in-package :nonempty)
(fold #'+ (nel 1 2 3))
6
(in-package :nonempty)
(fold #'+ (nel 1 2 3) :seed 10)
16

Generics

length

The length of the given non-empty structure.

(in-package :nonempty)
(length (nel 1 2 3))
3

reverse

The reverse of the given non-empty structure.

(in-package :nonempty)
(reverse (nel 1 2 3))
#S(NELIST :HEAD 3 :TAIL (2 1))

elt, last

The element of ITEMS specified by INDEX.

(in-package :nonempty)
(elt (nel 1 2 3) 2)
3

The last element of the ITEMS. Guaranteed to exist.

(in-package :nonempty)
(last (nel 1 2 3))
3

append

Append some OTHER collection to a NONEMPTY one.

(in-package :nonempty)
(append (nel 1 2 3) (nel 4 5 6))
#S(NELIST :HEAD 1 :TAIL (2 3 4 5 6))
(in-package :nonempty)
(append (nel 1 2 3) '(4 5 6))
#S(NELIST :HEAD 1 :TAIL (2 3 4 5 6))

to-list

Convert this non-empty collection into a normal list.

(in-package :nonempty)
(to-list (nel 1 2 3))
(1 2 3)

Transducers Support

For additional high-level collection operations, support for Transducers is provided by the nonempty/transducers system. As this incurs additions dependencies, it is entirely optional. The examples below use full symbol paths, but it’s assumed that you’ll set appropriate nicknames:

(defpackage foo
  (:use :cl)
  (:local-nicknames (:ne :nonempty)
                    (:nt :nonempty/transducers)
                    (:t  :transducers)))

Non-empty lists can be used as “sources” as-is:

(in-package :transducers)
(transduce (map #'1+) #'cons (nonempty:nel 1 2 3))
(2 3 4)

And you can also reduce back into a non-empty list, provided that something actually made it through the transduction:

(in-package :transducers)
(transduce (map #'1+) #'nonempty/transducers:nelist (nonempty:nel 1 2 3))
#S(NONEMPTY:NELIST :HEAD 2 :TAIL (3 4))

Further Work

  • Non-empty Vectors
  • Non-empty Hash Tables

See Also