== String Hierarchy Restructuring == On 13 Aug 2008 we committed a fairly major restructuring of the type hierarchy. There may be some consequences for your programs. === What Happened === String, which was a (parameterized) object, is now a trait. This means that various other kinds of object will be able to extend String. In fact, the idea is that it will be possible to add new subtypes of String at will, without any further restructuring. The things that you get from String literals like ''"Hello World"'' are now JavaStrings. JavaString extends String, so in most cases, you won't care. Moreover, neither String nor JavaString is defined in !FortressBuiltin. String is in !FortressLibrary, and is thus implicitly imported everywhere. JavaString is defined in a separate file (it is still "native"). === What May Break === In a few case, you may see some impact on your code, i.e., you may get weird error messages. * __Case__ statements that compare characters with string literals won't compile. The string literals are JavaStrings, and the __case__ is compiled into a MATCH operation. But the MATCH operator appears to have the wrong argument types… The solution is to fix the interpreter/compiler, but in the meantime, just __import__ing {{{ JavaString.{...} }}} allows your case statement to compile. * Although {{{JavaString}}} is a subtype of {{{String}}}, {{{List⟦JavaString⟧}}} is ''not'' a subtype of {{{List⟦String⟧}}}. This may bite you. For example in {{{ #!fss x : String ... x := "hello" ... <| x |> }}} the type of the constructed list is {{{List⟦JavaString⟧}}}, because the dynamic type of the object denoted by ''x'' is {{{JavaString}}}, not {{{String}}}. If this matters, the fix is to put an explicit type in the ''List'' constructor, i.e., to write {{{ #!fss <|[\String\] x |> }}} Yes, I know that it's ugly. I believe that static type inference will make this go away. * If you are writing code that cared that {{{String}}} was really {{{FortressBuiltin.String}}}, you will obviously have to convince it otherwise. Some of the static type inference tests cared, and had to be bent to the will of the new Lord. === Printing === Now that we have tree-structured (lazy) strings, it makes no sense to convert them to JavaStrings just so that they can be printed. So the printing architecture has been revamped. '''component''' {{{Writer}}} defines two new Fortress objects (implemented as wrappers on Java objects) {{{Writer}}} and {{{BufferedWriter}}}. These both extend trait {{{WriteStream}}}, which does essentially everything that a {{{FileWriteStream}}} used to do. {{{Writer}}} is unbuffered; {{{BufferedWriter}}} is buffered. A {{{BufferedWriter}}} is constructed by giving it a {{{WriteStream}}}, on which it will emit its characters when it fills up or is {{{flush()}}}ed. Thus, it's OK to stack one {{{BufferedWriter}}} on top of another. You can make a new {{{Writer}}} on a file by giving the object constructor a file name as argument, just as you used to do with {{{FileWriteStream}}}. In addition, the component {{{Writer}}} defines two {{{Writer}}}s {{{ #!fss stdOut: Writer stdErr: Writer }}} which are already open and ready for your use. {{{Print s}}} is now defined to (roughly) be {{{stdOut.write(s)}}}, and similarly for {{{println}}}. In addition, there are new top-level functions {{{errorPrint}}} and {{{errorPrintln}}}, which write to {{{stdErr}}}. The availability of {{{stdOut}}} and {{{stdErr}}} will make it much simpler to write a Fortress program that decided to write to the standard output or to a file depending on its input or a command line argument. ==== Printing and Concurrency ==== Because the Writer objects are wrapped Java objects rather than Fortress objects, they are not subject to Fortress concurrency control. If multiple threads in a Fortress program write characters concurrently to a single Writer, they will be serialized in an arbitrary order. If you don't like this behaviour, have each thread make its own new writer by wrapping a {{{BufferedWriter}}} around the underlying {{{Writer}}}. This buffering is done automatically for any write of a String: a buffer of sufficient size is allocated, the String is written to it, and the buffer is flushed. This has the effect of making all writes of a single string appear atomic. Thus, one way of making a batch of output appear atomically on a writer is to concatenate all of the fragments together and then write the whole of the concatenated string with one print function call or write method invocation. Because concatenation does not copy its argument strings (unless they are very short), this is quite efficient. Printing is not (yet) transactional. So, anything written on a {{{Writer}}} in a transaction will ''not'' be undone if the transaction aborts. Hence, when the transaction is retried, the output may appear a second (or third ...) time. If you are putting print statements inside a transaction for debugging purposes, this is probably what you want to see. If you are printing inside a transaction for other purposes, you may want to reconsider this behaviour.