1:49 PM
CMC: Implement a operator, that takes first 3 elements and if X [OP] Y then add Z to list, else do nothing. (List is [])
@Fmbalbuena What is "[]"?
So the result is either `⍬` or `,⊂Z`?
@Fmbalbuena What do you mean by "first 3 elements"?
@Fmbalbuena Can you give an example or two of complete calls, assuming the operator is called `OP`?
1:57 PM
@Adám `= F 1 2 3 4 4 5 8 8 1` gives `5 1`
Hm, the first three elements are `1 2 3` then I suppose you're calling those `X`, `Y`, `Z` (correct me if I'm wrong!) but it isn't true that `1=2` so we should return `⍬`, no?
`= F 1 2 3` returns ⍬
Oh, you mean collect the third elements from each sliding window of three, where the two first elements are equal (or whatever fn).
`> F 3 1 4 1 5 9 2 6 5` gives `1⍴4`
@Fmbalbuena `{∊⍺⍺{(⍺⍺/¯1↓⍵)/⊢/⍵}¨3,/⍵}`
2:03 PM
`⋄={∊⍺⍺{(⍺⍺/¯1↓⍵)/⊢/⍵}¨3,/⍵}0 1 0 1 1 0 1 1 1`
@Fmbalbuena `0 1`
1 byte shorter to write `2↑` instead of `¯1↓`
`{∊⍺⍺{(⍺⍺/2↑⍵)/⊢/⍵}¨3,/⍵`
Ok, Is there a tacit operator?
No, the only tacit operators in APL are dyadic operators with a curried right operand. Not relevant here.
`⋄(2∘*1)`
2:07 PM
@Fmbalbuena `2`
Shorter: `{∊⍺⍺{2(⍺⍺/⍤↑⍴↓)⍵}¨3,/⍵}`
Tacit operator example: `⋄ Twice←⍣2 ⋄ 1+Twice 3`
@Adám `5`
`{⍵⌿⍳⍺}`
^ Is there a way to do this in tacit?
`⌿∘⍳⍨`
Explain?
2:11 PM
`X f⍨ Y` is `X f Y` so `⍺⌿∘⍳⍨⍵` is `⍵⌿∘⍳⍺`
`X f∘g Y` is `X f g Y` so `⍵⌿∘⍳⍺` is `⍵⌿⍳⍺`
Understand?
wait, `⍺⍳⍳⍳⍳⍳⍳⍳⍳⍳⍳⍳⍨⍵` gives `⍵⍳⍳⍳⍳⍳⍳⍳⍳⍳⍳⍳⍺`?
No, `⍺⍳⍳⍳⍳⍳⍳⍳⍳⍳⍳⍳⍨⍵` gives `⍺⍳⍳⍳⍳⍳⍳⍳⍳⍳⍳⍵⍳⍵`
`⍨` does 4 separate things.
``` (Z⍨)Y → Z
X(Z⍨)Y → Z
f⍨ Y → Y f Y
X f⍨ Y → Y f X```
Where uppercase are arrays and lowercase is function.
`⍺⌿∘⍳⍨⍵` is `⍵⌿∘⍳⍺` but why?
2:17 PM
`⌿∘⍳` is a single function, so it is the last case in my list of 4.
will operators execute left, and functions execute right?
Yes. (The word is "associate".)
What ∘ will do?
It is a dyadic operator, so it will bind the array or (single) function to its immediate right, and the array or function (phrase) on its left.
E.g. `f∘g∘h` is `(f∘g)∘h`
Similarly, `+/⍣2` is `(+/)⍣2`
In other words, if you want a composite right operand, you need to parenthesise it. You do not need a parenthesis for a composite left operand.
Compare to functions: If you want a composite left argument, you need to parenthesise it. You do not need a parenthesis for a composite right argument.
E.g. `=∘|⍥(2⊃⊢)` and `(2+3)×4+5`
We only need to parenthesise `⍥`'s right operand and `×`'s left argument.
3:03 PM
Announcement: I'm running a webinar in an hour; Computing Check Digits – Fast

2 hours later…
4:35 PM
That was a quite interesting presentation
Tbh when I said "recommendations on how to improve it are very welcome" I mostly meant "how to make it more idiomatic", but a performance improvement showcase was a good surprise
@AndréLeria OK, but changing the picking into multiplication is more idiomatic, imo, and so is generalisation to higher ranks. Idiomatic APL tends to perform well.
Sure, I'm not denying you made it more idiomatic. I'm just stating I didn't expect the huge performance improvement
being able to provide an ordered alphabet to key sure would be nice
@FawnLocke Yes, imo Key should have been a dyadic operator allowing you to give it an alphabet (or a function to compute the alphabet) as right operand. E.g. `f⌸∪` for current `f⌸` or `f⌸{⍵[⍋∪⍵}` for a sorted alphabet or `{≢⍵}⌸'ACGT'` for counting DNA bases.
Agree
4:44 PM
Yep, was just referencing that now for an expression I'm writing
"Historical" issues with APL were what made me look into BQN. But I think I still prefer APL (I don't know either enough, so I will still study both more)
BQN is very sensible, and is a natural improvement over APL from what we've learned is good design. I still use APL because the glyphs are prettier and Dyalog has an excellent ecosystem. HTTP requests, being able to create C bindings, html rendering & plotting, etc, etc. I don't think there's much you couldn't do in Dyalog (if, anything)
I'd risk saying Dyalog is such an improvement it can't even be compared to other APL distributions. I tried using GNU APL before Dyalog, but it's so limited
It just bothers me with Key because it isn't very "historical", as it was only added in 2014.
`⌺` also has issues, but at least it has the design space to be fixed.
4:53 PM
APL's historical issues aren't that bad, I think ⎕IO is the worst contender. It's mostly annoying because there's a lot of them, and through-out the course of a program it's easy to notice them stack up
@Adám Curious how you'd improve stencil.
At least with APL's issues they're directly apparent, compared to other languages where they might be hard to address due to many layers of abstraction
@FawnLocke See an example here.
Thanks.
I'm not sure I'd still do it exactly like that. E.g. an extension to `↑` and `↓` might be better than my switching from padding counts to masks.
@Adám Good work there, some valuable take-homes.
And some nasty typos :-(
5:00 PM
That was unfortunate
Does the interpreter distinguish between +/....× and +.×, or is it the same, in terms of performance?
It does distinguish.
Continuing from my last message, I think this largely explains why so many people are interested in completing their own APL impl. Even beginner to intermediate APLers, such as myself, can find ways to improve or change the notation to benefit themselves. TLDR; APL's lack of abstraction is nice :)
I like a ton of things about APL. My interest in implementing a version if it has quite a few motivations
@Adám is the inner product inherently faster?
5:02 PM
First, I want to implement a programming language for almost 10 years. Even before I learned functional / declarative programming were things. And now, I learn about array programming, which has me amazed once more.
Second, APL seems relatively easy to implement, at least the basics. Of course, making a Dyalog 2 would be daunting, but I'm not that ambicious
@FawnLocke Yeah, while people recognise inconsistencies and bad design in other languages, but it is so much more noticeable in APL because things are so regular… so close to perfect.
Agree
@AndréLeria Yes, the core language is fairly easy to implement. It is the "packaging" that's hard. That's why I'm dreaming of Dyalog creating a switch for a fixed version, so that the same interpreter can execute both old and new code, and the two "languages" can share all the tooling, and can seamlessly call each other.
That'd be interesting
Like an advanced form of `⎕ML`
5:05 PM
In "two languages" you mean... as in Python 2 and Python 3?
No, those cannot easily co-exist.
Like, remove some bad decisions from the past, and not worry about being backwards-incompatible
Yeah. And change things that need changing (like making `⌸` a dyadic operator).
In fact, Dyalog already kind of has two languages, dfns and everything else.
Is there any case in which tradfns are better than dfns?
I see many implementations just skip tradfns
It is a matter of opinion.
Dfns don't (currently — there's nothing syntactically preventing it) allow control structures and keywords.
Tradfns are nicer if you want to manipulate global state (not usually a good idea, though).
5:09 PM
Indeed
I don't know how APL would handle state, however
I know many functional-ish implementations treat state as an argument to functions and just recursively loop through them... or something like that
I'm not an expert to be able to explain it
One thing that sometimes bothers me is that if a function has a large sub-function that manipulates its variables, and the outer function gets called in a loop, then if formulated as dfns, the sub-function must be defined inside the outer function. But this means that the large sub-function gets defined again and again, every time around the loop. With tradfns, the sub-function can be defined just once, outside the outer function.
Hmmmmm
Is that due to APL technically not having first class functions?
No, it is because dfns use lexical scoping while tradfns use dynamic scoping.
@AndréLeria Ah, you can pass a namespace around and manipulate that.
@Adám I couldn't watch the webinar live and I just finished watching the recording. Nice animations, which really help to understand what's going on. Thanks, @Adám.
You're very welcome.
5:13 PM
I think I'm just too deep into front-end reactive programming to be able to think of other ways of handling state
Which is kinda sad, I miss back-end development
How is APL used more often? To create full applications, or as a piece? (for example, to do some specialized computations via April inside a CL application)
@FawnLocke the second contender is glyph overloading between replicate and reduce, IMO.
Definitely one of the more frustrating ones
@AlexB That one we can at least fix by giving replicate (and expand) new glyphs.
My issues with it are mostly ideological though prefixing with ⊢⍤ isn't the end of the world. being able to pass a / to an operator which is later used itself as an operator is :/
@Adám not only because it's regular, but also because it has almost no syntax and no glue between language elements; some of the problems appear more strikingly visible because of APL's terseness. In other languages, you add a bit more glue (another function call) that hides problems under the rug.
5:21 PM
E.g `⎕←/{+⍺⍺⍵}⍳10`
@FawnLocke `55`
@FawnLocke Just because you can, doesn't mean you should!
Indeed
Also: `⎕←⊢⍤/{+⍺⍺⍵}⍳10`
```@Adám (1 trailing line)
SYNTAX ERROR: The function requires a left argument
⎕←⊢⍤/{+⍺⍺ ⍵}⍳10

␄```
5:22 PM
@TryAPL As it should be.
@Adám I always get the impression that tradfn's look like imperative language, whereas dfn's (and even more so tacit) look like functional language. I think there's a beauty to behold in arrays+pure functions.
That's a mistake though. You can write just as functional and pure code using tradfns (as everyone did before dfns).
@Adám What's a mistake? I am talking about the aesthetic impression I get when I look at code.
@FawnLocke whoa
TIL, that's so cool
Tacit is my favourite part about APL, followed by rank and inverses
5:29 PM
@FawnLocke ...and power operator in general, not just inverses.
@AlexB You're probably judging imperative code written using tradfns. Try converting some dfn stuff to tradfns and see how that looks.
Power operator generally isn't too interesting imo
It isn't exactly hard, once you know how the header works.
@FawnLocke not with fixed number of iterations, but used as a repeat...until. It can be more readable (and probably more efficient) than recursion.
5:31 PM
I love the "until" power
Even more now that the "until" makes sense
@Adám yes, of course — but I have a demanding day job and I started looking into APL for the first time only about 3 weeks ago... :-)
`f⍣g` does have have issues though. There's no simple way to prevent it from applying `f` if `g` is already true.
Gosh, I got completely distracted back to that key vocab..
@FawnLocke isn't this an unintended consequence of the / glyph also being used for a function? I tried a few times with other operators whose glyphs are not overloaded and they are not being substituted to ⍺⍺. I get syntax error. @Adám, what do you say?
@AlexB Yes, absolutely. It is what Fawn had in mind too. It is solely because of `/`'s hybrid nature; preferring to be an operator; acting as function only while you force it to.
5:49 PM
Yeah, hence ideological dislike towards it. It goes against the explicit nature of APL.
@Adám maybe we need to indicate operator arguments with ⍺⍺⍺ :-)
(in case we want to create meta-operators)
@Adám this is what a repeat...until typically do in Pascal and other languages. Condition is checked at the end. Those languages also offer while... constructs for this reason, but it is clearly a duplication.
@AlexB Hyperators. NARS2000 has them and uses that syntax.
@Adám ha! I am soooo clever that I even imagined something that is a thing! ha ha ha
But a more general solution is to have a clean way to arrayify functions (and de-arrayify them) so they can be passed around. Dyalog's way is really clunky. BQN's is better — I came up with it :-)
heh
Can you pass functions to trains in BQN?
5:58 PM
@Adám yes please! the fact that functions are not first-class citizens in APL bothers me, because you can have arrays everywhere and functions everywhere, except that you cannot have arrays of functions (which are very useful).
Hmm I don't think that could be done sensibly, unfortunate
You can obtain arrays of function using ⎕OR but like Adám said, it's clunky
I think I've shown before that you can quite easily write covers on top of that which makes it not too ugly.
Yeah
@FawnLocke thanks, I didn't know. I will look into this.
6:07 PM
@FawnLocke thanks. Read it. I'm glad that it's possible but TBH it's really ugly.
@AlexB I used long names, but if you call Arrayify `⍙` and Apply `∆` or something like that, then it isn't too bad.
@Adám I will concede that it would look much better.
I wonder if there's a way to use arrays of functions to aid monadic application in trains eg: [f,g,h,i] Y → (f(g(h(i Y))))
Nicer than f⍤g⍤h⍤i imo
Ehh, actually I don't like this idea
Also, is there a nicer way to remove a leading axis than ⊃↓ ?
6:26 PM
@FawnLocke Huh, how does that work?
Eg, I have a matrix of shape 1 5, when I want a vector
Should've clarified