''[[This page is being used to work on building more user-oriented descriptions of the new features of 8.6; please have a bit of care as this is work-in-progress right now]]'' **Included Object Systems** Tcl 8.6 includes two object systems (that's two more than any previous release of Tcl). But just because we provide them does not mean that your scripts have to change; objects in Tcl are just commands that have a particular way of accessing subcommands that you should already be familiar with: : ''someCommandThatIsAnObject'' '''theSubcommandThatIsAMethod''' ''someArgument1'' ''someArgument2'' … If you've used Tk, you've used that pattern. If you've used many of Tcl's commands (the [ensemble]s, like [string] or [namespace] or [chan] or [interp]), you've used that pattern. [TclOO] (also available as a stand-alone package for Tcl 8.5) is designed to be a small, efficient and powerful object system core that is deeply connected into Tcl and into the way that Tcl works logically. It currently does not ship with a substantial class library, but it is designed to offer a base on which other object systems can be built. [[[incr Tcl]]] 4 also ships with Tcl (and it's an example of an object system built on top of [TclOO]). There's a much larger class library available for itcl. ***Getting started with TclOO*** ====== # Let's make a class oo::class create Toaster { # The variables in the class; note that this is *not* the [variable] command variable toasting afterid # Initialization and finalization constructor {} { set toasting {} set afterid {} } destructor { after cancel $afterid } # A public method; name starts with lower case letter method toast {breadProduct} { if {$breadProduct eq ""} { error "cannot toast nothing!" } if {$toasting ne ""} { error "still toasting $toasting!" } puts "starting to toast $breadProduct" set toasting $breadProduct set afterid [after 30000 [namespace code {my DoneToasting}]] return } # A private method (callback handler) method DoneToasting {} { puts "Nicely burnt $toasting!" set toasting {} } } # Make an instance and use it set myToaster [Toaster new] $myToaster toast bagel ====== ***More on [[incr Tcl]]*** **The Non-Recursive Evaluation Engine** Tcl 8.6 is in many ways a “stackless Tcl”. This has many subtle consequences. 1. Tcl can now support very deep recursion (though it usually has a limit of 1000 imposed, which you can control with [interp recursionlimit]). 2. Tcl can now perform [tailcall%|%tail-calls], which ''replace'' the current procedure call with a call to some other command. 3. Tcl supports [coroutine]s, a system for allowing you to restructure your programs from continuation-passing style into much more straight-line code (with appropriate [yield]s at the points where you want to stop the coroutine from running while something else is happening). ***What is a coroutine and why do I care?*** Coroutines are a fairly old mechanism that fell out of favor for a while. The two principle uses for them are for building [generators] and for restructuring complex callback-driven code. As an example, let's write a little event-driven network client: ====== set s [socket $host $port] fileevent $s readable [list handleLine $s 1] proc handleLine {s lineNumber} { if {[gets $s line] < 0} { close $s } else { puts "${lineNumber}: $line" fileevent $s readable [list handleLine $s [incr lineNumber]] } } vwait forever ====== That's not very nice to read as it's asynchronous and event-driven. Let us use a coroutine to write more clearly what we mean: ====== set s [socket $host $port] fileevent $s readable [coroutine reader$s apply {s { yield [info coroutine] try { while {[gets $s line] >= 0} { puts "[incr lineNumber]: $line" yield } } finally { close $s } }} $s] vwait forever ====== There's a bit more complexity with setting up the coroutine (which you can hide in a procedure, of course) but the core of the processing is a nice [while] with a [yield] in it and a [try]/finally to clean things up. There's no updating of the callback. What's more, the more complex the interaction, the better the improvement to understandability from being able to say "let's just focus on this bit in a straight-line way". What's more, you can [yield] from inside a procedure called from a procedure inside your coroutine implementation. ''That's'' what the non-recursive execution engine does for you. ***NRE-enabling of extensions*** First off, you do not ''have'' to alter your C code to make use of NRE. It's only worth considering if you are making calls back into Tcl to evaluate scripts, and even then not in all circumstances. (For example, the Tcl [trace] command does not use NRE for its internals; that would be far too complicated for words!) See the example in the documentation of `[http://www.tcl.tk/man/tcl8.6/TclLib/NRE.htm#M6%|%Tcl_NRCreateCommand]`. In essence, you change from (warning: ''very'' abbreviated example): ======c BlockOfCode1(); result = Tcl_Eval(someScript); BlockOfCode2(); return result; ====== Into: ======c BlockOfCode1(); Tcl_NRAddCallback(runBlockOfCode2); return Tcl_NREvalObj(someScript); ====== You handle looping by getting `BlockOfCode2` to push itself (with `Tcl_NRAddCallback`) and run the body script again. If you compare what we are doing here with what was done in the Tcl coroutine example above, you'll see that the NRE scheme does not actually get rid of the continuation-passing style code: it just moves it into the C level of the API. This is a net win for most people (plus there are the gains from being able to stop putting so many calls on the C stack at any one time). **Errorstack with current parameter values** In addition to the '''errorInfo''', which is written when an error occures, an additional trace, the '''error stack''' is generated containing the `current` parameter values of all called procedures. **Database Support** We now define a standard interface ([TDBC]) for accessing databases, and ship a few database drivers that support the interface. We also include [SQLite], the database engine that started as a Tcl extension and then escaped into the big bad world! **IPv6 Support** Tcl 8.6 is fully IPv6 enabled. As long as your system hostname resolver is configured correctly (sadly, not something that Tcl can do much about if it isn't working right) Tcl's [socket] will just use the right protocol when connecting to a service. For server sockets, we now listen on all appropriate protocols. The one observable change is that now when you ask for the local address of a server socket, it can report a list of more than three items. In fact, it will be a list of three items for each protocol supported locally, and you can extract the information easily with [foreach]: ====== set s [socket -server someProc 12345] foreach {address name port} [chan configure $s -sockname] { puts "listening on address ${address}, port $port" } ====== Note also that hostnames returned when fetching the '''-sockname''' and '''-peername''' are now usually numeric; support for reverse-[DNS] is too patchy for the address-to-name translation to be useful in production, unfortunately. **New Commands** [lmap] and [try] and [zlib] and … ***The `lmap` Command*** We now provide an [lmap] command, which is very much like `foreach` except that its result is a list of the results of the evaluations of the body, rather than the empty string. This means that instead of writing this: ====== set lengths {} foreach v $values { lappend lengths [string length $v] } ====== You can just do this: ====== set lengths [lmap v $values {string length $v}] ====== ***The `try` Command and Error Codes*** The new [try] command exists to make it easier to trap ''specific'' problems, rather than the cruder “[catch] everything, including unexpected problems” that everyone used to use previously. Thus, you can use: ====== try { set f [open $filename w] } trap {POSIX EACCES} msg { puts "Error opening file for writing\n$msg" } ====== Without fear of what would happen if you got to that point and something mundane was wrong (e.g., the `filename` variable being unset). You can also use the `finally` clause to [try] to make it easier to handle doing cleanup: ====== set f [open $f] try { doStuff with $f } finally { close $f } ====== The [errorCode%|%error codes] that you trap with `try` are lists that go from least specific to most specific. The mechanism has been around in Tcl for a very long time, but has been under-used in previous releases because of the complexity of working with them. The [try] command changes that. We've also gone through and defined error codes for a great many internally-generated errors (all of which use `TCL` as the first word of the error code list). If you find any Tcl-generated errors that have their error code still being `NONE` (and they're not coming from the [error] or [return] commands), that's a reportable bug… **Updated `msgcat` Package** ***msgcat::mcflset*** Facilates to translate your application using message catalog files by not repeating the locale for each translation. **New Tk Features** ***PNG Image Support for Tk*** ***Web colors for Tk*** ***Rotated Text on the Canvas*** Text on the canvas can now be rotated in an arbitrary direction. ***tk busy*** Added the ability to lock the application GUI for an entire application or a specific window until released by the program. <>Documentation