folio

https://github.com/mikelevins/folio.git

git clone 'https://github.com/mikelevins/folio.git'

(ql:quickload :folio)
11

folio the Bard Runtime Library

Introduction

folio is a set of modules that make some functional programming idioms more convenient for use in Common Lisp. The folio modules are portions of the runtime from the new Lisp dialect Bard, packaged for convenient use as Common Lisp libraries. As Bard's runtime matures, additional portions may be added as folio modules.

Loading

folio assumes that it's being used with Zach Beane's Quicklisp. To load folio, after Quicklisp is installed, eval the following expression:

(ql:quickload “folio”)

folio also relies on Scott L. Burson's FSet library, which is also distributed by Quicklisp. Loading folio should also load FSet automatically. If for any reason you should need to load FSet manually, eval the expression:

(ql:quickload “fset”)

AS –

The AS module provides a single generic function designed as a convenient, extensible utility for converting from one type to another. Following are a few examples of AS in use:

CL-USER> (as 'list #(0 1 2 3 4)) (0 1 2 3 4)

CL-USER> (as ‘string ’(#\H #\e #\l #\l #\o)) “Hello”

The AS package exports a single name, “AS”, in order to make it easy to use the package. AS names a generic function of two arguments; the first argment is the name of a target type; the second argument is a value to be converted.

Type names are conventionally the names of Common Lisp classes, but don't have to be. You can use EQL specializers to name conversions, and the target type names don't necessarily have to be the names of CLOS classes. For example, folio provides methods for converting from FSet's MAP type to both alists and plists:

CL-USER> (setq $fmap (fset:map (:a 1) (:b 2) (:c 3)))

{| (:A 1) (:B 2) (:C 3) |}

CL-USER> (as 'map:alist $fmap) ((:A . 1) (:B . 2) (:C . 3))

CL-USER> (as 'map:plist $fmap) (:A 1 :B 2 :C 3)

Neither ALIST nor PLIST is the name of a CLOS class. Rather, each is the name of different a way of interpreting an instance of LIST as a map. folio specializes AS on each name to provide a method for converting to that use of lists.

Boxes

Boxes are very simple objects that contain references to other values. A box is a settable wrapper around a value. The primary utility of a box is to provide a settable place in a functional data structure.

For example, FSet's weight-balanced trees provide very efficient functional representations of searchable data structures like sequences and maps. They provide a good balance of efficiency in search, insertion, deletion, subsets, concatenation, and so on.

FSet's data structures are functional, which means that you normally can't destructively modify them. This is mostly an advantage, but there are some circumstances in which you may want to take advantage of FSet's strengths, and still be able to destructively update a value at a particular spot in a data structure. For such uses, boxes are handy.

CL-USER> (setf $box (box:make nil))

<FOLIO.BOXES:BOX 200C295F>

CL-USER> (box:get $box) NIL

CL-USER> (box:put 1001 $box) 1001

CL-USER> (box:get $box) 1001

Although there is presently no provision for thread-safe update of boxes, it's my intention to implement a mechanism to provide atomic updates of boxes at some point in the future.

Functions

Folio provides a small set of function-handling utilities, in order to make some functional idioms more convenient.

The FUN package exports a small set of functions that accept functions as inputs and return functions as outputs. Such functions are known, in the parlance of functional programming, as “combinators”.

The purpose of combinators is to arrange existing functions into more convenient forms. For example, you could conveniently create a sequence of folio boxes like this:

(mapcar (fun:partial #'make-instance ‘box:box :value) ’(0 1 2 3 4 5))

PARTIAL constructs a function from MAKE-INSTANCE; the constructed function has already accepted the arguments 'BOX:BOX and :VALUE. When applied to each of the numbers in the argument list, it applies MAKE-INSTANCE to BOX, :VALUE and that number:

The FN package exports three macros that are simply abbreviations for Common Lisp forms whose names are little bukly if you're writing a lot of functional code.

FN is an abbreviation for LAMBDA

^ is also an abbreviation for LAMBDA

$ is an abbreviation for FUNCALL

The names were not chosen completely arbitrarily: FN is the name used for LAMBDA in Clojure; ^ is the name for LAMBDA originally proposed by Haskell Curry, and replaced by LAMBDA because of a typographical accident; $ is the name that Haskell uses for APPLY.

The intention is not to replace all uses of LAMBDA and FUNCALL, but when writing functional code, with many references to anonymous functions, the numerous repetitions of LAMBDA and FUNCALL can make expressions bulky and reduce their readability. The purpose of the macros in the FN package is to provide a way to reduce that clutter and improve readability in functional code. Use them when they make code easier to read; avoid them when they don't.

Collections

The folio Collections module provides a common API for sets, sequences, and maps that works across Common Lisp's lists, vectors, and strings, and also across FSet's sets, sequences, and maps. Its goal is to provide an API for these collections that is the same regardless of which representation you choose.

The folio Collections module extends AS to convert among various representations of sets, sequences, and maps. It also provides optional literal syntax for sequences and maps, which can be convenient for using them in some code.

The number of possible conversions and uses is large; it seems I'm always finding new combinations that are useful. Over time, it's reasonable to expect that folio's collections will include more and more conversion methods and utilities for specifying representations of sets, sequences, and maps.