Conversation started Jan 17, 2018 at 18:30.
Jan 17, 2018 18:30
Welcome to the APL learning session!
We've being going through various useful system functions (special names which begin with ). The next category has tools to deal with user defined functions.
You may want to review Lesson 2.
User defined functions can have various attributes. E.g. they can be niladic/monadic/dyadic/ambivalent, and they of course have an author and a time when they were written. To access this info, we have ⎕AT Attributes.
⎕←⎕AT '⎕SE.Dyalog.Utils.formatText'
@Adám
┌──────┬────────────────────┬─┬──────┐
│1 ¯2 0│2018 1 17 18 33 26 0│0│runner│
└──────┴────────────────────┴─┴──────┘
Let me decode that for you.
⎕SE?
@Pavel ⎕SE is a namespace which is pre-populated with various things. We'll get to that.
The first part, 1 ¯2 0 means that 1: has an result (which is implicitly printed) ¯2: it is ambivalent (the left argument is optional) 0: it is not an operator.
The next part is a timestamp, in ⎕TS form.
The third element is the lock state 0=unlocked (APL allows you to lock your code so others cannot inspect and/or suspend it).
Why is the timestamp right now? I presume that the function was defined at an earlier point, being in the standard library and all.
Jan 17, 2018 18:37
And the last element is the username of whoever last established the function. It says "runner" (TIO's username) because this function was stored in a script and imported dynamically as the TIO session was spun up.
@Pavel ^
Ah
@Adám established? you mean loaded?
@EriktheOutgolfer Made into a function from a text source. It wouldn't update if the function was copied from a different workspace.
Let's have a look at the function. For various practical and/or historical reasons, there are a few different functions that let us inspect code under program control. (A user in an interactive session can of course just use the editor.)
All these system functions have names in the pattern ⎕xR where x is a single letter.
The simplest one is ⎕CR Character/Canonical Representation. It returns a matrix:
⎕←⎕CR '⎕SE.Dyalog.Utils.formatText'
@Adám
 text←{vals}formatText text;cr;pw;right;hang;first;lead;left
    ⍝ Format text according to specifications (see ]?format)
 :If 900⌶⍬ ⋄ vals←0 ⋄ :EndIf
 text←{(+/∨\' '≠⌽⍵)↑¨↓⍵}∘⎕FMT⍣(1=≡text)⊢text ⍝ convert everything to VTV
 text←↑,/(⊂''),(⊂vals)formatPar¨text
@Adám Not related to this specific lesson, but I, for one, think that these lessons are great. I've learned much more APL that I ever thought I would thanks to them. Thank you!
Jan 17, 2018 18:42
From this you can see on the first line that indeed, the function has a result (text) and that the left argument (vals) is optional (it is in braces).
@cairdcoinheringaahing I'm very happy to hear.
However, sometimes it is more practical to get the code as a vector of vectors (list of strings), e.g. to extract a single line. For that we have ⎕NR Nested Representation:
⎕←⊃⎕NR '⎕SE.Dyalog.Utils.formatText' ⍝ first line
@Adám
 text←{vals}formatText text;cr;pw;right;hang;first;lead;left
Finally, you may want a single string (with newlines) with all the decorations. ⎕VR Vector/Visual Representation:
⎕←⎕VR '⎕SE.Dyalog.Utils.formatText'
@Adám
     ∇ text←{vals}formatText text;cr;pw;right;hang;first;lead;left
[1]       ⍝ Format text according to specifications (see ]?format)
[2]    :If 900⌶⍬ ⋄ vals←0 ⋄ :EndIf
[3]    text←{(+/∨\' '≠⌽⍵)↑¨↓⍵}∘⎕FMT⍣(1=≡text)⊢text ⍝ convert everything to VTV
[4]    text←↑,/(⊂''),(⊂vals)formatPar¨text
     ∇
These three forms are all valid arguments to the function ⎕FX Fix which will establish a function according to the code given (or return an index of the first line which was problematic).
⍞←a plus b ⊣ ⎕FX 'r←a plus b' 'r←a+b'
@Adám ERROR: Use ⎕← for a full error report
Jan 17, 2018 18:47
⍞←3 plus 4 ⊣ ⎕FX 'r←a plus b' 'r←a+b'
@Adám 7
Here ⎕FX established the function plus (and returned its name, but we ignored that in favour of 4) and then we used the function right away.
As you may recall, tradfns and dfns can easily define dfns in their code, but they cannot easily define tradfns. ⎕FX lets you dynamically define tradfns should you want to do so.
⎕FX works for dfns too:
⍞←3 plus 4 ⊣ ⎕FX 'plus←{' '⍺+⍵' '}'
@Adám 7
Remember the formatText function? It looks complex. Let's get some order by listing all the identifiers that it uses. Enter ⎕REFS References:
⎕←⎕REFS '⎕SE.Dyalog.Utils.formatText'
@Adám
cr
first
formatPar
formatText
hang
lead
left
pw
right
text
vals
Jan 17, 2018 18:55
In the editor, you can set breakpoints (stops) and trace points (output function name, line number and value).
You can also do this under program control using ⎕STOP and ⎕TRACE, but for obvious reasons I cannot demo this in a non-interactive environment.
The syntax is simple, though. linenumbers ⎕STOP 'fnname' to set, and omit the left argument to query. Same for ⎕TRACE.
Actually, I can demo tracing:
⎕←3 plus 4 ⊣ 2 ⎕TRACE ⎕FX'r←a plus b' 'sum←a' 'sum+←b' 'r←sum'
@Adám
7
Hm, no, it outputs tracing info to STDERR.
So if you look in the Debug pane, you'll see that we passed the trace point in plus on line 2, and the last value evaluated there was 4.
I think we should keep all the OO stuff for a separate lesson (maybe next time?) so I'll skip those for now.
As I'm sure you know, you can explicitly request output using ⎕← or ⍞←.
The chatbot (ab)uses ⍞←. Really ⎕← means print to STDOUT (with trailing newline) and ⍞← means print to STDERR (without trailing newline).
However, you can also use these two symbols for input. ←⍞ means read a line from STDIN, and ←⎕ means get a value from STDIN.
What's the difference?
Note, isn't needed for input
@Zacharý Correct (for input). You can use the result directly.
will take an APL expression and evaluate it. If you give it an expression without a value, it will keep prompting until you give in (or enter to abort).
Expressions evaluated in are not encapsulated, so side-effects will persist (e.g. removing your program!)
@Adám What constitutes "no value"?
Jan 17, 2018 19:06
@Adám just tried inputting )OFF...don't try!
@Zacharý E.g. system commands and {} and result-less function calls.
@EriktheOutgolfer Exact same thing
Ah, I see.
For normal input, you can also set a Response Time Limit in seconds: ⎕RTL←10 gives the user 10 seconds to respond before a TIMEOUT error is thrown.
(obviously you can trap this with a dfns error guard {1006::} or tradfn :Trap 1006)
⎕←⎕RTL←1⋄⍞
@EriktheOutgolfer
1
Jan 17, 2018 19:09
>_>
@EriktheOutgolfer The 1 is the value you set ⎕RTL to.
⎕←⍞⊣⎕RTL←1
@EriktheOutgolfer
why no error >_>_<_<
@Adám trying to achieve TIMEOUT...
Well, input isn't possible with this bot (IIRC)
Jan 17, 2018 19:10
@EriktheOutgolfer Because TIO runs in scripted mode, and when there is no more input, but it needs more input, it terminates.
⎕NQ (Enqueue event) is mostly used for GUI programming, but I want to show you one nifty thing you can use it for.
Exist the pointer concept in APL?There is something as a pointer to the structure describes stdin stdout and stderr? There is something equivalent to C file pointer, or C++ streams? There is End of File EOF value?
@RosLuP When it comes to OO, APL has pointers, or "ref"s. But STDIN/OUT/ERR are just these special variables and . We'll get to file handling.
Woah, I was just fooling around with ⎕NQ, and I just opened a new window o_O
The Calendar and DateTimePicker have two methods (functions) called DateToIDN and IDNToDate. But the root object (#, or the APL session itself) also has these methods.
These convert between the ⎕TS format (Y M D h m s ms) and a International Day Number (as a float, so it includes the time). These are great for date and time calculations.
Two days from now:
⍞←3↑2⎕NQ#'IDNTODate',2+2⎕NQ#'DateToIDN'⎕TS
@Adám 2018 1 19
Jan 17, 2018 19:20
Don't worry much about the syntax. ⎕NQ needs 2 as left argument (for this job) and then the # says to look in the root object. at the end is the timestamp/IDN, either appended (,) or juxtaposed.
You can also use it to get the weekday:
⍞←4⊃2⎕NQ#'IDNTODate',2⎕NQ#'DateToIDN'⎕TS
@Adám 2
2 is Wednesday (because 0 is Monday).
@all Any questions before we move on to file handling?
well, you said let the syntax alone...
@EriktheOutgolfer Yeah, you can look up the docs for ⎕NQ if you want.
Dyalog APL has two sets of file handling system functions. One is intended to make it easy to work with Unicode files, the other gives low level control.
There are lots of options, but I'll just go through basic functionality.
To read the contents of a Unicode file, use ⊃⎕NGET 'filename'. This will normalise line breaks to LF (⎕UCS 10). If you'd rather a list of lines, use ⊃⎕NGET 'filename' 1
This will autodetect encoding an line break style, and should "just work" for almost all files. See docs if you want more fine-grained control.
Similarly, you can put content into a file with (⊂content) ⎕NPUT 'filename'. If you want to overwrite any existing file, use (⊂content) ⎕NPUT 'filename' 1
content may be either a simple character vector (string) or a "VTV" (vector of character vectors, i.e. a list of strings).
Again, more fine-grained control is available.
There are also some nice utilities which make it easy to perform some of the most common file operations. Ah, why not just use ⎕SH/⎕CMD to ask the OS to do it for you? Because various OSs need various commands and syntax. These system functions will let you write truly cross-platform code.
@Adám why would that thought ever cross your mind o_o
Jan 17, 2018 19:34
⎕MKDIR and ⎕NDELETE do what you'd think.
⎕NINFO gives you file listings' info like you'd get from ls/dir, but in a nice array format, perfect for further APL processing.
⎕←⍉↑1 0 6⎕NINFO⍠1⊢'/*'
@Adám
┌─┬───────────┬─┐
│1│/usr       │0│
├─┼───────────┼─┤
│1│/bin       │0│
├─┼───────────┼─┤
│1│/etc       │0│
├─┼───────────┼─┤
│1│/var       │0│
├─┼───────────┼─┤
│1│/boot      │0│
├─┼───────────┼─┤
│1│/srv       │0│
├─┼───────────┼─┤
│1│/dev       │0│
├─┼───────────┼─┤
│1│/opt       │0│
├─┼───────────┼─┤
│1│/sys       │0│
├─┼───────────┼─┤
│1│/lib       │0│
├─┼───────────┼─┤
│2│/.rnd      │1│
├─┼───────────┼─┤
│1│/home      │0│
├─┼───────────┼─┤
│1│/run       │0│
├─┼───────────┼─┤
│1│/sbin      │0│
This is a listing of TIO's root. the first column (indicated by the 1 in the left argument) is the type; 1=directory, 2=file. The second column (0) is the name. The third column (6) is Boolean for whether that item is hidden or not.
⎕←⍉↑1 0 6⎕NINFO
@RosLuP

Rebuilding user command cache... done
⍎SYNTAX ERROR
 __field_initialize_result_←(⎕NS ⍬).⍎'⎕CY''salt''⋄⎕SE.UCMD''←box on -fns=on -trains=tree''⊣enableSALT⋄''''' ⋄ ⎕←⍉↑1 0 6 ⎕NINFO
                                                                                                                ∧

Real time: 0.977 s
User time: 0.921 s
Sys. time: 0.033 s
CPU share: 97.58 %
Exit code: 0
@RosLuP You need to specify a right argument; a filename or pattern.
Jan 17, 2018 19:41
⎕←⍉↑1 0 6⎕NINFO⍠1⊢'*'
@RosLuP
┌─┬───────────────┬─┐
│1│.dyalog        │1│
├─┼───────────────┼─┤
│2│.bin.tio.dyalog│1│
├─┼───────────────┼─┤
│2│.env.tio       │1│
├─┼───────────────┼─┤
│2│.debug.tio     │1│
├─┼───────────────┼─┤
│2│.output.tio    │1│
├─┼───────────────┼─┤
│2│.input.tio     │1│
├─┼───────────────┼─┤
│2│.code.tio      │1│
├─┼───────────────┼─┤
│2│.error.tio     │1│
└─┴───────────────┴─┘
The ⍠1 indicates that the right argument contains wildcards. Otherwise it would look for a file which had actual question marks and/or stars in its name (bad idea, but at least APL can handle it).
⎕←⍉↑1 0 6⎕NINFO⍠ '*'
@RosLuP

Rebuilding user command cache... done
⍎SYNTAX ERROR
 __field_initialize_result_←(⎕NS ⍬).⍎'⎕CY''salt''⋄⎕SE.UCMD''←box on -fns=on -trains=tree''⊣enableSALT⋄''''' ⋄ ⎕←⍉↑1 0 6 ⎕NINFO⍠'*'
                                                                                                                ∧

Real time: 1.228 s
User time: 1.101 s
Sys. time: 0.046 s
CPU share: 93.39 %
Exit code: 0
@RosLuP You need the 1 after , and also to separate the 1 from the filename. Use parens or .
@all Let me know if you want to know about the low-level file handling tools. Otherwise I'll skip them for now.
Jan 17, 2018 19:43
⎕←⍉↑1 0 6⎕NINFO '*'
@RosLuP

Rebuilding user command cache... done
⍎FILE NAME ERROR: Invalid file or directory name ("No such file or directory")
 __field_initialize_result_←(⎕NS ⍬).⍎'⎕CY''salt''⋄⎕SE.UCMD''←box on -fns=on -trains=tree''⊣enableSALT⋄''''' ⋄ ⎕←⍉↑1 0 6 ⎕NINFO'*'
                                                                                                                ∧

Real time: 1.162 s
User time: 1.052 s
Sys. time: 0.053 s
CPU share: 95.10 %
Exit code: 0
@Adám uh, do we have time
@RosLuP This was a correct function call. Since you did not specify the wildcard option (⍠1) it assumed you were looking for info on a specific file. Since that file didn't exist, it threw an error.
@EriktheOutgolfer Not today, no. It would probably require an entire lesson anyway.
Let's do error handling then.
In a dfn, you can trap errors with error guards {errornums::result if error ⋄ try this} and in tradfns with :Trap errornums ⋄ try this ⋄ :Case errornum etc..
Is there a way to catch any error, irregardless of number?
But what are those error numbers? The easiest way to find out is to cause the error and then check ⎕EN which is a variable that you cannot set directly. It contains the error number of the most recent error.
@cairdcoinheringaahing Yes, using number 0 will catch all errors, using number 1000 will catch all exceptions.
⍞←2{0::⎕EN ⋄ ⍺÷⍵}5
Jan 17, 2018 19:49
@Adám 0.4
This catches all errors and returns the error number (or the result of the division if no error happened).
⍞←2{0::⎕EN ⋄ ⍺÷⍵}0
@Adám 11
Error 11 is DOMAIN ERROR (due to division by zero).
⎕EM is a function which takes an error number and gives you the Error Message for that Error Number (⎕EN):
⍞←{0::⎕EM ⎕EN ⋄ ⍺÷⍵}5
@Adám VALUE ERROR
@DyalogAPL since we didn't give a left argument, caused a VALUE ERROR.
⎕DM (Diagnostic Message) is a vector of three character vectors; a canonical form of what you see in the session when an error happens:
⍞←{0::↑⎕DM ⋄ ⍺÷⍵}5
Jan 17, 2018 19:54
@Adám VALUE ERROR
⎕←{0::↑⎕DM ⋄ ⍺÷⍵}5
@Adám
VALUE ERROR
 __field_initialize_result_←(⎕NS ⍬).⍎'⎕CY''salt''⋄⎕SE.UCMD''←box on -fns=on -trains=tree''⊣enableSALT⋄''''' ⋄ ⎕←{0::↑⎕DM ⋄ ⍺÷⍵}5
                                                                                                                         ∧
Ah, right, I forgot that doesn't quite work with the bot.
⎕←9{0::↑⎕DM ⋄ ⍺÷⍵}5
@RosLuP
1.8
Jan 17, 2018 19:56
⎕DMX is a namespace (an object) which has Diagnostic Message (Extended). It has a neat display form with more info:
⎕←2{0::⎕DMX ⋄ ⍺÷⍵}0
@Adám
 EM       DOMAIN ERROR
 Message  Divide by zero
We can use ⎕JSON to display all its content:
⎕←2{0::⎕JSON⍠'Compact'0⊢⎕DMX ⋄ ⍺÷⍵}0
@Adám
{
  "Category": "General",
  "DM": [
    "DOMAIN ERROR",
    " __field_initialize_result_←(⎕NS ⍬).⍎'⎕CY''salt''⋄⎕SE.UCMD''←box on -fns=on -trains=tree''⊣enableSALT⋄''''' ⋄ ⎕←2{0::⎕JSON⍠'Compact' 0⊢⎕DMX ⋄ ⍺÷⍵}0",
    "                                                                                                                                            ∧"
  ],
  "EM": "DOMAIN ERROR",
  "EN": 11,
  "ENX": 1,
  "HelpURL": "http://help.dyalog.com/dmx/16.0/General/1",
  "InternalLocation": [
Here you can even see where in our C source code the error was thrown. It was in arith_su.c on line 652!
Enough for tonight.
 
Conversation ended Jan 17, 2018 at 20:00.