Dyalog User Commands
Overview
User commands play a crucial role for Dyalog APL developers. Dyalog comes with a rich set of user commands, but independent parties also develop and maintain user commands. This article discusses how and where to install third-party user commands, and how to load them into ⎕SE
.
Prerequisite Knowledge
This article assumes that you are familiar with the concept of Dyalog user commands, and understand what a user command script is, along with its specific features and properties. If you are new to these topics, it is recommended to first study Dyalog's "User Commands User Guide".
Installing user commands
Dyalog installs its own set of user commands into [DYALOG]/SALT/spice
. Installing third-party user commands into this folder comes with both advantages and disadvantages.
Advantages of installing into the [DYALOG]/SALT/spice
folder:
- User commands cannot be modified by ordinary users; admin rights are required
- Every version of Dyalog has its own set of user commands
On the other hand, installing user commands in a different folder, such as MyUCMDs/
, has its own advantages:
- Users have write permission to this folder, making updates easier
- Anything installed into this folder will be available in all installed versions of Dyalog APL
- Each user has their own
MyUCMDs/
folder, which can be seen as an advantage or disadvantage based on individual preferences
Both the SALT/spice
folder and the MyUCMDs/
folder are scanned for user command scripts at startup.
Location of the MyUCMDs/ folder
The location of the MyUCMDs/ folder depends on the operating system used:
- On Windows it's usually
C:\Users\<username>\Documents\
- On Linux it is
/home/<username>/
- On Mac OS it is
/Users/<username>/
Note that MyUCMDs/
is created by the Dyalog APL installer on Windows, but you need to manually create it on non-Windows platforms before version 19.0.
If you have Tatin installed, or you are using version 19.0 or later (those come with Tatin) then you can call this Tatin API function:
⎕se.Tatin.GetMyUCMDsFolder'' ⍝ Windows C:\Users\<username>\Documents\MyUCMDs
Availability of user commands
Once a user command script is moved into the MyUCMDs/
folder (or a sub-folder of it), the user command becomes available across all versions of APL.
For simple user commands where all the code resides in the script itself, this is the end of the process. However, if a user command relies on a larger set of code files that need to be loaded into ⎕SE
for execution, additional steps are required.
The user command script could of course check whether the code is already available in ⎕SE
and if not load it, and that would work just fine.
User commands with an API
It has become increasingly popular to add an API to user commands. For example, the Dyalog APL project manager Cider offers a rich set of user commands:
]cider* -? CIDER: CloseProject Breaks the Link between the workspace and the files on disk CreateProject Makes the given folder a project folder Help Offers to put the HTML files on display ListAliases List all defined aliases with their folders ListOpenProjects List all currently open projects ListTatinPackages Lists all Tatin packages in all install folders Make Build a new version of the project OpenProject Load all source files into the WS and keep it linked by default RunTests Executes the project's test suite (if any) Version Returns name, version number and version date as a three-element vector ViewConfig Puts the config file of a project on display
But Cider also offers an API (public interface):
↑⎕se.Cider.⎕nl -3 AddAlias CloseProject CreateOpenParms DropAlias GetAliasFileContent GetCiderAliasFilename GetCiderConfigFilename GetMyUCMDsFolder GetProjectPath ListOpenProjects ListTatinPackages OpenProject RenameInfo_url RunMake RunTests Version ViewConfig
If you want to use any of the API functions without the need of first calling one of the Cider user commands (that would force the user command loading the code into ⎕SE
), then you have to make sure that the code is loaded by other means, ideally at an early stage: as part of the bootstrapping process.
Introducing setup.dyalog in MyUCMDs/
To address this, a script named setup.dyalog
is introduced in the MyUCMDs/
folder. When Dyalog encounters this script, it checks for the presence of a function called Setup
and executes it.
Notes:
- The script name must be lowercase to ensure compatibility with non-Windows platforms
- The script can be a class or a namespace
- The
Setup
function must accept a right argument
- The function
Setup
must return a result
There is no setup.dyalog yet
Create one that looks like this:
:Namespace SetItUp ∇ {r}←Setup arg;⎕IO;⎕ML;dmx r←⍬ ⎕IO←1 ⋄ ⎕ML←1 :Trap ⎕SE.SALTUtils.DEBUG↓0 ⎕←LoadMyUserCommand ⎕SE.SALTUtils.DEBUG :Else dmx←⎕DMX ⎕←'Setup.dyalog has a problem and was not executed successfully:' ⎕←↑' '∘,¨dmx.DM :EndTrap ∇ ∇ r←LoadMyUserCommand debug;wspath;path2Config r←0 0⍴'' :Trap debug/0 :If ~IfAtLeastVersion 18 r←'MyUserCommand not loaded: not compatible with this version of Dyalog' :ElseIf 80≠⎕DR' ' ⍝ Not in "Classic" r←'MyUserCommand not loaded: not compatible with Classic' :Else ... ⍝ Load your stuff :EndIf :Else r←'Attempt to load MyUserCommand failed with ',⎕DMX.EM :EndTrap ∇ IfAtLeastVersion←{ ⍝ ⍵ is supposed to be a number like 15 or 17.1, representing a version of Dyalog APL. ⍝ Returns a Boolean that is 1 only if the current version is at least as good. ⍵≤{⊃(//)⎕VFI ⍵/⍨2>+\'.'=⍵}2⊃# ⎕WG'APLVersion' } ∇ r←GetMyUCMDsFolder add ⍝ Returns standard path for Dyalog's MyUCMDs folder.\\ ⍝ Works on all platforms but returns different results.\\ add←{(((~'/\'∊⍨⊃⍵)∧0≠≢⍵)/'/'),⍵}add OS←3↑⊃#.APLVersion :If 'Win'≡OS r←⊃{⍺,⍵}/1 ⎕nparts (2⊃4070⌶0),'\..\MyUCMDs' :Else r←(2 ⎕NQ'.' 'GetEnvironment' 'Home'),'/MyUCMDs',add :EndIf ∇ :EndNamespace
Note that this also checks the version of Dyalog APL and whether it's "Classic" or not. Please customize the script to fit your specific needs. Ensure to modify the IfAtLeastVersion
function, if necessary.
If your user command is not a Tatin package then this will do.
There is already a setup.dyalog
Copy the functions IfAtLeastVersion
, GetMyUCMDsFolder
and LoadMyUserCommand
from above into your own setup.dyalog
script.
Make sure that LoadMyUserCommand
is called from your Setup
function.
This makes sure that the API of your user command is available right after instantiating Dyalog APL.
User commands that are Tatin packages
Since version 0.86. 0 a Tatin package can be marked as a user command: by specifying the path to a user command script with a project.
Example:
userCommandScript: "APLSource/MyUserCommand,dyalog",
With this line in the file apl-package.json
such a package can be installed and loaded with Tatin:
]Tatin.InstallPackages [tatin]MyUserCommand [MyUCMDs] ]Tatin.LoadDependencies [MyUCMDs]MyUserCommand
Notes:
- There is no name specified after
[MyUCMDs]
in the second argument ofInstallPackages
: this makes the function use the name of the package for the folder to be created inMyUCMDs/
, hereMyUserCommand
LoadDependencies
will look for a folderMyUserCommand
in theMyUCMDs/
folder. If there is one, and it contains a fileapl-dependencies.txt
, then the package will be loaded into⎕SE
.
- Usually
LoadDependencies
loads packages into#
in case no second argument is specified, but because the folder was specified as an alias ([MyUCMDs]
) the function knows that this is about a user command, and therefore the default target for the load operation is⎕SE
rather than#
.
- The user command script
MyUserCommand.dyalog
is moved to the top of the folder hosting the user command by]Tatin.Installpackages
, here[MyUCMDs]/MyUserCommand
Loading all such user commands
If you want to make sure that all user commands that are Tatin packages are loaded into ⎕SE
at an early stage then add this code to your setup.dyalog
script and make sure that it is called by your Setup
function:
r←path LoadUserCommandPackages debug;home;name;res;folders;folder;F ⍝ This loads Tatin packages that are user commands installed in "path" r←'' F←⎕SE._Tatin.FilesAndDirs :If 0<≢folders←F.ListDirs path :For folder :In folders :If F.IsFile folder,'/apl-buildlist.json' name←2⊃⎕NPARTS folder :Trap (~debug)/0 {}⎕SE.Tatin.LoadDependencies(path,name)⎕SE :Else r,←⊂'>>> Attempt to load ',name,' failed with ',⎕DMX.EM :EndTrap :EndIf :EndFor :EndIf :If 0=≢r r←0 0⍴'' :Else r←⍪r :EndIf ⍝Done
Conclusion
- Every user command that relies on code that is not part of the user command script as such should check whether that code is already in
⎕SE
and load it into⎕SE
if not - There are good reasons to load all user commands, packaged or otherwise, at an early stage as part of the Dyalog bootstrapping process
- If you have specific user commands that consume a significant amount of memory and are infrequently executed, you can request Dyalog to introduce a mechanism that allows a package to indicate its preference for loading its own code.