For Development HEAD DRAFTSearch (procedure/syntax/module):

6.7 Symbols

Gauche enhances Scheme symbols in a couple of ways:

Uninterned symbols

Like Common Lisp, you can create an uninterned symbol, which is not registered to the internal table, so it is guaranteed not to be eq? to any other symbols created by string->symbol or read.

Since Scheme’s hygienic macro system automatically avoids name collisions, uninterned symbols are less needed than Common Lisp, but it comes handy when you want a unique object with some descriptive name.

Keywords

A symbol whose name begins with a colon (:) is automatically bound to itself in gauche.keyword module. By importing or inheriting it, you can use such symbols as if they’re self-evaluating object. They’re extensively used in Gauche for passing keyword arguments.


6.7.1 Basic symbols

Builtin Class: <symbol>

A class for symbols.

Reader Syntax: |name|

[R7RS] Denotes a symbol that has weird name, including the characters that are not usually allowed in symbols. It can also include hex-escaped characters.

;; A symbol with spaces in its name
'|this is a symbol| ⇒ |this is a symbol|

;; Unicode codepoint can be used following backslash-x escape,
;; and terminated by semicolon.
'|\x3bb;| ⇒ λ

If the interpreter is running in case-insensitive mode, this syntax can be used to include uppercase characters in a symbol (see Case-sensitivity).

Function: symbol? obj

[R7RS base] Returns true if and only if obj is a symbol.

(symbol? 'abc)     ⇒ #t
(symbol? 0)        ⇒ #f
(symbol? 'i)       ⇒ #t
(symbol? '-i)      ⇒ #f
(symbol? '|-i|)    ⇒ #t
Function: symbol=? a b c …

[R7RS base] Every argument must be a symbol. Returns #t iff every pair of arguments are eq? to each other.

Function: symbol->string symbol

[R7RS base] Returns the name of symbol in a string. Returned string is immutable.

(symbol->string 'foo) ⇒ foo
Function: string->symbol string

[R7RS base] Returns a symbol whose name is a string string. String may contain weird characters.

(string->symbol "a") ⇒ a
(string->symbol "A") ⇒ A
(string->symbol "weird symbol name") ⇒ |weird symbol name|
Function: symbol-sans-prefix symbol prefix

Both symbol and prefix must be symbols. If the name of prefix matches the beginning part of the name of symbol, this procedure returns a symbol whose name is the name of symbol without the matched prefix. Otherwise, it returns #f.

(symbol-sans-prefix 'foo:bar 'foo:) ⇒ bar
(symbol-sans-prefix 'foo:bar 'baz:) ⇒ #f
Function: symbol-append interned? objs …
Function: symbol-append objs …

Returns a symbol with the name which is a concatenation of string representation of objs.

If the first argument is a boolean, it is recognized as the first form; the first argument specifies whether the resulting symbol is interned or not.

Each other argument is converted to a string as follows: If it is a keyword, its name (with the preceding :) is used. For all other objects, x->string is used. (The special treatment of keyword is to keep the consistency before and after keyword-symbol integration. See Keyword and symbol integration, for the details.)

This is upper-compatible to Bigloo’s same name procedure, which only allows symbols as the arguments and the result is always interned.

(symbol-append 'ab 'cd) ⇒ abcd
(symbol-append 'ab ':c 30) ⇒ ab:c30
(symbol-append #f 'g 100) ⇒ #:g100

6.7.2 Uninterned symbols

Reader Syntax: #:name

Denotes uninterned symbol. Uninterned symbols can be created by gensym or string->uninterned-symbol.

Uninterned symbols are mainly for legacy macros to avoid variable conflicts. They are not registered in the internal dictionary, so such symbols with the same name can’t be eq?.

(eq? '#:foo '#:foo) ⇒ #f
(eq? '#:foo 'foo) ⇒ #f

To preserve eq?-ness of uninterened symbols, you need to use write-shared which shows shared structures.

(write-shared (let1 s '#:foo (list s s)))
  ⇒ prints (#0=#:foo #0#)

(write-shared (let ((s '#:foo) (t '#:foo)) (list s t s t)))
  ⇒ prints (#0=#:foo #1=#:foo #0# #1#)
Function: symbol-interned? symbol

[SRFI-258] Returns #t if symbol is an interned symbol, #f if it is an uninterned symbol. An error is signaled if symbol is not a symbol.

This is included in SRFI-258 (see srfi.258 - Uninterned symbols (SRFI)).

Function: string->uninterned-symbol string

[SRFI-258] Like string->symbol, but the created symbol is uninterned.

(string->uninterned-symbol "a") ⇒ #:a

This is included in SRFI-258 (see srfi.258 - Uninterned symbols (SRFI)).

Function: gensym :optional prefix

Returns a fresh, uninterned symbol. The returned symbol can never be eq? to other symbol within the process. If prefix is given, which must be a string, it is used as a prefix of the name of the generated symbol. It is mainly for the convenience of debugging.

This is similar to SRFI-258’s generate-uninterned-symbol (see Uninterned symbols).


6.7.3 Keywords

Builtin Class: <keyword>

Keywords are a subtype of symbols that are automatically bound to itself. It is extensively used in named arguments (keyword arguments), and keyword-value list.

See Making procedures for how Gauche supports keyword arguments, and let-keywords macro (Optional argument parsing) for parsing keyword-value list manually.

NB: Keywords used to be a disjoint type from symbols. We suppored that compatibility mode with a special environment variable up to 0.9.15, but no longer provide that mode. See Keyword and symbol integration, if you need to update your code.

Reader Syntax: :name

Read to a keyword whose name is :name.

Function: keyword? obj

Returns #t if obj is a keyword.

Function: make-keyword name

Returns a keyword whose name is name prepended by :. The name argument can be a string or a symbol.

(make-keyword "foo")  ⇒ :foo

(make-keyword 'foo)   ⇒ :foo
Function: keyword->string keyword

Returns the name (without the initial :) of the keyword keyword, in a string. (This is mainly to support legacy code written when keywords are disjoint from symbols.)

(keyword->string :foo) ⇒ "foo"
Function: get-keyword key kv-list :optional fallback

A useful procedure to extract a value from key-value list. A key-value list kv-list must contains even number of elements; the first, third, fifth … elements are regarded as keys, and the second, fourth, sixth … elements are the values of the preceding keys.

This procedure looks for key from the keys, and if it finds one, it returns the corresponding value. If there are more than one matching keys, the leftmost one is taken. If there is no matching key, it returns fallback if provided, or signals an error otherwise.

It is an error if kv-list is not a proper, even-number element list.

Actually, ‘keywords’ in the keyword-value list and the key argument need not be a keyword—it can be any Scheme object. Key comparison is done by eq?.

This procedure is taken from STk.

(get-keyword :y '(:x 1 :y 2 :z 3))
  ⇒ 2
(get-keyword 'z '(x 1 y 2 z 3))
  ⇒ 3

(get-keyword :t '(:x 1 :y 2 :z 3))
  ⇒ #<error>
(get-keyword :t '(:x 1 :y 2 :z 3) #f)
  ⇒ #f
Macro: get-keyword* key kv-list :optional fallback

Like get-keyword, but fallback is evaluated only if kv-list does not have key.

Function: delete-keyword key kv-list
Function: delete-keyword! key kv-list

Removes all the keys and values from kv-list for keys that are eq? to key.

delete-keyword doesn’t change kv-list, but the returned list may share the common tail of it.

delete-keyword! doesn’t allocate, and may destructively changes kv-list. You still have to use the returned value, for the original list may not be changed if its first key matches key.

If there’s no key that matches key, kv-list is returned.

(delete-keyword :y '(:x 1 :y 2 :z 3 :y 4))
 ⇒ (:x 1 :z 3)
Function: delete-keywords keys kv-list
Function: delete-keywords! keys kv-list

Similar to delete-keyword and delete-keyword!, but you can specify a list of objects in keys; when a key in kv-list matches any of keys, the key and the following value is removed from kv-list.

(delete-keywords '(:x :y) '(:x 1 :y 2 :z 3 :y 4))
 ⇒ (:z 3)

6.7.4 Keyword and symbol integration

In older versions of Gauche, keywords are of disjoint type from symbols, and they are self-evaluating objects. To maintain the compatibility, the current Gauche makes symbols that begins with : automatically bound to itself, in gauche.keyword module.

The gauche module inherits gauche.keyword, so all the keyword look like self-evaluating objects. That allows most legacy Gauche code keep working, but there are some catches.

(symbol? :key) used to return #f, now returns #t

keyword? always returns #t on keywords, but if you need to switch behavior depending whether an object is a symbol or a keyword, you should test keyword-ness first.

;; behaved differently in 0.9.7 and before
(cond
  [(symbol? x) (x-is-symbol)]
  [(keyword? x) (x-is-keyword)])

;; works on all versions
(cond
  [(keyword? x) (x-is-keyword)]
  [(symbol? x) (x-is-symbol)])

Literal keywords in pattern matching

In the old versions, when keywords appear in a pattern of util.match or syntax-rules, they only matched to themselves. In the current version, such keywords in a pattern are treated as pattern variables, since they are symbols.

;; In the old versions
(match '(a b) [(:key z) (list :key z)] [_ "nope"])
   ⇒ "nope"

;; In the current version
;; :key is treated just as a pattern variable
(match '(a b) [(:key z) (list :key z)] [_ "nope"])
   ⇒ (a b)

The same thing happens to the patterns in syntax-rules.

To make the code work in both versions, explicitly mark the keywords as literals.

Displaying keywords

(display :key) used to print key (no colon), while it now prints :key.

You can use (display (keyword->string :key)) which prints key in both versions.

For R7RS code, quote them or import Gauche modules

Keywords (symbols beginning with :) are automatically bound to itself in the gauche.keyword module.

Gauche code inherits the gauche module by default, which inherits keyword, so you can see the binding of the keyword by default.

In R7RS code, however, you don’t inherit gauche, so symbols beginning with : are just ordinary symbols by default. Usually you do (import (gauche base)) to use Gauche built-ins, and that makes binding of gauche.keyword available in your code, too (since gauche.base inherits gauche.keyword). But keep this in mind just in case you want to handle keywords in your R7RS code separate from Gauche procedures—you have to either say (import (gauche keyword)) to get just the self-bound keywords, or quote them.

(import (scheme base))

:foo ⇒ ERROR: unbound variable: :foo

(import (gauche base))

:foo ⇒ :foo

In the following example, the R7RS library foo imports only copy-port from (gauche base); in that case, you have to import (gauche keyword) separately in order to use :size keyword without quoting. (Or add :size explicitly in the imported symbol list of (gauche base).)

(define-library (foo)
  (import (scheme base)
          (only (gauche base) copy-port)
          (gauche keyword))
  (export cat)

  (begin
    (define (cat)
      (copy-port (current-input-port)
                 (current-output-port)
                 :size 4096))))


For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT