While dictionaries are cool on their own, they were added specifically planning for extra arguments.
Like every time, all the code is available on GitHub.
This post tries something different. Instead of showing code for all the new things I've added, it tries to be some sort of extended release notes with examples and interesting info. An explanation for this choice is given in the last section.
On Infixes
On Infixes is a powerful function that abstracts over operations on windows of an array. Being so powerful, sadly its readability isn't perfect. I'm open to discussing a simpler interface that doesn't trade away any of the power.
In the simplest case, On Infixes just applies a function to windows of a certain size of the major cells of an array:
3⊂↡⍳5
┌───────┬───────┬───────┐
│┌─┬─┬─┐│┌─┬─┬─┐│┌─┬─┬─┐│
││0│1│2│││1│2│3│││2│3│4││
│└─┴─┴─┘│└─┴─┴─┘│└─┴─┴─┘│
└───────┴───────┴───────┘
It can also window over multiple axes:
(∧◡2‿2)⊂↡4‿4⍴…16
┌───────┬───────┬───────┐
│┌──┬──┐│┌──┬──┐│┌──┬──┐│
││ 1│ 2│││ 2│ 3│││ 3│ 4││
│├──┼──┤│├──┼──┤│├──┼──┤│
││ 5│ 6│││ 6│ 7│││ 7│ 8││
│└──┴──┘│└──┴──┘│└──┴──┘│
├───────┼───────┼───────┤
│┌──┬──┐│┌──┬──┐│┌──┬──┐│
││ 5│ 6│││ 6│ 7│││ 7│ 8││
│├──┼──┤│├──┼──┤│├──┼──┤│
││ 9│10│││10│11│││11│12││
│└──┴──┘│└──┴──┘│└──┴──┘│
├───────┼───────┼───────┤
│┌──┬──┐│┌──┬──┐│┌──┬──┐│
││ 9│10│││10│11│││11│12││
│├──┼──┤│├──┼──┤│├──┼──┤│
││13│14│││14│15│││15│16││
│└──┴──┘│└──┴──┘│└──┴──┘│
└───────┴───────┴───────┘
It can make chunks:
3‿3⊂↡⍳10
┌───────┬───────┬───────┐
│┌─┬─┬─┐│┌─┬─┬─┐│┌─┬─┬─┐│
││0│1│2│││3│4│5│││6│7│8││
│└─┴─┴─┘│└─┴─┴─┘│└─┴─┴─┘│
└───────┴───────┴───────┘
with many options for end behavior:
3‿3‿3‿⟨0⋄2⟩⊂↡⍳10
┌───────┬───────┬───────┬───────┐
│┌─┬─┬─┐│┌─┬─┬─┐│┌─┬─┬─┐│┌─┬─┬─┐│
││0│1│2│││3│4│5│││6│7│8│││9│2│2││
│└─┴─┴─┘│└─┴─┴─┘│└─┴─┴─┘│└─┴─┴─┘│
└───────┴───────┴───────┴───────┘
3‿3‿3‿3⊂↡⍳10
┌───────┬───────┬───────┬───────┐
│┌─┬─┬─┐│┌─┬─┬─┐│┌─┬─┬─┐│┌─┬─┬─┐│
││0│1│2│││3│4│5│││6│7│8│││9│8│7││
│└─┴─┴─┘│└─┴─┴─┘│└─┴─┴─┘│└─┴─┴─┘│
└───────┴───────┴───────┴───────┘
3‿3‿¯3‿1⊂↡⍳10
┌───────┬───────┬───────┬───────┐
│┌─┬─┬─┐│┌─┬─┬─┐│┌─┬─┬─┐│┌─┬─┬─┐│
││0│0│0│││1│2│3│││4│5│6│││7│8│9││
│└─┴─┴─┘│└─┴─┴─┘│└─┴─┴─┘│└─┴─┴─┘│
└───────┴───────┴───────┴───────┘
and lots of other possibilities. For example, I use On Infixes for implementing Find; it's short enough it can be an inline code block: {⍵≡∘s↡⍨⍪∘1‿6◡⍴s←⍺ϼ⍨ϼ⍵}
. The one difference with the actual primitive is that it doesn't check that the ranks of the two arguments are compatible, instead potentially demoting the needle to fit the haystack rank.
Find and Mask
Find and Mask are used to search for subarrays in a larger array. Note that Mask now uses the ⋷
glyph; ⋵
is now used for Count.
4‿4⍴⍳3
┌─┬─┬─┬─┐
│0│1│2│0│
├─┼─┼─┼─┤
│1│2│0│1│
├─┼─┼─┼─┤
│2│0│1│2│
├─┼─┼─┼─┤
│0│1│2│0│
└─┴─┴─┴─┘
1‿2⍷4‿4⍴⍳3
┌─┬─┬─┬─┐
│0│1│0│0│
├─┼─┼─┼─┤
│1│0│0│0│
├─┼─┼─┼─┤
│0│0│1│0│
├─┼─┼─┼─┤
│0│1│0│0│
└─┴─┴─┴─┘
1‿2⋷4‿4⍴⍳3
┌─┬─┬─┬─┐
│0│1│1│0│
├─┼─┼─┼─┤
│2│2│0│0│
├─┼─┼─┼─┤
│0│0│3│3│
├─┼─┼─┼─┤
│0│4│4│0│
└─┴─┴─┴─┘
As you can see, Find marks the beginning of each match and Mask assigns a number to each match. Find returns overlapping matches and Mask doesn't.
Histogram
Histogram is in some sense the inverse to Where. It lives on the same glyph as Count because it's a specialization of it that has a default left argument of a range based on the maximum of the argument.
⋵0‿2
1‿0‿1
⋵⍸0‿0‿1‿0‿1‿0
0‿0‿1‿0‿1
Compact Trains
The train syntax in TinyAPL is very powerful; however sometimes it's way too verbose. To implement probabilistic nor, currently you have to write ⦅1⋄-⋄+⋄-⋄×⦆
, which is quite annoying. For this reason I added compact trains, where in simple cases you can remove diamonds inside train syntax: ⦅1-+-×⦆
. Much more manageable! This works by binding specially inside train brackets without diamonds disabling function application (but leaving modifier application). When you want the empty-tine-forces-atop behavior from normal train syntax, you can use the ·
token. Like BQN!
Extra Arguments
This section is the big part of the update. There's a lot to explain, so maybe it's best to just link the documentation. In short, you can now do this:
1‿3‿2‿4‿2⍳2‿2.1‿10 ⍝ normal
2‿5‿5
1‿3‿2‿4‿2⍳⬚∞2‿2.1‿10 ⍝ set result for not found
2‿∞‿∞
1‿3‿2‿4‿2⍳⑴2‿2.1‿10 ⍝ set index origin to 1
3‿6‿6
1‿3‿2‿4‿2⍳≈¯0.5 2‿2.1‿10 ⍝ set tolerance to absolute ½
2‿2‿5
1‿3‿2‿4‿2⍳⤺2‿2.1‿10 ⍝ last index
4‿5‿5
1‿3‿2‿4‿2⍳⬚∞⑴≈¯0.5⤺2‿2.1‿10 ⍝ all together now!
5‿5‿∞
I think you will find this system much nicer to work with than semi-scoped variables like in Dyalog. It also adds a lot more flexibility because your dfns can define whatever extra argument they want! Inside {}
the special name ɛ
refers to all the extra arguments as a dictionary.
Moving reductions
Reduce/Fold has been moved from ⍆
to /
and Reduce Back/Fold Back has been moved from ⍅
to \
. The idea is to eventually drop \
in favor of /⤺
.
Rotating Transpose and Orient
Monadic Transpose now rotates axes instead of reversing them. Transpose Backward does monadically a rotation in the other direction and dyadically the "orient" operation, which grades the left argument before transposing. Negative left arguments are supported and subtract from the rank of the array. Incomplete left arguments are also supported and are filled with the axes not mentioned, in increasing order.
⍴⍉⍳2‿3‿4
3‿4‿2
⍴⍉⤺⍳2‿3‿4
4‿2‿3
⍴¯2⍉⍳2‿3‿4
3‿2‿4
⍴¯2⍉⤺⍳2‿3‿4
3‿2‿4
What's with the change in style?
Originally, I wrote these blog posts for myself. When I started working on TinyAPL, I didn't know Haskell nor APL as much as I do now. The goal was to reason more about the code I wrote by having to talk about it. However, pretty quickly I started to skip writing the posts while writing code and instead I switched to writing the whole post at the end. While that has helped find a couple of last-minute bugs, in general it was clear to me that posts had ended their usefulness. This led to me losing motivation to write them in the first place, and more often than not the last posts contain very little text around code, often just saying stuff like "this primitive is pretty simple to implement". Talking with a few people led me to take this decision to abandon the code posts. I don't need them anymore, and I'm not sure anyone else gained anything from them either. I'll still occasionally include code snippets when there's interesting implementation details to discuss.
If you're interested in understanding how the code works, and you think you'll miss the code posts, contact me! I'm happy to help you understand my code. Right now the easiest place to reach out to me is the APL Farm, I'm @RubenVerg
on the Discord side and @rubenverg:matrix.org
on the Matrix side. You can also ping me in the APL Orchard as @RubenVerg
. Alternatively you can ping me on Mastodon, I'm @RubenVerg@functional.cafe
.