diff --git a/_01.Language-basics.sz b/_01.Language-basics.sz index c5e6aa9..52df1f3 100644 --- a/_01.Language-basics.sz +++ b/_01.Language-basics.sz @@ -1209,7 +1209,7 @@ Sets and the sorted collections are libraries, so they have to be imported. Thei (orderedmap-min prices) ;; (Some ("apple" . 5)) }| -An ordered collection sorts its keys with their @code{Ord}, so a type that derives @code{Ord} can be a key in one. Strings are sorted by code unit, not by the rules of any language, so the order is the same on every machine. +An ordered collection sorts its keys with their @code{Ord}, so a type that derives @code{Ord} can be a key in one. Strings are sorted by code point, not by the rules of any language, so the order is the same on every machine. @subsubsection{Building collections quickly} @@ -1709,15 +1709,15 @@ Dealing a deck to four players means giving player 0 the cards 0, 4, 8 and so on @subsubsection{Strings and characters} -A @code{string} is a .NET string, which means it is stored as UTF-16. A @code{char} in bjolang is not a UTF-16 unit, though: it is a whole Unicode code point. An emoji is one @code{char}, even though it takes two units in the string. That leads to two different lengths: +A @code{string} is stored as UTF-8, and is always valid UTF-8. A @code{char} is not a byte, though: it is a whole Unicode code point. An emoji is one @code{char}, even though it takes four bytes in the string. That leads to two different lengths: @codeblock[#:lang "bjolang"]|{ (def s "Björn 😀 åt") -(string-length s) ;; 11: storage units, O(1). Use it for sizing buffers. +(string-length s) ;; 15: storage units, which are bytes, O(1). Use it for sizing buffers. (string-count s) ;; 10: characters, O(n). This is "how long is the text". }| -For the same reason there is no @code{string-ref} that takes an index. Finding the n:th character of a UTF-16 string means walking it, so walking is what you do: with a @code{loop}, a fold, or a cursor. +For the same reason there is no @code{string-ref} that takes an index. Finding the n:th character of a UTF-8 string means walking it, so walking is what you do: with a @code{loop}, a fold, or a cursor. @codeblock[#:lang "bjolang"]|{ (loop (:for c s) (:acc n (counting c)) => n) ;; 10: loops go by character @@ -1743,7 +1743,7 @@ Characters are written @code{#\a}, @code{#\space}, @code{#\newline}. They can be (string-empty? "") ;; #t }| -All of these compare strings ordinally, one storage unit at a time, and never according to the language settings of the machine. Upcasing and downcasing are the same everywhere too. To compare without caring about case, downcase both sides first. +All of these compare strings ordinally, byte by byte, which for UTF-8 is the order of the code points, and never according to the language settings of the machine. Upcasing and downcasing are the same everywhere too. To compare without caring about case, downcase both sides first. @subsubsection{Cursors} diff --git a/_02_.Writing-programs.sz b/_02_.Writing-programs.sz index ad3ab89..d2a7bf3 100644 --- a/_02_.Writing-programs.sz +++ b/_02_.Writing-programs.sz @@ -902,6 +902,8 @@ Each entry in @code{import/extern} specifies a local Bjolang identifier, the ful max-int ;; 2147483647 }| +.NET's own string, @code{System.String}, is UTF-16 and a different type from @code{string}. A .NET method that takes or returns one, or an array of them, is converted where it is called, so @code{trim} above takes and gives a Bjolang string; a @code{null} from .NET arrives as @code{""}. A .NET string inside another type, such as a @code{(Seq System.String)}, is not converted: @code{string->clr-string} and @code{clr-string->string} do it by hand. + Avoid binding an external method to the name of an existing prelude function. Locally bound names and prelude symbols take precedence over @code{import/extern} declarations, which means an alias named @code{println} would simply be shadowed and ignored. @subsubsection{Classes: import/class} @@ -920,6 +922,8 @@ The @code{import/class} form maps a .NET class to a Bjolang type, exposing its c (.ToUpper "shout") ;; "SHOUT" }| +A method that @code{string} does not have, like @code{ToUpper}, is called on the string converted to .NET's, so its indices and lengths are .NET's UTF-16 ones and not @code{string-length}'s bytes. + As shown in the example above, you do not need to declare every method up front with @code{import/extern}. Once an object's type is known to the type checker, you can invoke any public method using @code{(.Method obj args ...)} and inspect properties using @code{(.-Property obj)}. Because @code{Append} returns the builder itself and Bjolang enforces explicit handling of return values, discard unused returns intentionally using @code{ignore}. @subsubsection{When .NET fails}