The most basic Tcl source for this I can think of is
proc Real c {lindex $c 0} proc Imag c {lindex $c end} proc Add {c d} { lmap c $c d $d {expr {$c + $d}} } proc String c { if {[lindex $c end] < 0} { format (%g%gi) {*}$c } else { format (%g+%gi) {*}$c } } set z {1 2} String [Add $z $z]
But that's close to cheating. Let's at least make a class for it:
oo::class create Complex { variable re im constructor args { lassign $args re im if {$re != $re || $im != $im} { set msg {domain error: argument not in valid range} throw [list ARITH DOMAIN $msg] $msg } } method real {} {set re} method imag {} {set im} method add d { Complex new [expr {$re + [$d real]}] [expr {$im + [$d imag]}] } method string {} { if {$im < 0} { format (%g%gi) $re $im } else { format (%g+%gi) $re $im } } } set z [Complex new 1 2] [$z add $z] string
And that's it. Not a big deal. Unless you're working with Java, of course.
The biggest wart AFAICS is that Tcl doesn't allow implied conversions to string: everything has a string representation, but Tcl decides what it is. In the first code, this works in our favor, as the standard string representation (a string that is a list of two numbers) is identical to the model I used for complex numbers. Without calling String, the result of Add $z $z is 2 4, which is immediately useful both for printing and for further calculations.
In the second code, the string pseudo-representation is ::oo::Obj99 (or some other number), i.e. the name of the object command. The method string needs to be called explicitly to get a printable string that shows the data content.
Note that Tcl's expr command throws an exception with the message "domain error: argument not in valid range" instead of dumping a "NaN" message. You can still figure out that it was a NaN by looking at the message or by comparing the number to itself, as I'm doing here. I could have signalled the problem with the invocation "error NaN", but chose to emulate Tcl's handling instead.