ck-macros

Composable Scheme macros based on the CK abstract machine.

CK-macros are slow and hard to debug, so you probably don't want to use this egg for practical applications. Instead, it is an amusing puzzle or experiment to see how far this unusual kind of macro can go.

This egg is based on the CK-macro system described in "Applicative syntax-rules: macros that compose better" by Oleg Kiselyov. It provides an enhanced version of the core ck macro, many useful (and some not-so-useful) predefined CK-macros, the ck-wrapper procedure, and many wrappers of R5RS procedures.

If you create a useful or interesting general-purpose CK-macro, or an improvement to an existing CK-macro, please contact the maintainer (John) so your contribution can be added to the egg. All source code (including contributions) is public domain.

Project / Source Code Repository
https://gitlab.com/jcroisant/ck-macros
Maintainer
John Croisant
Based on work by
Oleg Kiselyov
License
Released to the public domain. See http://unlicense.org

Table of Contents

  1. ck-macros
    1. Version History
    2. Usage
    3. Terminology and conventions
    4. What are CK-macros?
    5. How to write CK-macros
    6. Combining CK and non-CK macros
    7. Partial application
    8. Quasiquotation
    9. Debugging
      1. Missing quote
      2. Reading error messages
      3. Errors inside CK-macro calls
    10. Known issues and limitations
      1. Slow compilation
      2. Pitfalls of c-sym-eq?
    11. ck
    12. ck-wrapper
    13. Portable CK-Macros
      1. General
      2. CK-macro builders
      3. Boolean Logic
      4. List Processing
      5. Vector Processing
      6. Unary Math
      7. Decimal Integers
    14. Non-portable R5RS Wrappers
      1. General (R5RS Wrappers)
      2. Numbers and Math (R5RS Wrappers)
      3. Pairs and Lists (R5RS Wrappers)
      4. Symbols (R5RS Wrappers)
      5. Chars and Strings (R5RS Wrappers)
      6. Vectors (R5RS Wrappers)

Version History

0.3.1 (2024-01-27)
Fixed unbound variable: ck-wrapper on CHICKEN 5.2.0 and later (thanks Peter Bex). The ck-macros module must now be imported into both runtime and syntactic environments (see Usage).
0.3.0 (2019-03-17)
Enhanced the core ck macro to support quasiquotation. Added some new portable CK-macros. Clarified license.
0.2.0 (2019-02-28)
Ported to CHICKEN 5. Added ck-wrapper and many R5RS wrappers. Added some new portable CK-macros. Breaking changes to c-apply. Renamed some CK-macros.
0.1.1 (2016-02-07)
Fixed c-append and c-vector-append failing when given one argument.
0.1.0 (2016-02-06)
Initial release. Includes version 1.1 (April 2011) of the core ck macro. Includes many CK-macros.

For more information about what changed in each version, see the CHANGELOG.

Usage

(import ck-macros)
(import-for-syntax ck-macros)

(ck () `(vector ,@(c-map2 '(c-list '*) '(1 2 3) '(4 5 6))))
;; Expands to (vector (* 1 4) (* 2 5) (* 3 6))
;; ==> #(4 10 18)

Terminology and conventions

To help avoid confusion, this documentation carefully uses the following terminology:

By convention, CK-macros provided by this egg start with "c-" to distinguish them from non-CK macros or procedures with similar names. You do not need to follow this naming convention in your own code.

Some private helper macros and functions used internally by this egg have the prefix "%ck:". To prevent accidental name collisions, you should not define anything with this prefix in your own code.

What are CK-macros?

CK-macros are Scheme macros which are written in a specific way to make them compatible with the core ck macro. The core ck macro recursively expands the CK-macros's arguments first, before the CK-macro itself is expanded.

This allows you to easily combine simple reusable macros to create more complex macros, similar to the way Scheme allows you to combine simple resuable functions to create more complex functions. In fact, many useful Scheme functions can be translated to CK-macros, allowing you to portably achieve the same effect at macro-expansion time. You can even implement "higher-ordered macros" which take a macro as an argument. See c-map1 and c-compose for examples of higher-ordered macros.

You can use CK-macros without understanding the theory, but basically, the core ck macro emulates a simple computer by cleverly rearranging and expanding the parts of the given expression. Specifically, the core ck macro implements a simplified CEK machine with no environment (E), only a control statement (C) and a stack (K).

CK-macros are not as flexibile, powerful, or efficient as CHICKEN's explicit and implicit renaming macros. The main practical advantage of CK-macros is that they provide more flexibility and power than a typical syntax-rules macro, while also being portable to other Scheme implementations:

How to write CK-macros

(Note: If you do not require strict R5RS portability, the easiest way to create a CK-macro is with ck-wrapper.)

Here is a basic template for portable CK-macros:

(define-syntax c-foo
  (syntax-rules (quote)
    ((c-foo s 'input1 'input2 ...)
     (ck s output))))

A CK-macro is just a normal macro that expands to a call to the core ck macro. To be portable, CK-macros are usually written using syntax-rules. But, you can write CK-macros using other macro systems, such as CHICKEN's explicit and implicit renaming macros, as long your macro eventually expands into a call to the core ck macro.

CK-macros treat quoting specially, so you should have quote in the list of literal identifiers (the first argument to syntax-rules). You can also have other literal identifiers if needed.

Every CK-macro's first argument must be the stack, usually called s. The stack is used internally by the core ck macro while recursively expanding macro arguments. Your CK-macro should not touch the stack, only pass it through to the core ck macro.

Each 'input is an argument passed to your macro. Your macro can have any number of input arguments, and they can be named whatever you want. They can also be lists or patterns, allowing you to destructure the arguments, as is often done with syntax-rules macros.

The core ck macro expands the input arguments ahead of time, so the arguments will always be quoted values by the time your macro is expanded. Usually CK-macros are defined with their input arguments quoted (as above), so that you can easily destructure the input to use in your macro. You may recall that 'a is translated by the reader to (quote a). So, the above example is exactly the same as:

(define-syntax c-foo
  (syntax-rules (quote)
    ((c-foo s (quote input1) (quote input2) ...)
     (ck s output))))

When written in this way, it is easier to see that input1 and input2 are merely placeholders in the syntax-rules pattern. (This is also why you must tell syntax-rules that quote is a literal identifier. Otherwise, syntax-rules would treat quote as a placeholder, too.)

Your macro should transform the inputs in some way to produce the output, which is passed to the core ck macro. The output must be either:

Quoting is how the core ck macro knows the difference between a value and a CK-macro call. Therefore, all values must be quoted, even values which normally do not need to be quoted in Scheme code, such as strings, numbers, and booleans.

Eventually, your CK-macro must expand into a call to the core ck macro with a quoted value. We say that the macro "yields" this quoted value. The quoted value is either used as an argument to another CK-macro, or if your CK-macro is the outer-most CK-macro call, then the core ck macro expands into the unquoted value.

So, if your CK-macro yields the quoted value '(+ 1 2), and it is the outer-most CK-macro (not an argument to another CK-macro), the core ck macro will expand to the unquoted expression (+ 1 2), which would later be evaluated to the number 3. (If you want to yield a quoted form as the final result, use c-quote as the outer-most CK-macro.)

See the original article or the source code for many examples of CK-macros.

Combining CK and non-CK macros

The arguments to a CK-macro must either be a quoted value or a call to a CK-macro (i.e. a macro that expands to a call to the core ck macro). Therefore, most non-CK macros and special forms cannot be used as arguments to a CK-macro.

Some non-CK macros can be used in a way that expands into a user-provided expression. It is therefore possible for these non-CK macros to be used as arguments to CK-macros, as long as they eventually expand into a call to the core ck macro.

It is possible to write a non-CK macro which invokes a CK-macro via the core ck macro. For example, if you are writing a macro for other people to use, you can create a convenience macro, like so:

(define-syntax foo
  (syntax-rules ()
    ((foo arg1 arg2 arg3)
     (ck () (c-foo 'arg1 'arg2 'arg3)))))

Also, it is possible for a CK-macro to expand into a call to a non-CK macro as its final result, or as part of the body of its final result. For example:

(ck () (c-list 'when (c-list '< '1 '2) (c-list 'print '"Yay!")))
;;; Expands to the expression (when (< 1 2) (print "Yay!")).

Partial application

Many CK-macros, such as c-compare and c-map1, take one or more operations as an argument. This is denoted with '(OP ...) in the CK-macro's signature. For example, c-map1 takes an operation which it applies to every element of the list:

(ck () (c-quote (c-map1 '(c-vector) '(2 3 4))))
;; ==> '(#(2) #(3) #(4))

;; Equivalent to:
(ck () (c-quote (c-list (c-vector '2)
                        (c-vector '3)
                        (c-vector '4))))

All CK-macros which take an operation allow partial application, by providing initial arguments that should be used when performing the operation:

(ck () (c-quote (c-map1 '(c-vector 'x 'y) '(2 3 4))))
;; ==> '(#(x y 2) #(x y 3) #(x y 4))

;; Equivalent to:
(ck () (c-quote (c-list (c-vector 'x 'y '2)
                        (c-vector 'x 'y '3)
                        (c-vector 'x 'y '4))))

You can even build up complex operations by partially applying c-compose or c-rcompose, even with partially applied operations as the initial arguments!

(ck () (c-quote (c-map1 '(c-rcompose '((c-* '10)
                                       (c-number->string)
                                       (c-string-append '"foo")
                                       (c-string->symbol)))
                        '(1 2 3))))
;; ==> '(foo10 foo20 foo30)

Quasiquotation

In version 0.3.0 and later, the core ck macro supports quasiquotation. Internally, (quasiquote x) (also written as `x) is converted into (c-quasiquote 'x), a call to the c-quasiquote CK-macro, which handles unquoting.

Quasiquotation can be used as the argument to the core ck macro itself, and/or in arguments to CK-macros. This is useful for assembling mostly-literal forms, lists, and vectors:

(ck () `(define (foo) (list ,@(c-list '1 '2) ,(c-vector '3))))
;; ==> (define (foo) (list 1 2 #(3)))

(ck () (c-vector-reverse `#(1 ,@(c-list '2 '3))))
;; ==> #(3 2 1)

Debugging

CK-macros utilize the Scheme macro system in unusual ways, so they are sometimes more difficult to debug than typical Scheme code.

The most common error message when using CK-macros is "no rule matches form". This usually indicates one of two problems:

Missing quote

As explained in the section How to write CK-macros, all values must be quoted, even values which normally do not need to be quoted in Scheme code, such as strings, numbers, and booleans.

For example, the code (ck () (c-not #t)) will result in an error like this:

Syntax error: no rule matches form

        (ck123 (((c-not))) #t)

In the error message above, ck123 is the core ck macro. The Scheme macro system has appended a unique number for macro hygiene. The number will vary, and is usually not important to pay attention to.

Because this error message shows a call to ck, we know it probably indicates a missing quote. Indeed, the solution is to quote the #t, like this: (ck () (c-not '#t))

Sometimes the error is not so obvious, and you need to read the stack to find clues about what the problem is and where in the code it happened.

Reading error messages

As explained in the section How to write CK-macros, the first argument to the core ck macro is always the stack. The stack is a list of pending operations and their arguments. The first item in the stack is the most recent (inner-most) pending operation, which will be resumed once the current operation is complete. The last item in the stack is the oldest (outer-most) pending operation.

You can use this like a "stack trace" to locate where in the code an error occurred. Consider the following code, which has a mistake somewhere:

(ck () (x 'x1
          'x2
          (y (c-cons 'y1 'y2)
             (z 'z1
                z2
                'z3)
             'y3
             (c-list 'y4))))

When compiled, we see an error like this:

Syntax error: no rule matches form

        (ck67 (((z (quote65 z1)) (quote z3)) ((y (quote56 (y1 . y2))) (quote y3) (c-list (quote y4))) ((x (quote30 x1) (quote33 x2)))) z2)

That looks pretty scary, but it's easier to understand if we reformat it a little:

(ck (((z 'z1) 'z3)
     ((y '(y1 . y2)) 'y3 (c-list 'y4))
     ((x 'x1 'x2)))
    z2)

The first place to look is at the bottom, the last argument to ck. That is the expression which is currently being evaluated, and therefore is the immediate cause of the error: we forgot to quote z2.

In this case, we only use the expression z2 in one place, so it is easy to locate exactly where the problem is in our code. But it's not always possible to tell where the error occurred just by looking at what argument is currently being evaluated. Sometimes you need to look at the stack, which is the first argument to ck.

In this example we know that the error occurred while evaluating arguments to z, because that is at the top of the stack. We know the value 'z1 has already been evaluated because it is inside the list with z. The expression 'z3 is outside the list, still waiting to be evaluated, after we finish evaluating the current argument.

Moving down one level of the stack, we can see that before calling z, we were evaluating arguments to y. The value '(y1 . y2) is inside the list with y, so we know it has already been evaluated; it came from the expression (c-cons 'y1 'y2). The expressions 'y3 and (c-list 'y4) are outside the list, waiting to be evaluated after we finish evaluating the call to z.

And moving one more level down the stack, we were evaluating arguments to x. The values 'x1 and 'x2 have already been evaluated, and there are no expressions waiting to be evaluated. The final argument in our call to x would be the result of the call to y, which is currently in the process of being evaluated.

Errors inside CK-macro calls

Sometimes a "no rule matches form" error will occur while evaluating a call to some CK-macro like c-cons, not a call to the core ck macro. This often indicates that the wrong number or type of arguments were passed to the CK-macro:

(ck () (c-quote (c-list '1 (c-cons '2 '3 '4) '5)))
Syntax error: no rule matches form

        (c-cons (((c-list (quote36 1)) (quote 5)) ((c-quote))) (quote45 2) (quote48 3) (quote51 4))

Notice that the error shows a call to c-cons, not ck like it did in the examples above. That indicates that each of the arguments to c-cons was evaluated with no errors, but there is some problem with the call to c-cons itself.

Like before, we can reformat the error output to make it easier to understand:

(c-cons (((c-list '1) '5)
         ((c-quote)))
        '2
        '3
        '4)

We can see that the error occurred while evaluating a call to c-cons with three arguments: '2, '3, and '4. The problem is that we passed too many arguments to c-cons.

Also, because the first argument to every CK-macro is the stack, we can read the stack to see where in the code the error happened. We can see that, before we started evaluating c-cons, we were evaluating arguments to c-list. And before that, we were evaluating c-quote.

The error can also occur if you pass the wrong type of argument:

(ck () (c-quote (c-vector->list '(1 2 3))))
Syntax error: no rule matches form

        (c-vector->list (((c-quote))) (quote36 (1 2 3)))

In this example, c-vector->list expects to be passed a vector, but we passed it a list.

Known issues and limitations

Slow compilation

CK-macros are pretty slow to compile, because the computation is done at compile time, using very deeply recursive macro expansions.

Non-portable wrappers created with ck-wrapper are probably somewhat faster than the portable CK-macros created with syntax-rules. But this library prioritizes portability over efficiency, so CK-macros are only implemented as wrappers if it is impossible to implement them using syntax-rules.

You could likely improve compilation speed in your own code by redefining various CK-macros using ck-wrapper.

Pitfalls of c-sym-eq?

c-sym-eq? has some strange and unexpected behaviors because of the way it uses let-syntax.

For example, this code will not work as expected:

(ck () (c-list 'define 'x (c-sym-eq? 'a 'a)))

You might expect it to expand to (define x #t), defining a global variable x with the value #t. But even though c-sym-eq? seems to be used "inside" the definition, the core ck macro turns things "inside out" to eagerly expand macro arguments first. So, the definition will actually appear inside of a let-syntax, and x will not be visible from outside.

These pitfalls also affect other macros that use c-sym-eq? directly or indirectly:

It is believed to be impossible to fix c-sym-eq? in a portable way, so it is recommended that you avoid using it or c-sym-equal? if possible. If you do not require strict R5RS portability, you should use the non-portable wrappers c-eq? and c-equal? instead.

ck

[syntax] (ck s 'VAL)
[syntax] (ck s `VAL)
[syntax] (ck s (OP ...))

This is the core ck macro, which implements the CK abstract machine. In version 0.3.0 and later, this macro has been enhanced to support quasiquotation.

This macro's public interface has three shapes: with a quoted value, with a quasiquoted value, and with a CK-macro call.

s
The stack, used internally by this macro. When initially invoking this macro, s should be the empty list, e.g. (ck () (c-cons '+ '(1 2))).
'VAL
A quoted value. Can be a quoted list, symbol, or other literal value. The quote is necessary, even for literal values like strings, numbers, and booleans.
`VAL
A quasiquoted value. This is syntactic sugar for (c-quasiquote 'VAL).
(OP ...)
A CK-macro call without the s argument, such as (c-cons '+ '(1 2)). Nested calls are allowed, such as (c-cons '+ (c-list '1 '2)).

ck-wrapper

[procedure] (ck-wrapper proc) → macro transformer

Wrap a procedure in a CK-macro, returning a macro transformer that can be used with define-syntax or let-syntax.

ck-wrapper requires version 0.2.0 or later of the ck-macros egg. It is considered non-portable because it depends on er-macro-transformer, which is not defined in standard R5RS. However, it should work on any Scheme which defines er-macro-transformer in the usual way.

You can wrap any other expression that evaluates to a procedure, such as a procedure name, a lambda form, a "let over lambda" expression, etc. The expression will be evaluated once, when the call to ck-wrapper is evaluated. Therefore, the procedure you are wrapping must be available in the syntax environment at macro expansion time. (In other words, you should use define-for-syntax, import-for-syntax, etc.)

Examples:

;;; Import iota from SRFI 1.
(cond-expand
  (chicken-4
   (use-for-syntax (only srfi-1 iota)))
  (chicken-5
   (import-for-syntax (only (srfi 1) iota))))

(define-syntax c-iota (ck-wrapper iota))

(ck () (c-quote (c-iota '5)))
;; ==> '(0 1 2 3 4)

;;; A helper procedure that returns a closure.
(define-for-syntax (make-adder n)
  (lambda (x) (+ x n)))

;;; Temporarily define a ck-wrapper around a closure.
(let-syntax ((c-add2 (ck-wrapper (make-adder 2))))
  ;; Map c-add2 over the result of c-iota.
  (ck () (c-quote (c-map1 '(c-add2) (c-iota '5)))))
;; ==> '(2 3 4 5 6)

Portable CK-Macros

The CK-macros in this section are defined using only standard R5RS features, such as syntax-rules and let-syntax. So, these CK-macros will work on any implementation of R5RS or later.

General

[syntax] (c-quote X) → 'X

Adds an extra level of quotation to the argument. This is useful for macros that should expand to a quoted value.

;; Without c-quote
(ck () (c-cons '+ '(1 2)))
;; Expands to (+ 1 2), which evaluates to the number 3.

;; With c-quote
(ck () (c-quote (c-cons '+ '(1 2))))
;; Expands to '(+ 1 2), a quoted list.
[syntax] (c-quasiquote X) → result

Implements quasiquotation. Added in version 0.3.0.

The core ck macro has special support for converting (quasiquote x) (also written as `x) into (c-quasiquote 'x). Within a call to c-quasiquote, you can use (unquote y) (also written as ,y) and (unquote-splicing z) (also written as ,@z). They are supported inside vectors as well as inside pairs/lists.

;; These expressions are exactly equivalent:
(ck () `(define (foo) (list ,@(c-list '1 '2) ,(c-vector '3))))
(ck () (c-quasiquote '(define (foo) (list ,@(c-list '1 '2) ,(c-vector '3)))))
;; ==> (define (foo) (list 1 2 #(3)))

(ck () `#(1 ,@(c-list '2 '3)))
(ck () (c-quasiquote '#(1 ,@(c-list '2 '3))))
;; ==> #(1 2 3)
[syntax] (c-eval '(OP ...)) → result

Takes a quoted operation and unquotes it, allowing the CK machine to expand it. Analogous to eval.

(ck () (c-quote (c-eval '(c-cons 'a 'b))))
;; ==> '(a . b)
[syntax] (c-call '(OP ...) X ...) → result

Like c-eval, but adds the given arguments on to the end of the operation. Analogous to a lambda call in normal Scheme code.

(ck () (c-quote (c-call '(c-cons 'a) 'b)))
;; ==> '(a . b)
[syntax] (c-apply '(OP ...) X ... '(Y ...)) → result

Like c-call, but the last argument is a list of more arguments. Prior to version 0.2.0, the arguments in the final list required an extra level of quoting. Analogous to apply.

(ck () (c-quote (c-apply '(c-list) 'a '(b) '(c d))))
;; ==> '(a (b) c d)
[syntax] (c-compose '((OP-N ...) ... (OP-1 ...)) X ...) → result

Compose one or more CK-macros and apply them to the arguments. Calls the right-most OP with the arguments X ..., then calls the next-right-most OP with that result, and so on:

(OP-N ... (OP-2 ... (OP-1 ... X ...)))

OP-1 must accept all the Xs as arguments, and the other OPs must each accept one argument (the result of the previous operation). See also c-rcompose, which is more efficient. Added in version 0.2.0

(ck () (c-compose '((c-car) (c-cdr)) '(1 2 3)))
;; ==> 2

;;; Map over a list of lists to see which are not empty.
(ck () (c-quote (c-map1 '(c-compose '((c-not) (c-null?)))
                        '((1) () (2 3)))))
;; ==> '(#t #f #t)
[syntax] (c-rcompose '((OP-1 ...) ... (OP-N ...)) X ...) → result

Like c-compose, but the operations are called in the reverse order (left to right). This is more efficient than c-compose. Added in version 0.2.0

[syntax] (c-flip '(OP ...) X Y) → (OP ... Y X)

Calls the operation with the two extra arguments in reverse order. Added in version 0.3.0.

[syntax] (c-branch '((OP ...) ...) X ...) → '(result ...)

Yields a list of the results of calling each operation with the Xs. Added in version 0.3.0.

(ck () (c-quote
        (c-branch '((c-list)
                    (c-cons)
                    (c-* '10))
                  '3 '5)))
;; '((3 5)
;;   (3 . 5)
;;   150)
[syntax] (c-identity X) → X

Simply yields the value as given. Sometimes useful for higher-order macros like c-filter.

(ck () (c-quote (c-identity 'a)))
;; ==> 'a
[syntax] (c-constantly X Y ...) → X

Always yields X, regardless of the other arguments. May be useful for some higher-order macros. Added in version 0.3.0.

CK-macro builders

These convenience macros are used to construct other CK-macros.

[syntax] (c-make-rules '(L ...) '(P X) ...) → '(syntax-rules ...)

Build a CK-macro based on syntax-rules. Added in version 0.3.0.

Given a list of zero or more literal symbols, and one or more pattern/expression lists, yields a syntax-rules form for a CK-macro with the following behavior:

Each pattern P is a list of zero or more sub-patterns, which will be matched (as with syntax-rules) against the already-evaluated and quoted arguments given to the new CK-macro. Alternatively, P may be an identifier which will capture all arguments as a list.

Each expression X is a single CK-macro expression or quoted value. Identifiers from the pattern can be used in the expression, as with syntax-rules.

Symbols in the literals list (L ...) will be treated as literal identifiers in patterns. Additionally, quote is always treated as a literal identifier.

Caveats:

;; Contrived example
(ck () `(define-syntax c-math
          ,(c-make-rules '(add subtract)
            '(('add 'x 'y)
              (c-+ 'x 'y))
            '(('subtract 'x 'y)
              (c-- 'x 'y)))))

;; The above is basically equivalent to:
;;   (define-syntax c-math
;;     (syntax-rules (add subtract quote)
;;       ((_ s 'add 'x 'y)
;;        (ck s (c-+ 'x 'y)))
;;       ((_ s 'subtract 'x 'y)
;;        (ck s (c-- 'x 'y)))))

(ck () (c-quote (c-math 'add '3 '1)))
;; ==> 4

(ck () (c-quote (c-math 'subtract '3 '1)))
;; ==> 2
[syntax] (c-make-next '(X1 X2 ...)) → '(syntax-rules ...)

Build a CK-macro that yields the next item in a sequence. Added in version 0.3.0.

Given a list of unique items, yields a syntax-rules form for a CK-macro with the following behavior:

E.g. with a list of increasing integers, the CK-macro behaves like c-dadd1. With a list of decreasing integers, it behaves like c-dsub1.

The list must have at least two items, with no duplicates. The items should be literal constants: booleans, numbers, strings, characters; or (possibly nested) pairs, lists, or vectors of those things. Symbols are allowed but the result may not be what you expect because they are treated as identifiers in the patterns.

Be advised that compilation can be very slow if there are many items because it generates a syntax-rules with many branches.

(ck () `(define-syntax c-next-square
          ,(c-make-next '(0 1 4 9 16))))

;; The above is basically equivalent to:
;; (define-syntax c-next-square
;;  (syntax-rules (quote)
;;    ((_ s '0) (ck s '1))
;;    ((_ s '1) (ck s '4))
;;    ((_ s '4) (ck s '9))
;;    ((_ s '9) (ck s '16))
;;    ((_ s otherwise) (ck s '#f))))

(ck () (c-next-square '4))
;; ==> '9
(ck () (c-next-square '16))
;; ==> '#f

Boolean Logic

[syntax] (c-not X) → '#t or '#f

Yields '#t if the argument is '#f, otherwise yields '#f. Analogous to not.

[syntax] (c-true X ...) → '#t

Always yields '#t, regardless of its arguments. May be useful for some higher-order macros. Equivalent to (c-constantly '#t X ...). Added in version 0.2.0.

;;; Recursively check if two structure have the same length,
;;; nesting, etc, while ignoring the value of the atoms.
(ck () (c-compare? '(c-true) '(1 #(2) 3) '(a #(b) c)))
;; ==> '#t
[syntax] (c-false X ...) → '#f

Always yields '#f, regardless of its arguments. May be useful for some higher-order macros. Equivalent to (c-constantly '#f X ...). Added in version 0.2.0.

[syntax] (c-if TEST PASS FAIL) → PASS or FAIL

Conditional branching. If TEST is '#f, this yields FAIL. Otherwise it yields PASS.

Due to the way the CK machine works, both branches will be expanded, then the unneeded branch will be discarded. If you only want the needed branch to be expanded (e.g. because the branches are complex and slow to expand, or because it would be an error to expand the unneeded branch), use c-if* instead.

Analogous to (lambda (test pass fail) (if test pass fail)).

(ck () (c-quote (c-if (c-pair? '(x))
                      'pair
                      'not-pair)))
;; ==> 'pair

(ck () (c-quote (c-if (c-pair? 'x)
                      'pair
                      'not-pair)))
;; ==> 'not-pair
[syntax] (c-if* TEST 'PASS 'FAIL) → PASS or FAIL

Similar to c-if, except that the branches must have an extra level of quoting, and only one branch will be expanded. This is more similar to how if behaves, but it is a bit awkward to use.

Analogous to (lambda (test pass fail) (if test (eval pass) (eval fail)))

(ck () (c-quote (c-if* (c-pair? '(x))
                       '(c-car '(x))
                       ''not-pair)))
;; ==> 'x

(ck () (c-quote (c-if* (c-pair? 'x)
                       '(c-car 'x)
                       ''not-pair)))
;; ==> 'not-pair
[syntax] (c-or X ...) → item or '#f

Yields the first argument that is not '#f. Yields '#f if all of the arguments are '#f, or if there are no arguments.

Roughly analogous to or, except all arguments are expanded. If you only want to expand the arguments that are needed, use c-or* instead.

[syntax] (c-or* 'X ...) → item or '#f

Similar to c-or, except that all the arguments must have an extra level of quoting, and the arguments will be expanded one at a time until a non-'#f value is found. This is more similar to how or behaves, but it is a bit awkward to use.

[syntax] (c-and X ...) → item or '#f

If all arguments are not '#f, yields the last argument. If any of the arguments is '#f, yields '#f. If there are no arguments, yields '#t.

Roughly analogous to and, except all arguments are expanded. If you only want to expand the arguments that are needed, use c-and* instead.

[syntax] (c-and* X ...) → item or '#f

Similar to c-and, except that all the arguments must have an extra level of quoting, and the arguments will be expanded one at a time until a '#f value is found. This is more similar to how and behaves, but it is a bit awkward to use.

[syntax] (c-null? X) → '#t or '#f

Yields '#t if X is the empty list, '(). Otherwise yields '#f. Analogous to null?.

[syntax] (c-pair? X) → '#t or '#f

Yields '#t if X is a dotted pair or a non-empty list. Otherwise yields '#f. Analogous to pair?.

[syntax] (c-not-pair? X) → '#t or '#f

Opposite of c-pair?. Analogous to not-pair? from SRFI 1.

[syntax] (c-vector? X) → '#t or '#f

Yields '#t if X is a vector. Otherwise yields '#f. Analogous to vector?.

(ck () (c-quote (c-vector? '#(a))))
;; ==> '#t
[syntax] (c-boolean? X) → '#t or '#f

Yields '#t if X is either '#t or '#f. Otherwise yields '#f. Analogous to boolean?.

[syntax] (c-sym-eq? X Y) → '#t or '#f

ATTENTION: This CK-macro has major pitfalls that you should be aware of. If you do not require strict R5RS portability, it is recommended to use c-eq? instead.

Yields '#t if X and Y are the same symbol, otherwise yields '#f. X should be a symbol. Y can be any value. Some Scheme implementations allow X to be other types, but this macro is only portable if X is a symbol.

Roughly analogous to eq?, except it only works (portably) with symbols. Based on symbol-eq? from the original implementation.

[syntax] (c-sym-equal? X Y) → '#t or '#f

ATTENTION: This CK-macro has major pitfalls that you should be aware of. If you do not require strict R5RS portability, it is recommended to use c-equal? instead.

Similar to c-sym-eq?, except it recursively compares pairs, lists, and vectors.

Roughly analogous to equal?, except it only works (portably) with symbols, pairs, lists, vectors, and nested combinations of those things.

[syntax] (c-compare? '(OP ...) X Y) → '#t or '#f

Recursively compares atoms, pairs, lists, or vectors, using OP as the predicate to compare atoms. Similar to equal? but with a custom predicate. Added in version 0.2.0.

OP will be called with two arguments: an atom of X, and the corresponding atom of Y. In other words, the Nth atom of X will be compared with the Nth atom of Y, descending recursively into nested structures. If X and Y are themselves atoms, they are compared directly with OP.

Yields '#f if X and Y have dissimilar structures (length, nesting, type), or if OP yields '#f for any corresponding atoms of X and Y. Otherwise yields '#t.

(ck () (c-compare? '(c-string-ci=?) '#("a" ("b")) '#("A" ("B"))))
;; ==> #t

;;; X is a vector with a list, but Y is a list with a vector.
;;; The structures are dissimilar.
(ck () (c-compare? '(c-string-ci=?) '#("a" ("b")) '("a" #("b"))))
;; ==> #f

;;; Can use any predicate. Here, X and Y have same structure,
;;; and each atom of X is less than the correponding atom of Y.
(ck () (c-compare? '(c-<) '(1 #(5)) '(2 #(6))))
;; ==> #t

;;; Can compare atoms directly.
(ck () (c-compare? '(c-<) '1 '2))
;; ==> #t

List Processing

[syntax] (c-cons X Y) → '(X . Y)

Yields a pair with the two given arguments. Analogous to cons.

(ck () (c-quote (c-cons 'a 'b)))
;; ==> '(a . b)

(ck () (c-quote (c-cons '+ '(1 2))))
;; ==> '(+ 1 2)

(ck () (c-quote (c-cons '+ (c-cons '1 (c-cons '2 '())))))
;; ==> '(+ 1 2)
[syntax] (c-cons* X ... Y Z) → '(X ... Y . Z)

Yields a list from consing all the arguments together in a chain. If the final argument Z is a list, the result will be a proper list. Otherwise it will be a "dotted list". Analogous to cons* from SRFI 1. Added in version 0.3.0.

(ck () (c-quote (c-cons* 'a 'b 'c 'd)))
;; ==> '(a b c . d)
(ck () (c-quote (c-cons* 'a 'b 'c '(d))))
;; ==> '(a b c d)
[syntax] (c-xcons X Y) → '(Y . X)

Like c-cons, but exchanges the order of arguments. Analogous to xcons from SRFI 1. Added in version 0.2.0.

(ck () (c-quote (c-xcons 'a 'b)))
;; ==> '(b . a)
[syntax] (c-list X ...) → list

Yields a list containing the given items. Analogous to list.

(ck () (c-quote (c-list)))
;; ==> '()
(ck () (c-quote (c-list 'a 'b 'c)))
;; ==> '(a b c)
[syntax] (c-car P) → item

Yields the head of the given pair. Analogous to car.

(ck () (c-quote (c-car '(a . b))))
;; ==> 'a

(ck () (c-quote (c-car '(a b))))
;; ==> 'a
[syntax] (c-cdr P) → tail

Yields the tail of the given pair. Analogous to cdr.

(ck () (c-quote (c-cdr '(a . b))))
;; ==> 'b

(ck () (c-quote (c-cdr '(a b))))
;; ==> '(b)
[syntax] (c-first L) → item
[syntax] (c-second L) → item
[syntax] (c-third L) → item
[syntax] (c-fourth L) → item
[syntax] (c-fifth L) → item
[syntax] (c-sixth L) → item
[syntax] (c-seventh L) → item
[syntax] (c-eighth L) → item
[syntax] (c-ninth L) → item
[syntax] (c-tenth L) → item

Yields the Nth item of the given list. Fails if the list is too short.

Analogous to first ... tenth from SRFI 1.

(ck () (c-quote (c-first  '(a b c d e f g h i j k))))  ; ==> 'a
(ck () (c-quote (c-second '(a b c d e f g h i j k))))  ; ==> 'b
(ck () (c-quote (c-third  '(a b c d e f g h i j k))))  ; ==> 'c
;;; ...
(ck () (c-quote (c-tenth  '(a b c d e f g h i j k))))  ; ==> 'j
[syntax] (c-last L) → item

Yields the last value of the given list. Fails if the list is empty or is not a proper list.

Analogous to last from SRFI 1.

(ck () (c-quote (c-last '(a b c))))    ; ==> 'c
(ck () (c-quote (c-last '(a b . c))))  ; ==> ERROR!
[syntax] (c-last-pair L) → pair

Yields the last pair of the given list. Fails if the list is empty.

Analogous to last-pair from SRFI 1.

(ck () (c-quote (c-last-pair '(a b c))))    ; ==> '(c)
(ck () (c-quote (c-last-pair '(a b . c))))  ; ==> '(b . c)
[syntax] (c-drop1 L) → list
[syntax] (c-drop2 L) → list
[syntax] (c-drop3 L) → list
[syntax] (c-drop4 L) → list
[syntax] (c-drop5 L) → list

Drops a predefined number of items from the front of the given list. Fails if the list is too short. See also c-udrop.

Analogous to (drop L N) from SRFI 1.

(ck () (c-quote (c-drop1 '(a b c d e f g))))  ; ==> '(b c d e f g)
(ck () (c-quote (c-drop2 '(a b c d e f g))))  ; ==> '(c d e f g)
(ck () (c-quote (c-drop3 '(a b c d e f g))))  ; ==> '(d e f g)
(ck () (c-quote (c-drop4 '(a b c d e f g))))  ; ==> '(e f g)
(ck () (c-quote (c-drop5 '(a b c d e f g))))  ; ==> '(f g)
[syntax] (c-take1 L) → list
[syntax] (c-take2 L) → list
[syntax] (c-take3 L) → list
[syntax] (c-take4 L) → list
[syntax] (c-take5 L) → list

Yields a list containing a predefined number of items from the front of the given list. Fails if the list is too short. See also c-utake.

Analogous to (take L N) from SRFI 1.

(ck () (c-quote (c-take1 '(a b c d e f g))))  ; ==> '(a)
(ck () (c-quote (c-take2 '(a b c d e f g))))  ; ==> '(a b)
(ck () (c-quote (c-take3 '(a b c d e f g))))  ; ==> '(a b c)
(ck () (c-quote (c-take4 '(a b c d e f g))))  ; ==> '(a b c d)
(ck () (c-quote (c-take5 '(a b c d e f g))))  ; ==> '(a b c d e)
[syntax] (c-reverse L) → list

Yields the given list in reverse order. Fails if the list is not a proper list. Analogous to reverse.

(ck () (c-quote (c-reverse '(a b c))))
;; ==> '(c b a)
[syntax] (c-prefix L X ...) → list

Yields the given list with the extra arguments added to the front. Added in version 0.3.0.

(ck () (c-quote (c-prefix '(c d) 'a 'b)))
;; ==> '(a b c d)
[syntax] (c-suffix L X ...) → list

Yields the given list with the extra arguments added to the end.

(ck () (c-quote (c-suffix '(a b) 'c 'd)))
;; ==> '(a b c d)
[syntax] (c-append L ...) → list

Appends the given lists. Analogous to append.

(ck () (c-quote (c-append)))
;; ==> '()

(ck () (c-quote (c-append '(+) (c-append '(1) '(2)))))
;; ==> '(+ 1 2)

(ck () (c-quote (c-append '(define foo) '((+ 1 2)))))
;; ==> '(define foo (+ 1 2 3))
[syntax] (c-append-map1 '(OP ...) L) → list

Yields a list by calling the quoted operation on each item in the list, then appending the results. The operation must be a CK-macro that yields a list.

Analogous to append-map from SFRI-1, but only accepts one list. Prior to version 0.2.0, this was named c-append-map. This was named c-concatMap in the original implementation.

(ck () (c-quote (c-append-map1 '(c-list 'a 'b) '(1 2))))
;; ==> '(a b 1 a b 2)
[syntax] (c-map1 '(OP ...) L) → list

Yields a list by calling the quoted operation on each item in the given list. Analogous to map, but only accepts one list. (See also c-map2 ... c-map5 for versions that accept more lists.) Prior to version 0.2.0, this was named c-map.

(ck () (c-quote (c-map1 '(c-cons 'a) '(1 2))))
;; ==> '((a . 1) (a . 2))
[syntax] (c-map2 '(OP ...) L1 L2) → list
[syntax] (c-map3 '(OP ...) L1 L2 L3) → list
[syntax] (c-map4 '(OP ...) L1 L2 L3 L4) → list
[syntax] (c-map5 '(OP ...) L1 L2 L3 L4 L5) → list

Like c-map1, but they accept exactly two, three, four, or five lists respectively. OP must accept two, three, four, or five extra arguments. If the lists are different lengths, terminates when the shortest list runs out. Analogous to map from SRFI 1, but they accept a specific number of lists. Added in version 0.2.0.

;; The argument '(1 2) is shortest so the result only has two items.
(ck () (c-quote (c-map3 '(c-list) '(a b c) '(1 2) '(x y z))))
;; ==> '((a 1 x) (b 2 y))
[syntax] (c-fold1 '(OP ...) INIT L) → result

Yield a value by repeatedly calling the quoted operation with each item from the list and the previous result.

If the list is empty, yields INIT. Otherwise, the operation is first called with two arguments: the first item of the list, and INIT. Then, the operation is repeatedly called with the next item of the list and the previous result, until it reaches the end of the list. Yields the final result.

Analogous to fold from SRFI 1, but only accepts one list. Prior to version 0.2.0, this was named c-fold.

(ck () (c-quote (c-fold1 '(c-cons) '(x) '())))
;; ==> '(x)
(ck () (c-quote (c-fold1 '(c-cons) '(x) '(a b c d e f))))
;; ==> '(f e d c b a x)
[syntax] (c-unfold '(P ...) '(F ...) '(G ...) SEED) → list
[syntax] (c-unfold '(P ...) '(F ...) '(G ...) SEED '(TAIL-GEN ...)) → list

Generate a list by recursively "unfolding" from a seed. Analogous to unfold from SRFI 1. Added in version 0.3.0.

Takes several operations which are called with the seed:

(ck () (c-quote
        (c-unfold '(c-> '1)                        ; P
                  '(c-identity)                    ; F
                  '(c-dsub1)                       ; G
                  '10                              ; SEED
                  '(c-constantly '(BLASTOFF!)))))  ; TAIL-GEN
;; ==> '(10 9 8 7 6 5 4 3 2 1 BLASTOFF!)
[syntax] (c-filter '(OP ...) L) → list

Yields a list by calling the predicate operation on each item in the list, and discarding any item for which the test yields '#f. Analogous to filter from SRFI 1.

(ck () (c-quote (c-filter '(c-pair?)
                          '(a (b . c) 1 (d e) #t))))
;; ==> '((b . c) (d e))
[syntax] (c-remove '(OP ...) L) → list

Opposite of c-filter. Discards items that pass the test, keeps items that fail the test. Analogous to remove from SRFI 1.

(ck () (c-quote (c-remove '(c-pair?)
                          '(a (b . c) 1 (d e) #t))))
;; ==> '(a 1 #t)
[syntax] (c-find '(OP ...) L) → item or '#f

Searches the list for the first item that passes the predicate operation (i.e. the predicate yields a non-'#f value), then yields that item. Yields '#f if no item passes the predicate.

Analogous to find from SRFI 1.

(ck () (c-quote (c-find '(c-pair?)
                        '(a (b . c) 1 (d e) #t))))
;; ==> '(b . c)
[syntax] (c-find-tail '(OP ...) L) → pair or '#f

Searches the list for the first item that passes the predicate operation (i.e. the predicate yields a non-'#f value), then yields the tail of the list starting with that item. Yields '#f if no item passes the predicate.

Analogous to find-tail from SRFI 1.

(ck () (c-quote (c-find-tail '(c-pair?)
                             '(a (b . c) 1 (d e) #t))))
;; ==> '((b . c) 1 (d e) #t)
[syntax] (c-member X L) → '#t or '#f
[syntax] (c-member X L '(OP ...)) → '#t or '#f

ATTENTION: When using the default comparison operator, this CK-macro has major pitfalls that you should be aware of. If you do not require strict R5RS portability, it is recommended to use the comparison operator '(c-equal?) instead.

Searches the list for the first occurance of X, then yields the tail of the list starting with that item. Yields '#f if the list does not contain X.

Uses '(OP ...) for comparison, or '(c-sym-equal?) if the operation is omitted. So by default, X must be a symbol, list, pair, vector, or nested combination of those things.

Same as (c-find-tail '(OP ... X) L). Roughly analogous to member except for the default allowed types.

(ck () (c-quote (c-member 'b '(a b c))))
;; ==> '(b c)

(ck () (c-quote (c-member 'x '(a b c))))
;; ==> '#f

(ck () (c-quote (c-member '(a b c)
                          '((a) (x y z) (a b))
                          '(c-u=))))
;; ==> '((x y z) (a b))
;; Because (c-u= '(a b c) '(x y z)) yields '#t
[syntax] (c-any1 '(OP ...) L) → result or '#f

Calls the operation on each value in the given list until it finds a result that is not '#f, then yields that result. Yields '#f if the predicate yields '#f for all items in the list, or if the list is empty.

Analogous to any from SRFI 1, but only accepts one list. Prior to version 0.2.0, this was named c-any.

(ck () (c-quote (c-any1 '(c-pair?) '())))
;; ==> '#f
(ck () (c-quote (c-any1 '(c-pair?) '(a b c))))
;; ==> '#f
(ck () (c-quote (c-any1 '(c-pair?) '(a (b . c)))))
;; ==> '#t

(ck () (c-quote (c-any1 '(c-cons 'z) '(a b c))))
;; ==> '(1 . a)
;; Because (c-cons 'z 'a) yields a value that is not '#f.
[syntax] (c-every1 '(OP ...) L) → result or '#f

Calls the operation on each value in the given list until it finds a result that is '#f, then yields '#f. If the predicate yields a non-'#f value for every item in the list, this yields the result of calling the predicate on the last item. Yields '#t if the list is empty.

Analogous to every from SRFI 1, but only accepts one list. Prior to version 0.2.0, this was named c-every.

(ck () (c-quote (c-every1 '(c-pair?) '())))
;; ==> '#t
(ck () (c-quote (c-every1 '(c-pair?) '(a (b . c)))))
;; ==> '#f
(ck () (c-quote (c-every1 '(c-pair?) '((a . b) (b . c)))))
;; ==> '#t

(ck () (c-quote (c-every1 '(c-cons 'z) '(a b c))))
;; ==> '(z . c)
;; Because all results were non-'#f and (c-cons 'z 'c) was the final operation.
[syntax] (c-assoc KEY ALIST) → pair or '#f
[syntax] (c-assoc KEY ALIST '(OP ...)) → pair or '#f

ATTENTION: When using the default comparison operator, this CK-macro has major pitfalls that you should be aware of. If you do not require strict R5RS portability, it is recommended to use the comparison operator '(c-equal?) instead.

Searches ALIST for the first pair whose car matches KEY, then yields that pair. Yields '#f if no match is found. ALIST must be an association list, i.e. a list of pairs.

Uses '(OP ...) for comparison, or '(c-sym-equal?) if '(OP ...) is omitted.

Analogous to assoc from SRFI 1.

(ck () (c-quote (c-assoc 'x '((a . 1) (b . 2) (a . 3)))))
;; ==> '#f
(ck () (c-quote (c-assoc 'a '((a . 1) (b . 2) (a . 3)))))
;; ==> '(a . 1)
(ck () (c-quote (c-assoc '(a) '((a . 1) (b . 2) ((a) . 3)))))
;; ==> '((a) . 3)
[syntax] (c-alist-delete KEY ALIST) → list
[syntax] (c-alist-delete KEY ALIST '(OP ...)) → list

ATTENTION: When using the default comparison operator, this CK-macro has major pitfalls that you should be aware of. If you do not require strict R5RS portability, it is recommended to use the comparison operator '(c-equal?) instead.

Removes all pairs in ALIST whose car matches KEY. ALIST must be an association list, i.e. a list of pairs.

Uses '(OP ...) for comparison, or '(c-sym-equal?) if '(OP ...) is omitted.

Analogous to alist-delete from SRFI 1. Based on c-delete-assoc from the original implementation.

(ck () (c-quote (c-alist-delete 'a '((a . 1) (b . 2) (a . 3) (c . 4)))))
;; ==> '((b . 2) (c . 4)
(ck () (c-quote (c-alist-delete '(a) '((a . 1) (b . 2) ((a) . 3)))))
;; ==> '((a . 1) (b . 2))

Vector Processing

[syntax] (c-vector X ...) → vector

Yields a vector containing the given items. Analogous to vector.

[syntax] (c-list->vector L) → vector

Yields a vector containing the same items as the given list. Analogous to list->vector from SRFI 43.

[syntax] (c-vector->list V) → list

Yields a list containing the same items as the given vector. Analogous to vector->list from SRFI 43.

[syntax] (c-vector-reverse V) → vector

Yields the given vector in reverse order. Similar to vector-reverse-copy from SRFI 43, but does not take a start or end argument.

[syntax] (c-vector-prefix V X ...) → list

Yields the given vector with the extra arguments added to the front. Added in version 0.3.0.

(ck () (c-quote (c-vector-prefix '#(c d) 'a 'b)))
;; ==> '#(a b c d)
[syntax] (c-vector-suffix V X ...) → vector

Yields the given vector with the extra arguments added to the end.

(ck () (c-quote (c-vector-suffix '#(a b) 'c 'd)))
;; ==> '#(a b c d)
[syntax] (c-vector-append V ...) → vector

Appends the given vectors. Analogous to vector-append from SRFI 43, but only accepts two vectors.

(ck () (c-quote (c-vector-append)))
;; ==> '#()

(ck () (c-quote (c-vector-append '#(a b) '#(c d) '#(e f))))
;; ==> '#(a b c d e f)
[syntax] (c-vector-map1 '(OP ...) V) → vector

Yields a vector by calling the quoted operation on each item in the given vector. Analogous to vector-map from SRFI 43, but only accepts one vector. Prior to version 0.2.0, this was named c-vector-map.

Unary Math

The CK-macros in this section perform mathematical operations by treating lists as unary numbers. Unary math is pretty slow for large values or complex operations, but it is interesting, portable, and maybe even useful in some cases. If you do not require strict R5RS portability, you may prefer to use the number and math wrappers.

Unary numbers are a way of representing non-negative integers as a list of a certain length. For example, the list '(a b c d e) means the number 5, and the list '() means the number 0. The contents of the list do not matter, only the length. Negative numbers and non-integral numbers cannot be represented in unary.

[syntax] (c-u= U1 U2) → '#t or '#f

Unary equality. Yields '#t if the two lists have the same lengths, otherwise yields '#f.

(ck () (c-quote (c-u= '(a b c) '(a b c))))
;; ==> '#t
(ck () (c-quote (c-u= '(1 2 3) '(a b c))))
;; ==> '#t
(ck () (c-quote (c-u= '(1 2) '(a b c))))
;; ==> '#f
[syntax] (c-u< U1 U2) → '#t or '#f

Unary less-than. Yields '#t if the first list is shorter than the second list, otherwise yields '#f.

(ck () (c-quote (c-u< '(1 2) '(a b c))))
;; ==> '#t
(ck () (c-quote (c-u< '(1 2 3) '(a b c))))
;; ==> '#f
[syntax] (c-u<= U1 U2) → '#t or '#f

Unary less-than-or-equals. Yields '#t if first list is the same length or shorter than the second list, otherwise yields '#f.

(ck () (c-quote (c-u<= '(1 2) '(a b c))))
;; ==> '#t
(ck () (c-quote (c-u<= '(1 2 3) '(a b c))))
;; ==> '#t
(ck () (c-quote (c-u<= '(1 2 3 4) '(a b c))))
;; ==> '#f
[syntax] (c-u> U1 U2) → '#t or '#f

Unary greater-than. Yields '#t if the first list is longer than the second list, otherwise yields '#f.

(ck () (c-quote (c-u> '(1 2 3 4) '(a b c))))
;; ==> '#t
(ck () (c-quote (c-u> '(1 2 3) '(a b c))))
;; ==> '#f
[syntax] (c-u>= U1 U2) → '#t or '#f

Unary greater-than-or-equals. Yields '#t if first list is same length or longer than the second list, otherwise yields '#f.

(ck () (c-quote (c-u>= '(1 2 3 4) '(a b c))))
;; ==> '#t
(ck () (c-quote (c-u>= '(1 2 3) '(a b c))))
;; ==> '#t
(ck () (c-quote (c-u>= '(1 2) '(a b c))))
;; ==> '#f
[syntax] (c-uzero? U) → '#t or '#f

Unary zero?. Yields '#t if the list is empty, otherwise yields '#f. Same as c-null?.

(ck () (c-quote (c-uzero? '())))
;; ==> '#t
(ck () (c-quote (c-uzero? '(a))))
;; ==> '#f
[syntax] (c-ueven? U) → '#t or '#f

Unary even?. Yields '#t if the given list's length is even (i.e. a multiple of 2), otherwise yields '#f.

(ck () (c-quote (c-ueven? '())))
;; ==> '#t
(ck () (c-quote (c-ueven? '(a))))
;; ==> '#f
(ck () (c-quote (c-ueven? '(a b))))
;; ==> '#t
[syntax] (c-uodd? U) → '#t or '#f

Unary odd?. Yields '#t if the given list's length is odd (i.e. not a multiple of 2), otherwise yields '#f.

(ck () (c-quote (c-uodd? '())))
;; ==> '#f
(ck () (c-quote (c-uodd? '(a))))
;; ==> '#t
(ck () (c-quote (c-uodd? '(a b))))
;; ==> '#f
[syntax] (c-u+ U1 U2) → list

Unary addition. Same as c-append. This was named c-add in the original implementation.

(ck () (c-quote (c-u+ '(a b) '(c))))
;; ==> '(a b c)
[syntax] (c-u- U1 U2) → list

Unary subtraction. Drops an element from the front of the first list for each element in second list, then yields the remaining list. Negative numbers cannot be represented in unary, so this yields '() if the second list is equal or longer than the first.

(ck () (c-quote (c-u- (c-list 'a 'b 'c 'd) '(x y))))
;; ==> '(c d)

(ck () (c-quote (c-u- (c-list 'a 'b) (c-list 'x 'y 'z))))
;; ==> '()
;; Because negative numbers cannot be represented in unary.
[syntax] (c-u* U1 U2) → list

Unary multiplication. Yields a list containing the contents of the first list, repeated once for every item in the second list.

Based on c-mul from the original implementation, except the symbol 'u has no special significance, and result is made from duplicating the first list.

(ck () (c-quote (c-u* '(a b) '(c d e))))
;; ==> '(a b a b a b)
[syntax] (c-u/ U1 U2) → (list list)

Unary division. Yields a list of two unary numbers, representing the quotient and the remainder of the division.

Given the second list has length N, the quotient will contain every Nth item from the first list, and the remainder will contain the tail of the first list. Division by zero (empty list) is a syntax error.

(ck () (c-quote (c-u/ '(a b c d e f g h i j k)
                      '(x y z))))
;; ==> '((g d a) (j k))
;; Because 11 / 3 = 3 with a remainder of 2.
[syntax] (c-ufactorial U) → list

Unary factorial. If the given list has length zero, yields the list '(u). If the given list has length one, yields the given list. Otherwise, yields a list containing items of the given list repeated (N-1)! times, where N is the length of the given list. This was named c-fact in the original implementation.

(ck () (c-quote (c-ufactorial '(a b c))))
;; ==> '(a b c a b c)
;; Because 3! = 6.
[syntax] (c-udrop L U) → list

Drops up to U items from the front of the given list, where U is a unary number.

Same as c-u-. Analogous to drop from SRFI 1, but uses unary numbers, and yields the empty list if the list is too short.

(ck () (c-quote (c-udrop (c-list 'a 'b 'c 'd) '(x y))))
;; ==> '(c d)
(ck () (c-quote (c-udrop (c-list 'a 'b) (c-list 'x 'y 'z))))
;; ==> '()
[syntax] (c-utake L U) → list

Yields a list containing up to U items from the front of the given list, where U is a unary number.

Analogous to take from SRFI 1, but uses unary numbers, and yields the entire list if it is too short.

(ck () (c-quote (c-utake '(a b c d) '(x y z))))
;; ==> '(a b c)
(ck () (c-quote (c-utake '(a b) '(x y z))))
;; ==> '(a b)

Decimal Integers

[syntax] (c-dadd1 N) → 'N+1 or '#f

Add 1 to a decimal integer. If 0 <= N <= 15, yields N + 1. Otherwise yields '#f. Added in version 0.3.0.

(ck () (c-dadd1 '0))
;; ==> '1
(ck () (c-dadd1 '15))
;; ==> '16
(ck () (c-dadd1 '16))
;; ==> '#f

The default domain is small because library compilation time increases rapidly the larger the domain is, because it generates a syntax-rules with many branches. You can define an add1 with a larger (or just different) domain using c-make-next and a list of increasing integers:

(ck () `(define-syntax c-my-add1
          ,(c-make-next '(-5 -4 -3 -2 -1 0 1 2 3 4 5))))
[syntax] (c-dsub1 N) → 'N-1 or '#f

Subtract 1 from a decimal integer. If 1 <= N <= 16, yields N - 1. Otherwise yields '#f. See the notes for c-dadd1.

(ck () (c-dsub1 '16))
;; ==> '15
(ck () (c-dsub1 '1))
;; ==> '0
(ck () (c-dsub1 '0))
;; ==> '#f

As with c-dadd1, you can define a sub1 with a larger (or just different) domain using c-make-next and a list of decreasing integers:

(ck () `(define-syntax c-my-sub1
          ,(c-make-next '(5 4 3 2 1 0 -1 -2 -3 -4 -5))))
[syntax] (c-du N) → list or '#f
[syntax] (c-du N '(SUB1 ...)) → list or '#f

Convert from decimal integers to unary integers. If N is in the domain of SUB1, yields a list of that many elements, specifically the integers (N-1, ..., 0). Otherwise, yields '#f.

SUB1 is any operation that given N yields N-1, where 1 <= N. Defaults to c-dsub1, which supports 1 <= N <= 16. See the note for c-dsub1 for how to make an operation with a larger domain.

(ck () (c-quote (c-du '0)))
;; ==> '()
(ck () (c-quote (c-du '16)))
;; ==> '(15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0)
(ck () (c-quote (c-du '17)))
;; ==> '#f
[syntax] (c-ud U) → integer or '#f
[syntax] (c-ud U '(ADD1 ...)) → integer or '#f

Convert from unary integers to decimal integers. Given a list of items, yields the list's length as a decimal integer. The contents of the list do not matter, only its length. If the list length is too large for ADD1, yields '#f.

ADD1 is any operation that given N yields N+1, where 0 <= N. Defaults to c-dadd1, which supports 0 <= N <= 15. See the note for c-dadd1 for how to make an operation with a larger domain.

(ck () (c-ud '()))
;; ==> '0
(ck () (c-ud '(a b c d e f g h i j k l m n o p)))
;; ==> '16
(ck () (c-ud '(a b c d e f g h i j k l m n o p q)))
;; ==> '#f

Non-portable R5RS Wrappers

These are CK-macros that wrap some R5RS procedures that are useful for building macros. Not every R5RS procedure is provided here. If you need other procedures, use ck-wrapper to create your own wrappers.

These wrappers are considered non-portable because they use ck-wrapper, which is not portable to all R5RS Scheme implementations. See the ck-wrapper docs for portability information.

Some R5RS procedures have portable, non-wrapper CK-macro equivalents, which are described in the Portable CK-macros section, above. For example, there is no wrapper for pair? listed below, because c-pair? is a portable CK-macro listed above.

General (R5RS Wrappers)

[syntax] (c-eqv? X Y) → '#t or '#f

CK-macro wrapper for eqv?. Added in version 0.2.0.

[syntax] (c-eq? X Y) → '#t or '#f

CK-macro wrapper for eq?. Added in version 0.2.0.

[syntax] (c-equal? X Y) → '#t or '#f

CK-macro wrapper for equal?. Added in version 0.2.0.

Numbers and Math (R5RS Wrappers)

[syntax] (c-number? X) → '#t or '#f

CK-macro wrapper for number?. Added in version 0.2.0.

[syntax] (c-integer? X) → '#t or '#f

CK-macro wrapper for integer?. Added in version 0.2.0.

[syntax] (c-= N ...) → '#t or '#f

CK-macro wrapper for = (equal). Added in version 0.2.0.

[syntax] (c-< N ...) → '#t or '#f

CK-macro wrapper for < (less than). Added in version 0.2.0.

[syntax] (c-> N ...) → '#t or '#f

CK-macro wrapper for > (greater than). Added in version 0.2.0.

[syntax] (c-<= N ...) → '#t or '#f

CK-macro wrapper for <= (less than or equal). Added in version 0.2.0.

[syntax] (c->= N ...) → '#t or '#f

CK-macro wrapper for >= (greater than or equal). Added in version 0.2.0.

[syntax] (c-max N ...) → number

CK-macro wrapper for max. Added in version 0.2.0.

[syntax] (c-min N ...) → number

CK-macro wrapper for min. Added in version 0.2.0.

[syntax] (c-+ N ...) → number

CK-macro wrapper for + (addition). Added in version 0.2.0.

[syntax] (c-* N ...) → number

CK-macro wrapper for * (multiplication). Added in version 0.2.0.

[syntax] (c-- N ...) → number

CK-macro wrapper for - (subtraction). Added in version 0.2.0.

[syntax] (c-/ N ...) → number

CK-macro wrapper for / (division). Added in version 0.2.0.

[syntax] (c-remainder N1 N2) → number

CK-macro wrapper for remainder. Added in version 0.2.0.

[syntax] (c-floor N) → number

CK-macro wrapper for floor. Added in version 0.2.0.

[syntax] (c-round N) → number

CK-macro wrapper for round. Added in version 0.2.0.

[syntax] (c-exact->inexact N) → inexact number

CK-macro wrapper for exact->inexact. Added in version 0.2.0.

[syntax] (c-inexact->exact N) → exact number

CK-macro wrapper for inexact->exact. Added in version 0.2.0.

[syntax] (c-number->string N) → string
[syntax] (c-number->string N RADIX) → string

CK-macro wrapper for number->string. Added in version 0.2.0.

[syntax] (c-string->number STR) → number or '#f
[syntax] (c-string->number STR RADIX) → number or '#f

CK-macro wrapper for string->number. Added in version 0.2.0.

Pairs and Lists (R5RS Wrappers)

[syntax] (c-length L) → integer

CK-macro wrapper for length. Added in version 0.2.0.

[syntax] (c-list-ref L I) → item

CK-macro wrapper for list-ref. Added in version 0.2.0.

Symbols (R5RS Wrappers)

[syntax] (c-symbol? X) → '#t or '#f

CK-macro wrapper for symbol?. Added in version 0.2.0.

[syntax] (c-symbol->string SYM) → string

CK-macro wrapper for symbol->string. Added in version 0.2.0.

[syntax] (c-string->symbol STR) → symbol

CK-macro wrapper for string->symbol. Added in version 0.2.0.

Chars and Strings (R5RS Wrappers)

[syntax] (c-char? CHAR ...) → '#t or '#f

CK-macro wrapper for char?. Added in version 0.2.0.

[syntax] (c-char=? CHAR ...) → '#t or '#f

CK-macro wrapper for char=?. Added in version 0.2.0.

[syntax] (c-string? X) → '#t or '#f

CK-macro wrapper for string?. Added in version 0.2.0.

[syntax] (c-string CHAR ...) → string

CK-macro wrapper for string. Added in version 0.2.0.

[syntax] (c-string-length STR) → integer

CK-macro wrapper for string-length. Added in version 0.2.0.

[syntax] (c-string-ref STR I) → char

CK-macro wrapper for string-ref. Added in version 0.2.0.

[syntax] (c-string=? STR ...) → '#t or '#f

CK-macro wrapper for string=?. Added in version 0.2.0.

[syntax] (c-string-ci=? STR ...) → '#t or '#f

CK-macro wrapper for string-ci=?. Added in version 0.2.0.

[syntax] (c-substring STR START) → string
[syntax] (c-substring STR START END) → string

CK-macro wrapper for substring. Added in version 0.2.0.

[syntax] (c-string-append STR ...) → string

CK-macro wrapper for string-append. Added in version 0.2.0.

Vectors (R5RS Wrappers)

[syntax] (c-vector-length V) → integer

CK-macro wrapper for vector-length. Added in version 0.2.0.

[syntax] (c-vector-ref V I) → item

CK-macro wrapper for vector-ref. Added in version 0.2.0.