Storing Class Definitions on File

Dick Bowman

August 2006

Note - the work described here has essentially been replaced by a modified approach, which plunders a little of Dyalog's own SALT.  I believe the code described below remains functional and could prove useful, but readers are also directed to the more recent "Living on a LowSALT Diet".

At one level Dyalog APL's classes can be seen as a variation on utility code, and it's easy to think of using them in applications in much the same way as utility code.  I described an approach to utilities earlier.

Briefly - the goals are


Dyalog themselves offer SALT as an approach to class code maintenance - and the work outlined here may be overtaken by all or part of SALT, but for the nonce here it is as a low-footprint alternative.

Design Goals and Issues

The design is essentially a transmigration of the previously-mentioned utility code mechanism.


Classes are stored as components in a component file, which also has a table of essential information about each class

Name Target namespace Timestamp Component Comment Requires
The name of the class Where it goes in the application workspace Last saved/edited (not currently active) Which file component contains this class definition Not currently used Pre-requisite classes (may be replaced by class-specific syntax and hence be redundant here)

The code for class maintenance is written with no use of APL/W classes itself - obviously it could be more elegant to use classes for tasks like reading and writing component files, but this stuff is supposed bootstrap itself in as the application starts up, so simplicity is the watchword.

ClassLoad

To load class(es), use ClassLoad, for example...

  'd' ClassLoad 'c:\dick\mycode\dyalog11\testing\classfile' 'DogButton'
<ClassLoad> Loaded <DogButton> into <#.CLASSES.GUI>
 DogButton

Syntax of all of these functions is pretty similar...

Left Right Result
Mode, 'd' meaning "developer mode", which turns all the loading and saving on
  • Name of the file containing class definitions
  • Class Name(s)
Class name
(there's a bit of narrative side-effect, just to say what's going on - this will probably be removed some day)

The code of ClassLoad is quite mundane...

     loaded←a ClassLoad w;⎕IO;⎕ML;tie;reqd;class;ns;comp;src;file;classlist
⍝ Load required classes
 ⎕IO ⎕ML←0 3
 loaded←''
 file classlist←w
 classlist←(⊂⍣(1=≡classlist))classlist
 :If a='d'
     tie←ClassFile ⎕FSTIE 0
     index←⎕FREAD tie 3
     reqd←,({(+/∧\⍵=' ')↓⍵}¨((0⌷[1]index)∊classlist)⌿4⌷[1]index)~⊂''
     :If ×⍴reqd
         reqd←⊃,/{(⍵≠' ')⊂⍵}¨reqd
         reqd←reqd~classlist
     :EndIf
     :While ×⍴reqd
         classlist←∪classlist,reqd
         reqd←,({(+/∧\⍵=' ')↓⍵}¨((0⌷[1]index)∊classlist)⌿4⌷[1]index)~⊂''
         :If ×⍴reqd
             reqd←⊃,/{(⍵≠' ')⊂⍵}¨reqd
             reqd←reqd~classlist
         :EndIf
     :EndWhile
     :For class :In ∪classlist
         :If (⊂class)∊0⌷[1]index
             ns comp←((0⌷[1]index)⍳⊂class)(1 3)⌷index
             src←⎕FREAD tie,comp
             :If 0=⎕NC ns
                 ns ⎕NS''
                 ⎕←'<ClassLoad> Created ',ns
             :EndIf
             ⍎ns,'.⎕fix src'
             ⎕←'<ClassLoad> Loaded <',class,'> into <',ns,'>'
             loaded,←⊂class
         :Else
             ⎕←'<',class,'> not found in <',ClassFile,'>'
         :EndIf
     :EndFor
     ⎕FUNTIE tie
 :EndIf

ClassSave

To save class definitions use ClassSave, for example...

'd' ClassSave 'c:\dick\mycode\dyalog11\testing\classfile' 'DogButton'
<ClassSave> Saved modified DogButton

Same argument structure, but no result.  Code is equally mundane...

mode ClassSave w;⎕IO;⎕ML;class;tie;index;row;freelist;component;ns;file;classlist;nslist
⍝ Save changed classes
 ⎕IO ⎕ML←0 3
 file classlist←w
 classlist←(⊂⍣(1=≡classlist))classlist
 :If mode='d'
     tie←ClassFile ⎕FSTIE 0
     index←⎕FREAD tie 3
     nslist←'#',flatten namespacelist'#'
     :For class :In classlist
         row←index[;0]⍳⊂class
         ns←∊(9.4=⎕NC 2↓¨nslist,¨⊂'.',class)/nslist
         :If row∊⍳↑⍴index
             (row(1 2)⌷index)←ns ⎕TS
             index ⎕FREPLACE tie 3
             (⎕SRC⍎ns,'.',class)⎕FREPLACE tie,row 3⌷index
             ⎕←'<ClassSave> Saved modified ',class
         :Else
             :If 0=⍴freelist←⎕FREAD tie 4
                 index,[0]←class ns ⎕TS(1⊃⎕FSIZE tie)'' ''
                 index ⎕FREPLACE tie 3
                 (⎕SRC⍎ns,'.',class)⎕FAPPEND tie
             :Else
                 component←↑freelist
                 (1↓freelist)⎕FREPLACE tie 4
                 index,[0]←class ns ⎕TS component'' ''
                 index ⎕FREPLACE tie 3
                 (⎕SRC⍎ns,'.',class)⎕FREPLACE tie component
             :EndIf
             ⎕←'<ClassSave> Saved new ',class
         :EndIf
     :EndFor
     ⎕FUNTIE tie
 :EndIf

namespacelist
and flatten are utilities that find the names of all namespaces in the active workspace and flatten this result respectively - left as exercises for the reader.  What's going on here is that the code looks for the class rather than assumes that it's where it's supposed to be (because classes new to file don't have an entry in the file index, and possibly there's been some shuffling around by the developer - although doing this is not a good idea, because of the potential impact on other applications)

ClassList

ClassList is a straightforward formatter for the file index...

      ClassList 'c:\dick\mycode\dyalog11\testing\classfile'
 Name       Namespace      Timestamp               Component  Comment  Required
 DogButton  #.CLASSES.GUI  2006 5 30 13 54 26 906         12                   
 DogEdit    #.CLASSES.GUI  2006 5 25 15 30 40 328         13                   
 DogForm    #.CLASSES.GUI  2006 5 25 15 49 54 125         11                   
 DogRadios  #.CLASSES.GUI  2006 5 25 15 30 53 828         14         

Left as an exercise for the reader

ClassInfo


ClassInfo returns vital statistics on the component file which contains class definitions

   ClassInfo ClassFile
Folder      ←→ c:\dick\mycode\dyalog11\Testing
Name        ←→ classfile.dcf                 
Size        ←→ 5940                          
Created     ←→ 24/05/2006 14:53:36           
Last Access ←→ 07/08/2006 14:29:56           
Last Write  ←→ 03/08/2006 15:32:38      

Nothing very special about the code (just an example of using .NET calls)...

 z←ClassInfo file;⎕IO;⎕ML;⎕USING;info
⍝ Information about the class file
 ⎕IO ⎕ML ⎕USING←0 3 ''
 info←⎕NEW System.IO.FileInfo(⊂file)
 z←⊃'Folder' 'Name' 'Size' 'Created' 'Last Access' 'Last Write'
 z,←((↑⍴z),4)⍴' ←→ '
 z,←⊃⍕¨info.(DirectoryName Name Length CreationTime LastAccessTime LastWriteTime)

It would probably be more useful as raw data, without the formatting (another exercise for the reader).   

ClassRemove

ClassRemove is used to remove a class definition from file...

ClassRemove 'c:\dick\mycode\dyalog\testing\classfile' 'DogButton' 

Again, left as an exercise for the reader (don't forget to return the newly-emptied component(s) to the free components list).

And Finally...

It's a simple mechanism, and there are a few loose ends to tidy up, chiefly...


Page updated 3 November 2006

Copyright İ Dogon Research, 2006.  Material from the Dogon Research Music and APL Pages may be freely redistributed, but copyright is retained and attribution to Dogon Research is requested.