by Peter McGoron
This SRFI is currently in draft status. Here is
an explanation of each status that a SRFI can
hold. To provide input on this SRFI, please send email to
srfi-267@nospamsrfi.schemers.org. To subscribe to the list, follow
these instructions. You can access previous
messages via the mailing list archive.
Raw string syntax is lexical syntax for strings that do not interpret escapes inside of them. They are
useful in cases where the string data has a lot of characters like \ or
" that would otherwise have to be escaped. The raw string syntax in this document is
derived from C++11's raw string literals.
None.
Many programming languages have raw string syntax: to name a few, Rust, C++, Python, Go, C#, and Zig. (For a more complete list of languages referenced while writing this proposal, see this wiki page.) Raw strings are useful when writing SQL, JSON, or code snippets in other programming languages that include double quotes and the backslash.
Scheme implementations generally do not have raw string syntax. Two exceptions are
CHICKEN
and
Gambit, which use
heredoc style syntax. Both
are Scheme→C compilers that require inserting C code into their Scheme programs. Some Scheme implementations
have the ability for the programmer to extend their readers, so some like
Guile have raw strings as external extensions.
Daphne Preston-Kendal proposed a syntax for strings similar to raw strings using #" in
another document. The matter
of raw string syntax in the R7RS-large was discussed in a WG2 meeting on
November 21st, 2025
with no consensus. It has also been discussed on the
issue tracker
for the R7RS-Large process.
This SRFI proposes the use of raw strings based off of C++'s syntax. C++'s syntax is a raw string syntax
with a customizable delimiter that allows for the use of a wide variety of characters inside of the
delimiter. In this SRFI, raw strings start with the sequence #"X" for any sequence of characters X that does not contain ",
and are terminated by "X" for the same X. The
prefix #" is not used for anything in standard Scheme:
Gauche
uses it for string interpolation.
The C++-style raw-string syntax was chosen because it was easy to parse and can embed arbitrary strings:
", a raw string can be read in linear time without
using a complicated string-search algorithm like Knuth-Morris-Pratt.
`` `like this` ``.
sql, and
LaTeX syntax can use a delimiter that starts with
latex. A text editor could use this information to switch its syntax highlighting within
the raw string.
Some languages, like C#, have special whitespace handling for raw strings. Others, like Rust and C++, do not. This SRFI does not handle leading, trailing, or indentation whitespace in any special way: they are all preserved in the resultant string. This is the least surprising option, and further string processing can be done by the programmer.
Some “raw” string syntaxes allow for interpolation, or have different types of escape sequences. This
proposal does not include any support for interpolation. (String interpolation in Scheme is the subject of
SRFI 109.) Interpolation makes string processing much more
complicated and is not extensible, while also not making the strings truly “raw.” Interpolation of strings
in Scheme is better accomplished by macros that can inspect strings, such as
syntax-case macros.
Equivalent syntax for string-notated bytevectors in SRFI 207 or for vertical-bar identifiers is not included.
The grammar of raw strings is not context free. The following grammar describes the creation of a raw string literal for any valid delimiter X:
⟨raw string (X)⟩ ⩴ #" X "
⟨raw string internal (X)⟩ " X "
⟨raw string internal (X)⟩ ⩴ Any sequence of zero or more characters that does not contain
" X " as a subsequence
⟨valid delimiter⟩ ⩴ Any sequence of zero or more characters not including "
Note that although valid delimiters look like strings when used, they do not interpret escape sequences inside of them.
The grammar of Scheme is modified so that the ⟨string⟩ production becomes
⟨string⟩ ⩴ " ⟨string element⟩* " |
⟨raw string (X)⟩ for X satisfying ⟨valid delimiter⟩
A raw string, when read, is a string. They are allowed wherever a regular Scheme string is allowed. For
example, raw strings are allowed in the include and
include-library-declaration forms described in the R7RS.
#"""" ; → ""
#""\begin{document}"" ; → "\\begin{document}"
#"--")")")"-""--" ; → ")\")\")-\")"
#""a"" ; → "a"
#""\"" ; → "\\"
#"-"""-" ; → "\""
#"-" " "-" ; → " \" "
#"-"#""a"""-" ; → "#\"\"a\"\""
#"-"ends with \""-" ; → "ends with \\\"
#""multiline
string"" ; → "multiline\nstring"
#""
no whitespace stripping"" ; → "\n no whitespace stripping
#""{"first_name" : "John",
"last_name" : "Doe"}"" ; → "{\"first_name\" : \"John\",\n\"last_name\" : \"Doe\"}"
#""\(?(\d{3})\D{0,3}(\d{3})\D{0,3}(\d{4})""
; → "\\(?(\\d{3})\\D{0,3}(\\d{3})\\D{0,3}(\\d{4})"
; Example from SRFI 264
The following example shows how a raw string can be used to embed other syntaxes as a string. The syntax inside of the string is SRFI 119 wisp syntax.
#"wisp-EOS"
define : hello-name name
string-append "Hello," name "!"
"wisp-EOS"
; ⇒ "\ndefine: hello-name name\n string-append \"Hello,\" name \"!\"\n"
One use of raw strings is in “docstring” documentation, where " and \ may be
used to document strings. The following example is from Daphne Preston-Kendal:
(define (parse-url url-string)
#""Given a URL as a string, returns a Parsed-URL record with the
components of that URL.
(parse-url "https://example.org/~smith/?record")
=> #<Parsed-URL protocol: "https" domain: "example.org"
path: "/~smith/" query: "?record">""
...)
A portable implementation is impossible in general. However, some implementations, such as CHICKEN, Guile, Racket, and Sagittarius allow modifying the reader. This sample implementation works with CHICKEN-5 and Guile 3, and also includes some tests at the bottom of the file.
A patch is available for Chez Scheme 10.3.0 that implements this SRFI.
The original C++ proposal was written by Bemen Dawes. The last C++ proposal was written by Bemen Dawes and Lawrence Crowl. The raw string syntax was ratified in C++11.
The choice of #" was copied from Daphne Preston-Kendal. John Cowan suggested double quotes
in place of balanced parentheses for raw strings and also gave some editorial advice.
Thanks to the members of WG2 and others who discussed the issue.
© 2025–2026 Peter McGoron
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.