Adding :grammar to split and join
This proposal adds the keyword :grammar to the split and join functions. Here is its meaning for join:
-
:infixmeans an infix or separator grammar: insert the delimiter between list elements. An empty list will produce an empty string. -
:strict-infixmeans the same as:infixif the string-list is non-empty, but will signal an error if given an empty list. -
:suffixmeans a suffix or terminator grammar: insert the delimiter after every list element. -
:prefixmeans a prefix grammar: insert the delimiter before every list element.
And here for split:
-
:infixis the same as above, but the empty string returns an empty list. -
:strict-infixis the same as above, but the empty string signals an error. -
:suffixand:prefixare the same as above.
Source: SRFI 152
I demand code examples ;)
First thoughts:
- It looks like
:infixis the current default. - to make the function error on an empty list, I'd give a more explicit argument
You actually felt this need?
From SRFI 152, but still should be readable:
(string-join '("foo" "bar" "baz")) => "foo bar baz" (string-join '("foo" "bar" "baz") "") => "foobarbaz" (string-join '("foo" "bar" "baz") ":") => "foo:bar:baz" (string-join '("foo" "bar" "baz") ":" :grammar :suffix) => "foo:bar:baz:"
;; Infix grammar is ambiguous wrt empty list vs. empty string: (string-join '() ":") => "" (string-join '("") ":") => ""
;; Suffix and prefix grammars are not: (string-join '() ":" :grammar :suffix)) => "" (string-join '("") ":" :grammar :suffix)) => ":"
An obvious application of :prefix is constructing an absolute path (Posix, Windows, URI, what have you) from a list of components. I'm not sure what to say about :suffix,
Here would be a working example of join with grammar keyword:
(defun join (separator strings &key (grammar :infix))
(let ((sep (if separator
(string separator)
" ")))
(case grammar
(:infix
(with-output-to-string (out)
(loop for (s . rest) on strings
do (write-string s out)
unless (null rest)
do (write-string sep out))))
(:strict-infix
(when (= 0 (length strings))
(error "When grammar is :strict-infix, strings cannot be nil or a list with zero elements"))
(funcall #'join sep strings :grammar :infix))
(:suffix
(with-output-to-string (out)
(loop for (s . rest) on strings
do (write-string (concat s sep) out))))
(:prefix
(with-output-to-string (out)
(loop for (s . rest) on strings
do (write-string (concat sep s) out)))))))
We need to decide whether we follow the Scheme convention of adding " " when separator is nil or if we use "". Also we could name the second parameter string-list (more descriptive?) instead of strings.
I wouldn't add these changes. I don't find the grammar stuff self-explanatory enough. I think we should rather understand what's missing from the existing function, and add meaningful key arguments.
It looks like the added features, with join, are:
- add a separator at the end
- at the start
- "strict" mode: signal an error on the empty list
that's it? Do we want to add "ensure-prefix,suffix" key args?