Q L   H A C K E R ' S   J O U R N A L
      ===========================================
           Supporting  All  QL  Programmers
      ===========================================
         #25                        July 1996
      
    The QL Hacker's Journal (QHJ) is published by Tim
Swenson as a service to the QL Community.  The QHJ is
freely distributable.  Past issues are available on disk,
via e-mail, or via the Anon-FTP server, garbo.uwasa.fi. 
The QHJ is always on the look out for article submissions.

        QL Hacker's Journal
     c/o Tim Swenson
     5615 Botkins Rd 
     Huber Heights, OH 45424 USA
     (513) 233-2178
     swensontc@mail.serve.com
     http://www.serve.com/swensont/

EDITOR'S FORUMN

   I don't have much to say for an introduction to this
issue.  I do want to thank Peter Tillier for contributing
two articles.  He really filled a few pages for me.  The
more articles I get the easier it is on me and the more
often I can publish.  I hate it when I have programming dry
spells.  

   In QHJ #22, was a Day of the Week program.  Mel Laverne
found out one small bug in the program that did not make it
work.  Then translating from C to SuperBasic, I forgot that
the original program was done with Integer arithmatic. 
SuperBasic defaults to floating point, so the program was
off fairly often.  So change all variables to integers and
the whole thing should work out.
 
   While browing the Internet recently, I came across an
article that I had heard about but had not read; The Tao of
Programming.  The Tao of Progamming is written in a very
Eastern way of writing, with formal sounding wisdom, but
sprinkled lightly with modern humor.  Here is an example:

   "The Tao gave birth to machine language.  Machine
language gave birth to the Assembler.

   The Assembler gave birth to the compiler.  Now their are
ten thousand languages.

   Each language has its purpose, however humble.  Each
language exprresses the Yin and Yany of software.  Each
language has its place within the Tao.

   But do not program in COBOL if you can avoid it."
   
   If you find the Tao of Programming, give it a read.
   
   I hope you like this issue, and I'll see you on the
'Net.
      

BOOT UP REMINDER

   Productivity tools for the QL are far and few between. 
On the PC, there is a dirth of these tools; Meeting Maker,
Lotus Organizer, Maximizer, etc.  One feature of most
productivity tools is the ability to remind you of special
days, such as birthdays, anniversaries, appoinments, and so
on.

   Without doing much development work, a simple day
reminder can be written for the QL.  A good way to setup a
reminder program is to have it check for special days when
the QL boots up.  During boot up, the program reads in the
reminder data file and outputs any special days that are set
for today.  These special days can be set up to appear
yearly (a birthday), weekly (trash day), or monthly (bills,
bills, and more bills).  Of course, this program will only
work well if you boot up your QL at least once a day.  If
you boot it up less than that, you will need to set your
reminders to appear days before the special day.

   The format of the reminder file ( reminder_dat ) is as
follows:
   
      T:XXXXXX:.....................
      
      Where T is the type of reminder
          W for weekly, M for montly, and Y for yearly
      XXXXXX is the date of the reminder
      ...... is the text of the reminder
      Colons seperate each field.

      The program is case insensitive.
	          
   There are three types of reminders, weekly, monthly, and
yearly.  A weekly reminder is based on the day of the week. 
If you must take out the trash every Wednesday night, then
you could set a reminder for Wed to say "Take out Trash."
The first field has a W and the second field has a three
letter abreviation for the day of the week.  Mon for Monday,
Tue for tuesday, etc.  This is all based on the format
returned from DAY$.

   A Monthly reminder is based only on the day of the month.  
If you have to pay a bill on the 1st of each month, you
could set a montly reminder to "Pay Bill" for the 1st.  The
first field has an M and the second field is the day of the
month in a two number format.  The 6th of the month would be
listed as 06.

   A yearly reminder is based on the month and day.  This is
for reminding you of things like birthdays.  The first field
has a Y and the second field has a three letter abreviation
for the month (Jun), a space, and the day of the month
listed as two digits (06 for the 6th).  The 4th of Jul would
be listed as "Jul 04".

   The text of the reminder is the last field.  It goes from
the second colon to the end of the line.  You can put
anything in this text, as it is copied from the reminder
file and printed to the screen.

   This program can easily be included into a Boot program
or it can be called from the Boot program.  It simply
prints out the reminders, but you can liven it up with
flashing letters or beeping noises, what ever will get your
attention.

100 OPEN #3,scr_350x75a75x50
110 PAPER #3,0: INK #3,2: BORDER #3,3,4
120 CLS #3
130 month$ = DATE$
140 month$=upper$(month$(6 TO 11))
150 daym$ = DATE$
160 daym$=upper$(daym$(10 TO 11))
170 dayw$=upper$(DAY$)
180 OPEN_IN #4,flp1_reminder_dat
190 REPeat loop
200   IF EOF(#4) THEN EXIT loop
210   INPUT #4,in$
220    IF LEN(in$) < 3 THEN END REPeat loop
230   colon = ":" INSTR in$
240   type$ = upper$(in$(1 TO colon-1))
250   in$ = in$(colon+1 TO )
260   colon = ":" INSTR in$
270   remind$ = upper$(in$(1 TO colon-1))
280   reminder$ = in$(colon+1 TO )
290   IF type$ = "W" THEN
300      IF remind$ = dayw$ THEN
310         BEEP 1000,10
320         PRINT #3,dayw$;" ";reminder$
330      END IF
340   END IF
350   IF type$ = "M" THEN
360      IF remind$ = daym$ THEN
370         BEEP 1000,10
380         PRINT #3,daym$;" ";reminder$
390      END IF
400   END IF
410   IF type$ = "Y" THEN
420      IF remind$ = month$ THEN
430         BEEP 1000,10
440         PRINT #3,month$;" ";reminder$
450      END IF
460  END IF
470 END REPeat loop
480 CLOSE #4
490 CLOSE #3
500 DEFine FuNction upper$(up$)
510 LOCal x, temp
520 FOR x = 1 TO LEN(up$)
530   temp = CODE(up$(x))
540   IF temp > 96 AND temp < 123 THEN up$(x)=CHR$(temp-32)
550 NEXT x
560 RETurn up$
570 END DEFine upper$

Example Reminder File:

   w:tue:This is a Tuesday Reminder
   w:wed:This is a Wednesday Reminder
   m:04:This is a 4th day of the month Reminder
   m:13:This is a 13th day of the month reminder
   y:jun 04:This is a June 4th reminder
   y:jul 19:This is a July 19th reminder


PARAMETER PASSING MECHANISMS
   By Peter Tillier
   
   There are many parameter passing mechanisms that are
used to pass data into, out of or both into and out of
procedures or functions being accessed.  What is said here
applies to procedures and functions in the Pascal sense and
to functions in the C sense; so I'll omit 'or function' for
the rest of the article.

Mechanisms for Passing Data into Procedures.
--------------------------------------------

There are basically three types of input passing
mechanism:-
    - call by value;
    - call by constant-value;
    - call by reference-constant.

Note that this list doesn't include call by reference - see
later.

   Most languages support only call by value, although ADA,
for example, supports call by constant-value and can
support (implementation dependent) call by
reference-constant.

   In call by value the actual parameter's value is passed
as an initial value to the formal parameter at the time the
procedure call is made and the formal parameter (total in
the following example) then acts as a local variable within
the called procedure (i.e., it can be modified within the
procedure).  So,

    procedure DoSomethingWith(total : integer); {in Pascal}

    var symbol : char;

    begin
      while total > 0 do
        begin
          read(symbol);write(symbol);
          total := total - 1
	    { total is a local variable in Pascal }
            { so this is OK }
        end
    end { DoSomethingWith };

   In some versions of Pascal a formal parameter isn't
allowed to be the controlling variable of a 'for' loop, so
this wouldn't compile using the ProPascal compiler:

    program fortest;

    var z : integer;

    procedure test(j : integer);

    begin
      for j := j downto 0 do
        writeln( j ) { this line may fail to compile }
                     { depending on the compiler     }
    end { test };
    
    begin { fortest }
      z := 10;
      test( z )
    end { fortest }.

   [Note that, for example,  Turbo Pascal (PC) does allow
this sort of thing to compile if the formal parameter j is
a value parameter (as shown) whereas it fails to compile if
j is declared as a var parameter (which is very sensible!)]

   In call by constant-value, the formal parameter simply
receives the value of the actual parameter and then acts as
a constant within the procedure.  In ADA this is
implemented using the 'in' mode keyword (which is implied
if no mode is given explicitly), e.g,

    procedure do_something_with(total: in integer) is
                     { in ADA }
      symbol: character;
      count : integer := total;
       { required in ADA as total is constant }

    begin
      while count > 0 loop
        get(symbol); put(symbol);
        count := count - 1;       
	   { can't use total := total - 1 }
      end loop;
    end do_something_with;

   in this case 'total' is treated purely as a constant
within the procedure and the variable count is needed to
control the loop iterations.

   The main problem with call by value and call by
constant-value is that when aggregates such as Pascal (or
Modula-2) records are passed to procedures, then exact
copies of the actual parameters need to be made and placed
in the formal parameters, which is very expensive in both
space and execution time.  A common work-around in Pascal
and Modula-2 is to pass the parameters using call by
reference, where the address of the aggregate rather than
its contents is passed into the procedure, but this allows
the aggregate to be modified by the procedure (see later).

   In some implementations of ADA when aggregates are
passed using 'in' mode they are passed by
reference-constant, in which the reference is treated
locally as constant within the procedure and so its
elements cannot be updated, only referred to and copied.

   For all three of call by value, call by constant-value
and call by reference-constant the value of the actual
parameter is unaffected by the processes carried out within
the procedure body.

   In QL SuperBasic the simple rule is that any expression
[literal numeral, or variable *+-/ literal numeral or
variable or similar combination] is passed to the procedure
or function by value. The interpreter really can't do
anything else.  It has to evaluate the expression and then
assign its result to a temporary storage location which is
the formal parameter in the proc/func's declaration, i.e.,
x in Tim's example procedure inc(x) in QHJ #24.  This x may
be modified within the procedure (call by value).

Mechanisms for Passing Data out of Procedures.
----------------------------------------------
    
   Most languages do not have a specific mechanism for
passing information out of procedure (unless the parameter
is both in and out), but ADA supports call by result which
is implemented as an 'out' mode parameter.  Such  'out'
mode formal parameters act as if they are uninitialized
local variables on entry to the procedure and the last
value that they receive during the procedure is returned in
the actual parameter.  So in ADA you can get this sort of
thing:

    procedure read_neg_num( negative : out integer) is
      number : integer;

    begin
      get(number);
      while number > 0 loop
        put_line("Number not negative, try again");
        get(number);
      end loop;
      negative := number;
    end read_neg_num;

   In Algol-W call by result is also used, but by a
slightly different mechanism when compared with ADA.  If
the formal parameter is a structure, then the same local
copy problems occur as with input parameters; so ADA allows
aggregates to be passed 'out' by reference. Pascal and
Modula, etc., don't support the call by result mechanism at
all and use call by reference to return values to calling
procedures.

Mechanisms for Updating Data Passed to Procedures.
--------------------------------------------------
    
   The first mechanism that achieves updating is call by
value-result which combines call by value and call by
result.  In this the formal parameter acts as a local
variable, which is initialized to the value of the actual
parameter.  During the procedure's execution changes to the
formal parameter only affect the local copy, but the actual
paramater is updates to the latest value of the local copy
on exit from the procedure.  ADA achieves call by
value-result for its 'in out' mode parameters, but uses
call by reference (qv) for aggregates.

    The second mechanism is call by reference (and is by
far the most common) and Pascal, Modula, C, FORTRAN, PL/I,
SuperBasic, etc., are all capable of updating parameters
using this approach.  In some cases call by reference is
the default (FORTRAN, PL/I) in others call by value
(Pascal, Modula, ADA) and in others it depends on the
parameter type - in C arrays are passed by reference as a
default, everything else is by value.

    In SuperBasic when the actual parameter is the name of
a variable [not contained in an expression], the
interpreter has a choice - as Tim says in QHJ #24 - it can
pass the address of the variable (its reference) or its
value.  All implementers of compilers and interpreters need
to decide what to do in this situation as default.  The
default for SB is by reference, but it is simple to pass by
value when required - you simply create an expression using
parentheses around the variable name and the result is
passed by value.

    There is another type of updating call mechanism "call
by name" that was used in Algol 60.  In this the name of
the actual parameter effectively textually replaces the
occurrence of the formal parameter in the procedure body,
with the address of the actual parameter being recalculated
each time it is needed.  This is much more expensive to
implement and in execution than call by reference, so it
has not been used in more modern languages.

Other Issues.
-------------
    
   Some languages (C++, ADA and others) support overloading
of procedure and function names wherein different instances
of the same procedure name are distinguished by their
parameter types.  For example in ADA,

    procedure swap(a,b : in out real) is
    begin
      ...
    end swap;

    procedure swap(a,b : in out integer) is
    begin
      ...
    end swap;

   The compiler sorts out which procedures is being called
by inspecting the types of the parameters and then uses the
approriate version.

   Normally actual parameters and formal paramaters are
associated by the order in which they occur in the
procedure call and procedure declaration, i.e., 1st formal
paramater = 1st actual parameter, and so on.  In PL/I and
ADA it is possibly to make associations explicitly by name
(Named Association), for example in ADA,

    procedure inc(val : in out integer;
                   by : in integer) is
    begin
      val := val + by;
    end inc;

    and the call could be written in any of the following
ways,

         inc( number, 2 );
         inc( number, by => 2 );
         inc( val => number, by => 2 );

    or even,

         inc( by => 2, val => number );

    - all with exactly the same effect!

   Some languages support the idea of default values (C++,
ADA) for formal parameters so that if no parameter is
supplied explicitly the default value is used.  For example
in ADA if 'by' is defaulted to 1, then,

    procedure inc(val : in out integer;
                   by : in integer := 1 ) is
    begin
      val := val + by;
    end inc;

    inc(number);

    is identical in effect to

    inc(number,1);

   You can also pass procedure or function names as
parameters in some languages - food for another article
sometime...

   One thing that I try to do when writing procedures that
I believe is a good idea is to only allow the code in the
procedure to modify or use either local variables or
parameters.  In other words I make every effort NOT to use,
and especially NOT to modify, global variables or non-local
variables that are effectively global to the procedure
under the scope rules.  This makes it much easier to
identify from the procedure code what is its precise
effect.  When I am constrained to modify globals or other
non-local variables in scope then I tend to use comments to
this effect in the code, e.g.,

    procedure SomethingOrOther( var x : intger;
                                var y : real );

    (* GLOBALS: var a : SomeRecordType;
                          {this is read-write accessible}
                    b : SomeOtherRecordType;
		          {this is read} *)

    (* NON-LOCALS: var q : boolean; 
                       p : integer; *)

    label ...;

    const ...;

    type ...;

    var ...;

    procedure ...;

    function ...;

    ...

    begin {SomethingOrOther}

    ...

    end {SomethingOrOther};


   This makes it quite clear to someone reading the code
that the identified variables are potentially modified by
the procedure.  It suits me - others will, no doubt, hate
it, but I defend it on maintenance grounds.  I would still
prefer to pass the items as parameters even if the
parameter list gets very long as a result.

Some Thoughts on Functions.
---------------------------
    
   Functions and procedures only really differ in that the
former return values directly to the calling procedure.  In
FORTRAN, ALGOL 60 and Pascal this is achieved by assigning
a value to the function name within the function body.  In
later languages such as C and Modula a 'return' statement
is used to pass a value to the caller.  [NB In ADA, C and
FORTRAN the return statement can also be used without an
argument to exit a procedure]

   Functions can usually take both value and reference
parameters too; so there is the possibility of functions
returning lots of values!! This has to be a really bad
idea, but the languages let you do it!!!

   In fact, ADA prevents 'out' and 'in out' mode for
function parameters, but still allows the programmer to
'mess about' with globals and variables in scope -
gruesome! .  I believe that EUCLID and quite a few
functional languages do prevent such social solecisms, but
I only wish C, C++ (yes!), Pascal and others did too.  We'd
all be much better programmers (or atleast we'd produce
better source code) if language designers reduced the
possibility of side-effects that can be created (all too
often unwittingly!)



SOME THOUGHTS ON PROGRAMMING STYLE
   By Peter Tillier
   
   In QHJ #24 Tim talks about a colleague's style of
writing Perl and contrasts it with his own.  I have spent
several years as a programming and system development
lecturer within my company's internal training department
and nothing seems to cause more grief/criticism/etc., etc.,
as differences of programming style.

   I tend to use procedure calls in preference to the use
of deep nesting of 'if..then..else..endif' structures as
does Tim's colleague. I do this for a number of reasons and
even if the procedure may only be called once in the entire
program (incidentally this approach is taken by Kernighan
and Ritchie in 'The C Programming Language' and by
Kernighan and Plauger in 'Software Tools in Pascal').

My reasons are these:

it is sometimes inconvenient to read deeply nested
'if..else..endif' or 'while..endwhile' constructions;

this approach works very well with the program design
method that I prefer to use (Jackson Structured
Programming, aka. JSP);

if suitable procedure names are chosen the clarity of the
code is often improved;

the style is closer to the object-oriented programming
approach that I would prefer to use;

the arguments about inefficiency ("It's wasteful to set up
a stack frame and call code that could have been inline.")
take little account of the maintenance benefits that can
accrue from well-designed and named procedures.

   I find something like this much easier to follow (and
debug!),

    procedure DoLotsOfThingsTo(var A : AType);

    var
         i : integer;

    procedure DoOneThingTo(var A : AType );

    begin
         A.A := ...;
         A.B := ...;
    end {DoOneThingTo};

    begin
         for i := 1 to SizeOfAType do
              DoOneThingTo(A);
         ...
    end {DoLotsOfThingsTo};

   (the above also shows one reason that I like Pascal -
the ability to nest procedure declarations - I miss it a
lot when I use C, C++ or things like Visual Basic).

   Incidentally, Question: can you nest procedure
definitions in SuperBasic? 

   Answer: Yes you can:

    1000 define procedure testa
    1010 :
    1020 define procedure testb
    1030 print "In testb"
    1040 end define testb
    1050 :
    1060 print "In testa (1)"
    1070 testb
    1080 print "In testa (2)"
    1090 :
    1100 end define testa

    works perfectly, printing out,

    In testa (1)
    In testb
    In testa (2)

    as expected.

   As I said in my article on parameters and parameter
passing mechanisms I think that most languages would be
better if they were designed so that procedures and
functions (in the SB or Pascal sense) could only access
local variables or parameters - even for read access only.


SOFTWARE REUSE

   For years I've reading articles on Software Reuse and
how it can increase the productivity of programming shops. 
Since I program alone, as most QLers programmers do, I have
never given it much thought for my programming.  For some
reason, a recent article on software reuse sparked a new
thought about software reuse and the QL.

   Before I go into my sparked thought, I want repeat
here one sideline from the article.  The Eight Commandments
of Reuse:

1. Golden rule of reuse: encourage individuals and teams to
behave in ways that support reuse.

2. Keep an inventory of reusable artifacts.

3. Provide a catalong with descriptors and search support.

4. Desigate a reuse administrator/facilitator who keeps the
catalog and helps users.

5. Develop a methodology outlining how and when to reuse
software components.

6. Have a measurement program to track reuse and adherence
to the methodology.

7. Design standards that specify how artifacts are
contructed.

8. Adhere to a quality-assurance program to guarantee the
integrity of artifacts.

   Now that you have read the above, set it aside for the
moment (for you Assembly programmers, PUSH it.  You will
need to POP it later).

   I think one of the most difficult areas of writing
programs for the QL is dealing with the Pointer
Environment.  You either buy a PE toolkit (such as
EasyPointer) and a SuperBasic Compiler (QLiberator) for a
fair amount of dollars, or, you can program in C with C68
and the Pointer libraries.  Being cheap, I would opt out
for C68, but I am very weak with full C (OK, I write a few
hacks in C, but I am no where near calling myself a C
programmer).  Using C68 and the Pointer Environment is not
trivial.  It's not something for the fledgling C
programmer.

   For those that do program in C and the Pointer
Environment, each programmer is writing a lot of the same
display routines to get output to the screen.  For some
this is not easy and takes up some significant time and
effort.

   OK, now POP what you had PUSHed earlier; software reuse.
What if a number of C68 programmers were to get together
(just like they do in the development of C68) and started
collecting a library of C68 PE routines that could be used
by other programmers?  Kind of sounds like software reuse.

   If QLers where to use the 8 Commandments of Reuse, we
would only need to use commandments 2, 3, 4, 5, and 7.  We
would not need to track who uses reuse or who does not.  If
someone was to volunteer to be the administrator (they
would need to be a C68 programmer), other C68 programmers
could send in thier functions and procedures to be added to
a library.  This could be documented and then distributed
back out to C68 programmers.

   Submiting functions and procedures may require some code
changes on the part of the submitting programmers.  The
functions and procedures would have to be written in a more
portable "black box".  No use of global variables.

   I don't know if the time and effort put into this would
save any programmer time in the long run.  The time saver
for the programmer would be the time saving in having to
re-write code that has been written before.  Would this
time savings be enough to warrant the cost of organizing
the library?  Again, I don't know.  I just thought it might
be worth considering.  Any takers?


DBAS

   For most database programming, the QL programmer has
been pretty much stuck with Archive.  Archive is a fine
language and is fairly similar to dBase III in programming
feel.  It has many advantages: editing of records built in,
easy screen creation, a well structured language.  But it
also has a few weak points: limited functions, little
control over end user accessing code, relatively slow.

   If you are looking for a database development system that
allows you to create stand-alone code, full access to
features of QDOS, relatively fast, and free, then DBAS is
something that you should look into.

   DBAS, also called Database Handler, is a library of
database handling routines for SuperBasic, C68, or Machine
Code.  DBAS is not a database language system like Archive,
so it is not a true replacement for Archive.

   The core part of DBAS resides in DATA_BIN.  It is loaded
by LRESPR.  DATA_BIN contains the main routines for
database handling, but only for Machine Code programs.  If
you use SuperBasic, DBAS_BIN contains the SuperBasic
interface to DATA_BIN, and it too is LRESPRed. For C68
programmers, there is a library of database routines that
access DATA_BIN.

   Programming with DBAS is not as easy as programming with
Archive.  You are using DBAS for database function calls,
but you still have your programing control constructs
(looping, branching, etc) in SuperBasic or C68.  What you
loose in ease of programming from Archive, you gain in
power of programming.  Since you are using SuperBasic or
C68 to program in, you still have the full power of either
language and all that they can do.

   DBAS has both procedures and fuctions.  A sampling of
procedures is:
   
      ADD_FIELD     Add a field
      APPEND        Add a new record
      CREATE        Create a database
      EXCLUDE       deselect records
      FIND          Find by INSTR
      INCLUDE       Select records
      LOCATE        Find by ORDER paramaters
      OPEN_DATA     Open a database
      ORDER         Order a database
      REMOVE        Delete a record
      SEARCH        Find by INCLUDE paramaters
      UPDATE        Update a record

   A sample list of fuctions is:

      COUNT         Get record count
      FETCH         Get record contents
      FLLEN         Get field length
      FLNAME        Get field name

   Databases are treaded like files and are opened with the
OPEN_DATA procedure.  After that they are refered to by
their channel number.  Fields do not specifially have names
- they are referenced by field number - but field names can
be implemented with some work arounds.

   DBAS does not seem to prohibit opening more than one
database at one time, but I do not see in any of the
commands where you can specify a JOIN ( selecting records
from two databases/tables with a common equality).  By doing
a couple of searches on each database, you should be able to
rig up the equivalency of a JOIN.

   Since DBAS does not have a front-end for doing database
creating, editing databases, etc, two utilities come with
DBAS to make maintaining individual records easier. 
DBPTR_BIN is a Pointer Environement program for editing,
adding, and deleting records.  For non-PE users, there is
ALTER_BIN.  Both of these programs are executables.

   DBAS has a lot of potential.  Since it is LRESPRed,
it is compatible with SuperBasic compilers, like QLiberator.
You can compile your code and create a stand alone
application.  The _BIN files are freeware and can be
distributed with your program.

   DBAS comes with full documentation for all of its
features, including the SuperBasic, C68, and Machine Code
interfaces. It comes with example programs that help in
learning how to use DBAS.

   If you are new to databases and you want to learn how to
program them, stay with Archive.  If already know how
databases work and want to develop your own stand-alone
applications, then DBAS is worth the look.

   DBAS should be available on most QL BBS's worldwide. 
For North American QL users, you can get it from QHJ
Freeware (me) (just send a disk with return postage).

    Source: geocities.com/siliconvalley/pines/5865

               ( geocities.com/siliconvalley/pines)                   ( geocities.com/siliconvalley)