« first day (2191 days earlier)      last day (468 days later) » 

5:25 AM
I was reading through the transcript and saw that ,(⎕UCS 10), was one method for having multiline strings. That was in December 2020 though. Has there been any new ways to have multiline strings added since then?
 
 
2 hours later…
7:27 AM
@lyxal AFAIK, there's no multi-line literal syntax, but it's usually not as bad as manually throwing ⎕UCS 10 (or ⎕UCS 13) everywhere. If you have a vector of lines, then something like ∊(⍪lines),⎕UCS 10 or whatever will flatten it into one big string.
 
 
2 hours later…
9:00 AM
@lyxal You can define the following utility function:
∇ r←mls;n
  n←⎕XSI{1++/∧\⍵∘≡¨(⊃⍴⍵)↑¨⍺}(⍕⎕THIS),'.'
  r←{⍵/⍨'⍝'≠⊃¨⍵}{1↓¨⍵/⍨∧\'⍝'=⊃¨⍵}{⍵{((∨\⍵)∧⌽∨\⌽⍵)/⍺}' '≠⍵}¨(1+n⊃⎕LC)↓↓(180⌶)n⊃⎕XSI
  r←¯1↓∊r,¨⎕UCS 10
∇
And then you can embed literal multi-line strings in functions by preceding each line with a comment symbol, and don't double internal quotes:
∇ foo
  myString←mls
⍝ first line
⍝ 'middlest' line
⍝ last line
⍝ no, one more
  bar baz
∇
Wait, remove the first 1+ from mls. Sorry.
 
9:35 AM
Does (⍕⎕THIS) perform the introspection of the function call?
Because that's pretty neat that the syntax can be completely overriden in a function
 
@lyxal That gets the current namespace as a string.
180⌶ gets the source code. It is like ⎕NR but can break through the defences of classes.
 
Interesting. Thanks for the tradfn!
 
 
1 hour later…
10:47 AM
I've got two sibling namespaces, generated from directories via Link. I'd like to transparently reference variables and functions defined in A from within B, without having to write ##.B.blah.
I tried creating an apln file that sets ##.⎕PATH←'##.B', but that's not doing what I expected.
Clearly my mental model is broken, but I'm not quite sure where.
 
⎕PATH only works for functions, not variables.
@B.Wilson Do you need to track changes to the variables (hopefully not the functions)? If not, then you can maybe copy the members across both ways. Why is it that you have two namespaces instead of one, if they are to give the outwards appearance of being identical?
 
One sibling is the actual application, the other is test code.
 
Wait, so there are differences between them?
 
Yes. The application code doesn't need to know anything about the test code, but the test code wants to reference functions and globals used by the application.
For the sake of argument, say we have A.word←'illustriousness' ⋄ A.calc←{⍵} and we want to write B.test_foo←{'illustriousness'≡calc word} instead of B.test_foo←{'illustriousness'≡##.A.(calc word)} everywhere.
 
11:17 AM
You can make the reference shorter by doing B.A←A and then you can skip ##.
Or you make the test namespace into a class, and :include the application namespace.
@B.Wilson (I occasionally disappear without warning, because I have a newborn at home, so here's a ping to signal you that I came back with an answer.)
 
11:32 AM
@Adám Thank you for the consideration, despite all the baby fun you're having!
Classy-ing up the test suite sounds like an interesting idea.
 
@B.Wilson Note that as with ⎕path variables are not included.
 
11:46 AM
@B.Wilson Excessive memory usage probably doesn't matter so much for the test code, so maybe just copy in the application code when the tests begin: (⍕⎕THIS)⎕NS##.A
 
12:24 PM
@Adám Oooh. ⎕NS has namespace to namespace syntax. Very nice.
 
 
1 hour later…
1:43 PM
That did the trick very nicely.
Have a really nice little test DSL
'Test description' WITH <input>
  field_of_output_of_function_were_testing←'should have this value'
  <etc>
 
APL is really awesome for making small DSLs.
 
Yeah! Started out with something very boilerplate-heavy, and am super pleased it's possible to get it down to literally zero!
Only issue is that my implementation ends up being horridly slow :P
 
We might be able to do something about that.
 
By turning me into a better APLer?
 
Might be a side effect.
 
1:55 PM
One issue I ran into was trying to use non-result functions within a dfns.
In particular, trapping to →0 seems to exit functions without a result.
Is there a good way of handling this without resorting to tradfns?
I guess it's similar to issues that come up using Execute.
 
@B.Wilson I'm not sure I follow. Can you show a code example (feel free to take the "meat" out)?
 
Just f←{... ⋄ _←g ⋄ ...} where g may or may not return a result. E.g. g←{⍎'1+1'/⍨~0∊⍵} or some such.
Here, we want f to keep executing past g, but if g doesn't return a result, there's nothing for to assign and we get an error.
 
g←{⍎'0','⋄1+1'/⍨~0∊⍵}
 
Hah! I should have known you'd point out a retrospectively obvious workaround!
The real case I'm wondering about is something like g←{⎕TRAP←0 'C' '⎕←''ERR'' ⋄ →0' ⋄ _←do_work ⋄ result}.
 
Why are you not using error guards?
 
2:07 PM
Because the trap can is there to capture potential ⎕SIGNALed errors inside do_work.
 
Error guards are dynamic too.
 
Oh! Wait. I was misunderstanding you and thinking of basic guards. Completely forgot about error guards!
 
The only thing you cannot do with them, is pick up local values where the error happened.
 
Oh, like with ⎕TRAP←0 'E' ...?
 
Yes.
 
2:58 PM
Lol. I find myself writing ¯1+var by default, even when var-1 would be just fine. Is this a rite of passage?
 
3:43 PM
⎕←1 ¯1↓'abc' '123'
 
@B.Wilson
RANK ERROR
      ⎕←1 ¯1↓'abc' '123'
          ∧
 
⎕←↓⌿1 ¯1⍪(⊢⍴⍨1,⍴)'abc' '123'
 
@B.Wilson
┌──┬──┐
│bc│12│
└──┴──┘
 
I'm confused why the latter works. My mental model of f⌿ is that it "inserts f in between major cells", i.e if M←↑v1 v2 v3 then f⌿M ←→ v1 f v2 f v3.
 
4:02 PM
@B.Wilson It doesn't in APL. It operates on scalars along the leading axis. For scalar functions, it is equivalent. To reduce major cells, do f⌿⊂⍤¯1
@B.Wilson Yes, welcome to the club.
 
> R is an array formed by applying function f between items of the vectors along the Kth (or implied) axis of Y.
@Adám Okay. So this means that it reduces along the Kth axis but on the corresponding scalars of each item?
That's a really important distinction!
 
Indeed.
Note that other array languages do reduce over the major cells.
 
4:21 PM
       a←(⊢⍴1000?⍤⍴⍨×⌿)50 300 400
       cmpx '+⌿a' '+⌿[1]a' '{⊢⊢⍺+⍵}⌿a' '{⊢⊢⍺+⍵}⌿[1]a'
  +⌿a          → 6.0E¯3 |      0%
* +⌿[1]a       → 6.0E¯3 |      0%
  {⊢⊢⍺+⍵}⌿a    → 1.7E0  | +28291% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
* {⊢⊢⍺+⍵}⌿[1]a → 1.7E0  | +27583% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
 
Why is +⌿[1] different? Are you in ⎕IO←0?
 
Ah, yes.
The correct ⎕IO :P (runs and hides)
 
Maybe, but you just fell for thinking that 1 meant 1st!
 
Was trying to test the middle axis, actually.
 
Then you want +⌿⍤2
 
4:26 PM
Oh, does not take an axis spec?
 
It does, but you cannot meaningfully compare +⌿ and +⌿[1] with ⎕IO←0
      a←?50 300 400⍴⎕IO←0
      cmpx '+⌿⍤2⊢a' '+⌿[1]a'
  +⌿⍤2⊢a → 3.5E¯3 |  0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
  +⌿[1]a → 3.5E¯3 |  0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
 
@Adám Hrm? Isn't the same as ⌿[0] under ⎕IO←0?
Meaning that ⌿[1] with ⎕IO←0 is the same as ⌿[2] with ⎕IO←1, right?
 
Yes it is.
 
Then why can the two not be meaningfully compared?
 
Note that isn't ⎕IO-sensitive, yet another reason it is superior to bracket axis.
 
4:31 PM
Oh. Cannot be meaningfully compared because the results are different?
I was mostly interested in whether middle axis is slower, given the necessary difference in memory access patterns.
 
Oh.
But then you have to have equal length axis, no?
 
Uh... Good point :P
 
      a←?100 100 100⍴⎕IO←0
      cmpx '+⌿⍤1⊢a' '+⌿⍤2⊢a' '+⌿⍤3⊢a'
  +⌿⍤1⊢a → 5.6E¯4 |   0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
* +⌿⍤2⊢a → 4.9E¯4 | -12% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
* +⌿⍤3⊢a → 4.7E¯4 | -16% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
Didn't need to set ⎕IO there ;-)
 
  +⌿⍤1⊢a → 1.0E¯3 |   0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
* +⌿⍤2⊢a → 4.7E¯4 | -55% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
* +⌿⍤3⊢a → 4.3E¯4 | -59% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
Quite a bit different on my machine here!
 
"Same" array?
 
4:36 PM
  +⌿a    → 4.2E¯4 |    0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
* +⌿[1]a → 4.7E¯4 |  +12% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
* +⌿[2]a → 1.0E¯3 | +138% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
@Adám Yes, same as your a←?100 100 100⍴⎕IO←0
 
18.2?
 
  +⌿a          → 5.0E¯4 |      0%
* +⌿[1]a       → 5.0E¯4 |      0%
* +⌿[2]a       → 1.0E¯3 |   +100%
  {⊢⊢⍺+⍵}⌿a    → 2.6E¯1 | +52075% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
* {⊢⊢⍺+⍵}⌿[1]a → 2.6E¯1 | +52775% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
* {⊢⊢⍺+⍵}⌿[2]a → 2.7E¯1 | +53000% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
@Adám Dyalog APL/S-64 Version 18.2.45405
 
Odd.
I'm on Windows.
 
A priori, I'd assume it to be pretty sensitive to SSE intruction availability, cache architecture, etc.
 
Dyalog APL/S-64 Version 18.2.45319
  +⌿⍤1⊢a → 3.0E¯3 |   0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
* +⌿⍤2⊢a → 2.7E¯3 | -10% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
* +⌿⍤3⊢a → 2.5E¯3 | -15% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
 
4:45 PM
Probably not an artifact of the patch version difference, I'd assume.
 
no
Integers make a big difference:
      a←?100 100 100⍴100⊣⎕IO←0
      cmpx '+⌿⍤1⊢a' '+⌿⍤2⊢a' '+⌿⍤3⊢a'
  +⌿⍤1⊢a → 1.9E¯3 |     0% ⎕⎕⎕
* +⌿⍤2⊢a → 2.3E¯2 | +1162% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
* +⌿⍤3⊢a → 2.3E¯2 | +1141% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
 
What's your CPU? I'm on an i7 with 4 cores, hyperthreaded to 8.
Looks like each real core has 48kB L1 dcache.
 
Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz 2.59 GHz (6 cores, 12 logical)
mine can boost to 5GHz iirc
 
Nice. More cores but 2/3 L1 dcache.
 
so, same cache distributed to more cores?
 
4:54 PM
L1 cache on each core, but shared between hyperthreads.
L2 and L3 are unified though.
Oh, you meant total L1, maybe? Since it just happens that per-core dcache on your machine is 2/3 mine, but my core count is 2/3 yours, so the total L1 dache size actually matches.
 

« first day (2191 days earlier)      last day (468 days later) »