Dyalog User Commands: Difference between revisions

Jump to navigation Jump to search
607 bytes added ,  20:45, 20 January 2023
Correcting some issues
No edit summary
(Correcting some issues)
Line 1: Line 1:
== Overview ==
== Overview ==
User commands are useful for the Dyalog  APL developer. 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.
User commands are useful for the Dyalog  APL developer. 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 </syntaxhighlight lang=apl inline>⎕SE</syntaxhighlight>.
 
This article assumes that you are familiar with the concept of Dyalog user commands, and that you know what a user command script is, and what specific features and properties it has. If you are not familiar with these topics this article is not for you: study Dyalog's "User Commands User Guide" first.
This article assumes that you are familiar with the concept of Dyalog user commands, and that you know what a user command script is, and what specific features and properties it has. If you are not familiar with these topics this article is not for you: study Dyalog's "User Commands User Guide" first.
Dyalog installs its own set of user commands into <syntaxhighlight lang=apl inline>[DYALOG]/SALT/spice</syntaxhighlight>. Installing third-party user commands into this folder has advantages and disadvantages.
Dyalog installs its own set of user commands into <syntaxhighlight lang=apl inline>[DYALOG]/SALT/spice</syntaxhighlight>. Installing third-party user commands into this folder has advantages and disadvantages.
Pro:
Pro:
* The user commands cannot be changed by an ordinary user (admin rights are required for that)
* The user commands cannot be changed by an ordinary user (admin rights are required for that)
* Every version of Dyalog has its own set of user commands
* Every version of Dyalog has its own set of user commands
But what some may consider an advantage can also be viewed as a disadvantage. Installing it into a different place, the folder <syntaxhighlight lang=apl inline>MyUCMDs/` (we discuss in a second where that folder lives), has some advantages:
 
But what some may consider an advantage can also be viewed as a disadvantage. Installing it into a different place, the folder <syntaxhighlight lang=apl inline>MyUCMDs/</syntaxhighlight> (we discuss in a second where that folder lives), has some advantages:
 
* The user has "write" permission to that folder, so updating is easy
* The user has "write" permission to that folder, so updating is easy
* Anything installed into this folder will be available in all installed versions of Dyalog APL
* Anything installed into this folder will be available in all installed versions of Dyalog APL
Note that every user has their own folder </syntaxhighlight>MyUCMDs/`. Again this might be considered an advantage or a disadvantage.
 
Like the <syntaxhighlight lang=apl inline>SALT/spice</syntaxhighlight> folder, <syntaxhighlight lang=apl inline>MyUCMDs/` is scanned for user command scripts at startup time.
Note that every user has their own folder </syntaxhighlight lang=apl inline>MyUCMDs/</syntaxhighlight>. Again this might be considered an advantage or a disadvantage.
== Where does the folder MyUCMDs/ live? ==
 
Like the <syntaxhighlight lang=apl inline>SALT/spice</syntaxhighlight> folder, <syntaxhighlight lang=apl inline>MyUCMDs/</syntaxhighlight> is scanned for user command scripts at startup time.
 
== Where does the folder MyUCMDs/ live? ==
 
The answer to this question depends on the operating system used.  
The answer to this question depends on the operating system used.  
* Under Windows it's usually </syntaxhighlight>C:\Users\<username>\Documents\`
 
* Under Windows it's usually </syntaxhighlight>C:\Users\<username>\Documents\</syntaxhighlight>
* Under Linux and Mac OS it is <syntaxhighlight lang=apl inline>/home/<username>/</syntaxhighlight>
* Under Linux and Mac OS it is <syntaxhighlight lang=apl inline>/home/<username>/</syntaxhighlight>
Note that this folder is created by the Dyalog APL installer under Windows but it won't exist under Linux and Mac OS in versions before 19.0, so you need to create the folder yourself on non-Windows platforms.
Note that this folder is created by the Dyalog APL installer under Windows but it won't exist under Linux and Mac OS in versions before 19.0, so you need to create the folder yourself on non-Windows platforms.
If you have Tatin installed, or you are using version 19.0 or later (those come with Tatin automatically) then you can call this Tatin API function:
If you have Tatin installed, or you are using version 19.0 or later (those come with Tatin automatically) then you can call this Tatin API function:
<syntaxhighlight lang=apl>
<syntaxhighlight lang=apl>
       ⎕se.Tatin.GetMyUCMDsFolder'foo'
       ⎕se.Tatin.GetMyUCMDsFolder'foo'
C:\Users\kai\Documents\MyUCMDs
C:\Users\kai\Documents\MyUCMDs
</syntaxhighlight>
</syntaxhighlight>
== Availability of user commands ==
== Availability of user commands ==
Once a user command script is moved into a <syntaxhighlight lang=apl inline>MyUCMDs/` folder (or a sub-folder of it) the user command will be available from any version of APL.
 
Once a user command script is moved into a <syntaxhighlight lang=apl inline>MyUCMDs/</syntaxhighlight> folder (or a sub-folder of it) the user command will be available from any version of APL.
 
And that could be the end of the story in case the user command you want to install is relatively simple, so all the code can go into the script.
And that could be the end of the story in case the user command you want to install is relatively simple, so all the code can go into the script.
It's a different story when the user command relies on, say, a large set of code files that need to be loaded into </syntaxhighlight>⎕SE` in order to execute the user command.  
It's a different story when the user command relies on, say, a large set of code files that need to be loaded into </syntaxhighlight>⎕SE</syntaxhighlight> in order to execute the user command.  
 
Now of course the user command script could check whether the code is already available in <syntaxhighlight lang=apl inline>⎕SE</syntaxhighlight> and if not do the loading, and that would work just fine.
Now of course the user command script could check whether the code is already available in <syntaxhighlight lang=apl inline>⎕SE</syntaxhighlight> and if not do the loading, and that would work just fine.
== User commands with an API ==
== User commands with an API ==
However, these days it is becoming increasingly popular to add an API to a user command.
However, these days it is becoming increasingly popular to add an API to a user command.
For example, the Dyalog APL project manager [Cider](https://github.com/aplteam/Cider "Link to the Cider repo on GitHub") comes with a rich set of user commands:
 
For example, the Dyalog APL project manager [Cider](https://github.com/aplteam/Cider) comes with a rich set of user commands:
 
<syntaxhighlight lang=apl>
<syntaxhighlight lang=apl>
  ]cider* -?
  ]cider* -?
Line 45: Line 67:
   ViewConfig        Puts the config file of a project on display
   ViewConfig        Puts the config file of a project on display
</syntaxhighlight>
</syntaxhighlight>
But Cider also offers a public interface:
But Cider also offers a public interface:
<syntaxhighlight lang=apl>
<syntaxhighlight lang=apl>
       ↑⎕se.Cider.⎕nl -3
       ↑⎕se.Cider.⎕nl -3
Line 66: Line 90:
ViewConfig             
ViewConfig             
</syntaxhighlight>
</syntaxhighlight>
If you want to use any of these API functions without the need of first calling any of the Cider user commands (that would allow the user command script to load the code into <syntaxhighlight lang=apl inline>⎕SE`), then you have to make sure that the code is loaded, ideally at a very early stage: as part of the instantiating process.
 
If you want to use any of these API functions without the need of first calling any of the Cider user commands (that would allow the user command script to load the code into <syntaxhighlight lang=apl inline>⎕SE</syntaxhighlight>), then you have to make sure that the code is loaded, ideally at a very early stage: as part of the instantiating process.
 
This brings us to the real topic of this article: how to achieve that!
This brings us to the real topic of this article: how to achieve that!
== setup.dyalog in MyUCMDs/ ==
== setup.dyalog in MyUCMDs/ ==
We are going to introduce a script </syntaxhighlight>setup.dyalog` into the folder <syntaxhighlight lang=apl inline>MyUCMDs/</syntaxhighlight>. We are doing this because when Dyalog finds such a script then it will check whether it has a function <syntaxhighlight lang=apl inline>Setup`. If so then this function will be executed.
 
We are going to introduce a script </syntaxhighlight>setup.dyalog</syntaxhighlight> into the folder <syntaxhighlight lang=apl inline>MyUCMDs/</syntaxhighlight>. We are doing this because when Dyalog finds such a script then it will check whether it has a function <syntaxhighlight lang=apl inline>Setup</syntaxhighlight>. If so then this function will be executed.
 
Notes:
Notes:
* The name of the script must be lowercase because otherwise it won't be found on non-Windows platforms
* The name of the script must be lowercase because otherwise it won't be found on non-Windows platforms
* The script can be a class or a namespace
* The script can be a class or a namespace
* The function </syntaxhighlight>Setup` must accept a right argument
* The function </syntaxhighlight>Setup</syntaxhighlight> must accept a right argument
   In our case, the right argument will be an <syntaxhighlight lang=apl inline>i</syntaxhighlight> which stands for <syntaxhighlight lang=apl inline>init`
 
* The function </syntaxhighlight>Setup` must return a result
   In our case, the right argument will be an <syntaxhighlight lang=apl inline>i</syntaxhighlight> which stands for <syntaxhighlight lang=apl inline>init</syntaxhighlight>
* The function </syntaxhighlight>Setup</syntaxhighlight> must return a result
   The result will be ignored by the caller
   The result will be ignored by the caller
=== There is no setup.dyalog yet ===
=== There is no setup.dyalog yet ===
Create one that looks like this:
Create one that looks like this:
<syntaxhighlight lang=apl>
<syntaxhighlight lang=apl>
:Namespace SetItUp
:Namespace SetItUp
Line 124: Line 158:
:EndNamespace
:EndNamespace
</syntaxhighlight>
</syntaxhighlight>
Note that this also checks the version of Dyalog APL and whether it's "Classic" or not. Amend this to your needs.
Note that this also checks the version of Dyalog APL and whether it's "Classic" or not. Amend this to your needs.
If your user command is not a Tatin package then this will do.
If your user command is not a Tatin package then this will do.
=== There is already a setup.dyalog ===
=== There is already a setup.dyalog ===
Copy the functions <syntaxhighlight lang=apl inline>IfAtLeastVersion</syntaxhighlight>, <syntaxhighlight lang=apl inline>GetMyUCMDsFolder` and </syntaxhighlight>LoadMyUserCommand` from above into your own <syntaxhighlight lang=apl inline>setup.dyalog</syntaxhighlight> script and then make sure that <syntaxhighlight lang=apl inline>LoadMyUserCommand` is called from your </syntaxhighlight>Setup` function.
 
Copy the functions <syntaxhighlight lang=apl inline>IfAtLeastVersion</syntaxhighlight>, <syntaxhighlight lang=apl inline>GetMyUCMDsFolder</syntaxhighlight> and </syntaxhighlight>LoadMyUserCommand</syntaxhighlight> from above into your own <syntaxhighlight lang=apl inline>setup.dyalog</syntaxhighlight> script and then make sure that <syntaxhighlight lang=apl inline>LoadMyUserCommand</syntaxhighlight> is called from your </syntaxhighlight>Setup</syntaxhighlight> function.
 
This makes sure that the API of your user command is available right after instantiating Dyalog APL.
This makes sure that the API of your user command is available right after instantiating Dyalog APL.
== User commands that are Tatin packages ==
== 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.  
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:
Example:
<syntaxhighlight lang=apl>
<syntaxhighlight lang=apl>
userCommandScript: "APLSource/MyUserCommand,dyalog",
userCommandScript: "APLSource/MyUserCommand,dyalog",
</syntaxhighlight>
</syntaxhighlight>
With this line in the file <syntaxhighlight lang=apl inline>apl-package.json</syntaxhighlight> such a  package can be installed and loaded with Tatin:
With this line in the file <syntaxhighlight lang=apl inline>apl-package.json</syntaxhighlight> such a  package can be installed and loaded with Tatin:
<syntaxhighlight lang=apl>
<syntaxhighlight lang=apl>
       ]Tatin.InstallPackages [tatin]MyUserCommand [MyUCMDs]
       ]Tatin.InstallPackages [tatin]MyUserCommand [MyUCMDs]
       ]Tatin.LoadDependencies [MyUCMDs]MyUserCommand
       ]Tatin.LoadDependencies [MyUCMDs]MyUserCommand
</syntaxhighlight>
</syntaxhighlight>
Notes:
Notes:
* There is no name specified after <syntaxhighlight lang=apl inline>[MyUCMDs]` in the second argument of </syntaxhighlight>InstallPackages`: this makes the function act on the name of the package, here <syntaxhighlight lang=apl inline>MyUserCommand</syntaxhighlight>
 
* <syntaxhighlight lang=apl inline>LoadDependencies` will look for a folder </syntaxhighlight>MyUserCommand` in the <syntaxhighlight lang=apl inline>MyUCMDs/</syntaxhighlight> folder. If there is one, and it contains a file <syntaxhighlight lang=apl inline>apl-dependencies.txt`, then the package will be loaded into </syntaxhighlight>⎕SE`.
* There is no name specified after <syntaxhighlight lang=apl inline>[MyUCMDs]</syntaxhighlight> in the second argument of </syntaxhighlight>InstallPackages</syntaxhighlight>: this makes the function act on the name of the package, here <syntaxhighlight lang=apl inline>MyUserCommand</syntaxhighlight>
* Usually <syntaxhighlight lang=apl inline>LoadDependencies</syntaxhighlight> loads packages into <syntaxhighlight lang=apl inline>#` in case no second argument is specified, but because the folder was specified as an alias (</syntaxhighlight>[MyUCMDs]`) the function knows that this is about a user command, and therefore the default target for the load operation is <syntaxhighlight lang=apl inline>⎕SE</syntaxhighlight> and not <syntaxhighlight lang=apl inline>#`.
* <syntaxhighlight lang=apl inline>LoadDependencies</syntaxhighlight> will look for a folder </syntaxhighlight>MyUserCommand</syntaxhighlight> in the <syntaxhighlight lang=apl inline>MyUCMDs/</syntaxhighlight> folder. If there is one, and it contains a file <syntaxhighlight lang=apl inline>apl-dependencies.txt</syntaxhighlight>, then the package will be loaded into </syntaxhighlight>⎕SE</syntaxhighlight>.
* The user command script </syntaxhighlight>MyUserCommand.dyalog` is moved to the top of the folder hosting the user command, here <syntaxhighlight lang=apl inline>[MyUCMDs]/MyUserCommand</syntaxhighlight>
* Usually <syntaxhighlight lang=apl inline>LoadDependencies</syntaxhighlight> loads packages into <syntaxhighlight lang=apl inline>#</syntaxhighlight> in case no second argument is specified, but because the folder was specified as an alias (</syntaxhighlight>[MyUCMDs]</syntaxhighlight>) the function knows that this is about a user command, and therefore the default target for the load operation is <syntaxhighlight lang=apl inline>⎕SE</syntaxhighlight> and not <syntaxhighlight lang=apl inline>#</syntaxhighlight>.
* The user command script </syntaxhighlight>MyUserCommand.dyalog</syntaxhighlight> is moved to the top of the folder hosting the user command, here <syntaxhighlight lang=apl inline>[MyUCMDs]/MyUserCommand</syntaxhighlight>


=== Loading all such user commands ===
=== Loading all such user commands ===


If you want to make sure that all user commands that are Tatin packages are loaded into <syntaxhighlight lang=apl inline>⎕SE` at an early stage add this to your </syntaxhighlight>setup.dyalog` script and make sure that it is called by your <syntaxhighlight lang=apl inline>Setup</syntaxhighlight> function in that script:
If you want to make sure that all user commands that are Tatin packages are loaded into <syntaxhighlight lang=apl inline>⎕SE</syntaxhighlight> at an early stage add this to your </syntaxhighlight>setup.dyalog</syntaxhighlight> script and make sure that it is called by your <syntaxhighlight lang=apl inline>Setup</syntaxhighlight> function in that script:
 
<syntaxhighlight lang=apl>
<syntaxhighlight lang=apl>
  r←path LoadUserCommandPackages debug;home;name;res;folders;folder;F
  r←path LoadUserCommandPackages debug;home;name;res;folders;folder;F
Line 173: Line 221:
  ⍝Done
  ⍝Done
</syntaxhighlight>
</syntaxhighlight>
Note that it uses stuff from Tatin, so it works only with Tatin being available in <syntaxhighlight lang=apl inline>⎕SE`, but since you are about to load Tatin packages that must be the case anyway.
 
Note that it uses stuff from Tatin, so it works only with Tatin being available in <syntaxhighlight lang=apl inline>⎕SE</syntaxhighlight>, but since you are about to load Tatin packages that must be the case anyway.

Navigation menu