\documentclass[]{article}
\usepackage{amsmath,graphicx,supertabular,hyperref,tabularx,ifthen}
\title{TIP \#413: Unicode Support for 'string is space' and 'string trim'}
\date{October 08, 2012}
\author{Jan Nijtmans}
\urlstyle{sf}
\setlength{\parskip}{1ex}
\setlength{\parindent}{0pt}
\def\tipversion$#1${\texttt{\$#1\$}}
\def\tiplangle#1{\ensuremath{<}}
\def\tiprangle#1{\ensuremath{>}}
\def\tipbar#1{\ensuremath{|}}
\def\tipmail#1#2{\(\langle\){\small\expandafter\url{#1@#2}}\(\rangle\)}
\ifx\pdfoutput\undefined
\newcommand{\tipimage}[2]{\typeout{Make sure you download #1.eps}\ifthenelse{\lengthtest{0.8\textwidth>#2}\and\lengthtest{0pt<#2}}{\includegraphics{#1.eps}}{\includegraphics[width=0.8\textwidth]{#1.eps}}}
\newcommand{\tipxref}[1]{}
\newcommand{\tipxrefend}{}
\else
\newcommand{\tipimage}[2]{\typeout{Make sure you create #1.pdf}\ifthenelse{\lengthtest{0.8\textwidth>#2}\and\lengthtest{0pt<#2}}{\includegraphics{#1.pdf}}{\includegraphics[width=0.8\textwidth]{#1.pdf}}}
\pdfcatalog{/PageMode /UseOutlines}
\newcommand{\tipxref}[1]{\pdfannotlink attr {/C [0.5 0.5 1.0] /Border [0 0 1]} goto name {#1}}
\newcommand{\tipxrefend}{\pdfendlink}
\fi
\newenvironment{tipabstract}{\begin{abstract}}{\end{abstract}}
\begin{document}\maketitle
\begin{center}\begin{tabularx}{\linewidth}{|r@{: }X|}\hline
\textbf{TIP \#413}&\textbf{Unicode Support for 'string is space' and 'string trim'}\\\hline
Author&

Jan Nijtmans \tipmail{jan.nijtmans}{users.sf.net}
\\
Created&Monday, \(\text{8}^{\text{th}}\) October 2012\\
Type&Project\\
Tcl Version&\(8.6\)\\
State&Final\\
Vote&Done\\
Version&\tipversion$Revision: 1.5 $\\
Keywords&Tcl\\
Post History&\\
Discussions To&Tcl Core list\\
\hline\end{tabularx}\end{center}
\thispagestyle{empty}\pagestyle{empty}
\begin{tipabstract}

This TIP is in fact a re-consideration of \cite{tip318}, in that it attempts to define, once and for all, for which characters \textbf{string is space} should return 1 and which characters \textbf{string trim} should trim.
\end{tipabstract}
\tableofcontents\setcounter{page}{0}\clearpage\pagestyle{plain}

\section{Rationale}

Intuitively, \textbf{string is space} and \textbf{string trim} should treat the same characters as space, but currently that's not the case, even after the implementation of \cite{tip318}. The unicode standard advanced to version 6.2 now (at the time of this writing), but also Java and .NET have their own views on what whitespace should be. Let's try to learn from them.

\subsection{Defining the Tcl Space Set}

The NUL character has the function as string separator, which could be considered in the same group as LINE SEPARATOR (U+2028) and PARAGRAPH SEPARATOR (U+2029). It's a very useful character to be stripped. It even had the Whitespace property in Unicode 2.0. The problem with considering this character as space is that its visible representation is not specified, it even should not occur in normal text. Therefore, it is not in the ``Tcl space set'', but it is very useful to let it be stripped by \textbf{string trim}.

The Unicode standard changed in time, which resulted in whitespace characters being removed (deprecated) and added. The \textit{String.Trim()} method in .NET 3.5 stripped zero width space (U+200B) and zero width no-break space (U+FEFF) from strings, but later .NET versions don't do that any more. The ``Tcl space set'' should not depend on that: If characters are deprecated in future Unicode versions, and because of that Whitespace properties are changed, they will not be removed from the ``Tcl space set''. But if new whitespace characters are added in future Unicode standards, they will be added to the ``Tcl space set'' as well, influencing both \textbf{string is space} and \textbf{string trim}.

The 3 characters that are in the ``Tcl space set'' but not in the current Unicode whitespace set are discussed now.

Most obvious is zero width no-break space (U+FEFF), which is a very useful character to be stripped, as it is used now as Byte Order Mark (BOM). It has no visible representation, and --- in fact --- no meaning at all within Tcl, as Tcl is UTF-8 internally already. It should not occur anywhere else in the string, but in the past it could as being a zero-width no-break space. It had the \textit{White\_Space} property in Unicode 2.0, but later versions of Unicode do not; the use of the BOM as a space was deprecated.

When the use as space was deprecated for (U+FEFF), another character was put forward as replacement for it: word joiner (U+2060). As this character has no visible representation, and has no meaning at all when at the start or the end of a string, it makes sense to include it in the ``Tcl space set'' as well, the more because its predecessor had the \textit{White\_Space} property.

Finally, zero width space (U+200B), had the \textit{White\_Space} property in Unicode 3.0. In the current Unicode Charts it is still listed as being a space, even though the White\_Space property was removed later. Therefore it should be in the ``Tcl space set'' as well.

\section{Specification}

This document proposes:
\begin{itemize}
\item{}
For the ASCII set, \textbf{string is space} stays as is. \textbf{string trim} will be modified to trim all characters for which \textbf{string is space} returns 1, augmented with the NUL character. This means that NUL, VT and FF will be added to the set. This is a \textbf{potential incompatibility}.
\item{}
For characters outside ASCII, the Unicode \textbf{White\_Space} [\url{http://www.unicode.org/Public/6.2.0/ucd/PropList.txt}] property forms the basis of what \textbf{string is space} and \textbf{string trim} consider being space. But 3 characters are added to the set: zero with space (U+200B), word joiner (U+2060) and zero width no-break space (U+FEFF) (i.e., the BOM).
\end{itemize}

The \textbf{string trimleft} and \textbf{string trimright} commands will also be modified, as they track \textbf{string trim}.

\section{Compatibility}

For the ASCII set, the only change is the addition of 3 characters to \textbf{string trim}. For Unicode there are more changes, but all added characters are either rarely used, either intuitively expected to be trimmed by \textbf{string trim}. I don't think that any code will be adversely affected by this change, it will probably fix more bugs than that it breaks any existing code.

\section{Alternatives}
\begin{enumerate}
\item{}
NUL could be added to \textbf{string is space}, but that would be in conflict with what POSIX \textit{isspace()} function does.
\setcounter{enumi}{1}
\item{}
NUL could be left out of the \textbf{string trim} set.
\setcounter{enumi}{2}
\item{}
Additional characters I considered being part of the set:
\begingroup\small\begin{verbatim}
    break permitted here (U+0082)
    no break here (U+0083)
    zero width joiner (U+200C)
    zero with non-joiner (U+200D)
\end{verbatim}
\endgroup

Those are clearly useful characters to be stripped, as they have no meaning and no visible appearance at the beginning or end of a string. But they are not spaces, so it would diverge the two commands.
\end{enumerate}

\section{Reference Implementation}

A reference implementation is available in the Tcl fossil repository on the \textit{tip-318-update} branch [\url{https://core.tcl.tk/tcl/timeline?r=tip-318-update}].

\section{Copyright}

This document has been placed in the public domain.

\section{Colophon}

\textit{TIP AutoGenerator --- written by Donal K. Fellows}
\begin{thebibliography}{TIP \#1}
\addcontentsline{toc}{section}{References}
\bibitem[TIP \#318]{tip318}
Bill Poser,
\emph{Extend Default Whitespace in 'string trim' Beyond ASCII},
on-line at \url{http://purl.org/tcl/tip/318.html}
\end{thebibliography}
\end{document}
