Updated 2011-09-28 11:32:39 by RLE

Purpose: to discuss the structure of the physical code in a Tcl module----

[Peter Hiscocks] writes in news:comp.lang.tcl :

I hesitate to mention this idea, because it seems so obvious. However, I haven't read it in any of the excellent books on Tcl/Tk and have found it to be extremely useful.

I suggest that any Tcl/Tk program can be broken down into four major sections. As stated in the header from one of my programs:
 # Structure of this program:

 #       Part 1: Definition of all the procedures
 #       These are used by other sections of the program.

 #       Part 2: Definition of constants

 #       Part 3: Creation of the widgets
 #               - menus
 #               - frames and controls

 #       Part 4: Initialization of system, launching event loop.
 #       This must be at the end of the code because it refers to
 #       procedures and widgets that must be defined previously.

These sections are not necessarily of similar size. Part 1 and 3 tend to be much larger, and Part 4 can be as short as a few lines of code.

This structure clarifies the event-loop based nature of a Tcl/Tk program. You set up the data structures and then start the event loop. What happens after that depends what events are triggered.

It also helps organize the program. For example, the procedures and constants should be known when the widgets are created.

In the body of the code, I indicate the start of each section with something like this:
 #--------------------------------------------------------------
 # Part 4: Initialization
 #--------------------------------------------------------------

These 'section demarcations' can be very helpful in narrowing down the region of the program to search for some particular piece of code.

Comments?

Peter

Donal Fellows replied:

I consider section 3 to be better split between sections 1 and 4, and it is often a good idea to split section 2 off into a separate file so your system installation and configuration applet (:^) can manipulate that more easily.

Much larger programs are better divided up into functional areas, so code manipulating Foobars goes in one file (or even directory if there is a lot of it) and code manipulating Spongs goes somewhere else. This makes maintaining it much easier as you can identify what needs to be fixed with where to look for the code that needs fixing.

LES It is very hard for me to argue with Donal's experience, but after trying several approaches, I have totally given up the multifile or multidir method. I hate having to hunt for a proc or variable in several files, especially in Linux, with the notorious lack of decent tab-based text editors. Also, especially with the way Tcl accuses errors but won't say where (in which file) the error is. Now I keep everything in 2 files: one full of custom procs and aliases that is shared (sourced) by all my programs and one with the entire program itself, no matter how large it is. I also like the sequence "constants (globals), procs, widgets and algorithm".

Bryan Oakley says:

I tend to not be so formal with naming sections. But what I do do (that always sounds so weird...) is put a main() at the top, and call it at the bottom. I like the main logic to appear first, with the details hidden below. Something like this:
    #!whatever

    <constants get defined here...>

    proc main {} {
        yada yada yada
        foo 420
    }

    proc yada {args} {
        ...
    }

    proc foo {} {
       ...
    }

    main

VL says:

The presented structure is very close to the UIE architecture [1]

I prefer to put the definition of constants and globals at the top of the file. This mirrors C coding style, where your macros tend to appear right at the top. It also follows normal lookup: if you bump into a variable inside a proc you tend to assume it's defined further up so even though this is not required for globals or constants I like to follow the same logic.

--setok

slebetman says:

I kind of like the style set up by vtcl. It goes something like:
 #       Part 1: Definition of all the procedures
 #               These are used by other sections of the program.

 #       Part 2: Definition of constants

 #       Part 3: Run init proc.
 #               The init proc is responsible for initializing data structures, hiding . or console,
 #               creating images etc..

 #       Part 4: Creation of the widgets

 #       Part 5: Run main proc.
 #               The main proc is responsible for setting up event loops etc, deiconifying . etc..

The init proc lets me collect all the stuff I want to do before creating widgets in a single place. There are things that I sometimes need to do like loading icons & images or reading menu structures from a text file that must be done before creating widgets. All that stuff fits well into an init proc.

Things that needs to be done after creating widgets can then be done in the main proc.

LV So, why would you not define the constants before the procs?

MG July 23 2005 - Personally (and currently, as it's changed a little since I started with Tcl, and will no doubt do so again later), I define all the constants first (but inside a proc), then have a "main" proc, then all the others, like...
 proc setVars {} {
  global varName
  set varName value
 }
 proc main {} {
  # do stuff
 }
 proc otherProcs {} {
  # do stuff
 }
 # run the program
 setVars
 main

The reason I define the constants inside another proc is that, usually, I have a foreach (or a file id from open to set user preferences) or something like that, which uses variables which shouldn't be persistent. Encompassing the whole thing in a proc means that as long as you declare globals where you actually want them, the rest of the cleanup is done automatically at the end of the proc. (And, if you need to restore defaults later, you can just re-run the proc.) There could also be package require statements in there, but where they go depends on the program - sometimes you need to read user prefs before deciding which packages you actually need, etc..

Artur Trzewik July 25 2005 - It is not clear in this examples. For many cases it is very advantageous to have as less not proc definition in global context as possible. So I would prefer even a definition of constants in in one special proc. Of course use namespaces! The order of definition is not so important but should be clear.
  namespace eval mynamespace {
      proc defineConstans {} {
          ...
      }
      proc setUpWindows {} {
      }
      proc doJob {} {
      }
      proc otherProcs {} {
      }
      proc main {} {
          defineConstans
          setUpWindows
          doJob
      }
  }
  mynamespace::main

Only one line in global context starts the application. Such schema help to reuse Tcl scripts in other projects or under another conditions. If almost all is defined in proc you can access the contents of it per introspection (TkInspector). For XOTclIDE that is introspection based IDE it helps a lot for importing projects if they have such structure. Also another Tcl Tools like debugger or syntax checker will be thankful for such structure.

Juan-Carlos Gil July 26 2005 - The Simple Development Library [2] provides declare-program and declare-package commands which serve for the issue at hand:
    package require SimpleDevLibProgram
    declare-program {
       simpleprogramexample
    } -version {
       0.1
    } -synopsis {
       A program example.
    } -overview {
       Program example which computes the reciprocal of a number.
    } -option {
      {-n --number} -type float -description {number to compute its reciprocal}
    } -optionsusage {
       -n
    } -examples {
       To compute a number reciprocal:
          simpleprogramexample -n 2
    } -requisites {
       {Tcl 8}
    } -command {
       {
          reciprocal
       } -synopsis {
          Computes a number reciprocal.
       } -arguments {
          { number           float          {Number to compute its reciprocal}}
       } -returns {
          The reciprocal of the given number.
       } -body {
          return [expr {1.0 / $number}]
       }
    } -body {

       # Parse the command line
       ::Simple::Program::parse-command-line $argv

       # Display the reciprocal of the given number
       puts [reciprocal [::Simple::Option::information value -n]]
    }

See The Simple Development Library White Paper [3] for details and more examples.