Transcript

Many thanks to Adám Brudzewsky for producing this transcription
[ ] reference numbers refer to Show Notes

00:00:01 [AB]

I'll just say to the listeners: Don't give up. It might seem really complex, but once these concepts click — you'll know it!

00:00:12 [CH]

I mean, if they've made it to the end of this hour-and-a-half episode, hopefully… I mean we probably should have said that at the beginning. Maybe that's what you can do; insert that clip at the beginning, Bob.

00:00:24 [CH]

Welcome to another episode of Array Cast. I'm your host Conor, and today we have 3 panelists; 2 panelists, and one subject matter expert> And I'll have them go around and introduce themselves. We'll start with Adám, then go to Marshall and then go to Bob.

00:00:47 [AB]

I'm Adám Brudzewsky. Full-time APL programmer at Dyalog Ltd.

00:00:52 [ML]

I'm Marshall Lochbaum. Today I'm known for creating BQN. I started as a J programmer and worked at Dyalog for a while before that. Been through a few languages.

00:01:01 [BT]

And I'm Bob Therriault. And I'm a J enthusiast. I'm expecting to learn a tremendous amount because I did a little homework last night and I've realized there's a whole lot of things I don't know about the topic today and I'm going to find out a lot, I'm sure. Steven Taylor, who's our regular panelist, is not here because he's celebrating the 92nd birthday of his mom. And his mom is Bell McDonald. So happy birthday Bell McDonald!

00:01:26 [CH]

Happy birthday. Little bit sad that she took priority over us. Don't really understand that, but it is what it is. I will, I'll give him a pass this one time. And yes, as mentioned before, my name is Conor. I'm not an APL, K, BQN, J or any other array language developer, but I'm a huge enthusiast of the languages and combinators. I code in C++ day-to-day.

00:01:48 [CH]

And I think we have — before we jump into our topic today — I think we have 3 announcements, one from each of the individuals. So, we'll go to Adám first, then to Marshall and then to Bob, right?

00:01:57 [AB]

So, after over 55 years, APL finally has a logo. it took like about half a year for the APL community to establish a logo that the people were found acceptable.[01] And now that it's available, it's time to go and show your APL pride. So, we'll leave in the show notes links to a store where you can get merch of various sorts.[02]

00:02:24 [CH]

Do we actually have merch?

00:02:25 [AB]

Of course we have.

00:02:26 [CH]

Really‽

00:02:26 [CH]

Yes!

00:02:29 [BT]

There is merch, yeah!

00:02:30 [CH]

Since when?

00:02:31 [BT]

There's mugs…

00:02:32 [AB]

Since last week.

00:02:34 [CH]

Really?

00:02:35 [BT]

Yeah.

00:02:35 [AB]

Yeah.

00:02:35 [CH]

Well, I'll have to go get some merch. I thought we were just joking, because the last time we mentioned merch. It was Aaron Hsu site 'cause we didn't actually have merch, but we actually… It's not merch for us, it's just the APL merch.

00:02:46 [BT]

It's APL.

00:02:48 [CH]

Alright, not Array Cast merch.

00:02:51 [BT]

Not Array Cast merch.

00:02:50 [CH]

But the second-best thing, I guess. All right. Over to Marshall.

00:02:54 [ML]

All right, so after two years now, BQN is getting something that a lot of people have asked for, which is a Foreign Function Interface which you can use to call native functions like you would write in C or maybe, these days, Zig or Rust or whatever. So, we're not… We're still kind of polishing off bugs with this, but I expect by the time that this episode comes out, we'll have released our foreign function interface. So, if you wanted to use BQN, but were stuck on interfacing with other languages, that's a that's a big step.

00:03:26 [CH]

Awesome, that sounds super cool. And Bob?

00:03:29 [BT]

And we had a listener email us and mentioned that there was a change to KX's terms of service in terms of the free… the use of the free commercial license. [04] And so, we reached out to KX to ask them — well, for one thing, we asked them if they'd like to be on a future episode — but also, we asked them if they had any statement, and they provided this statement and the statement I will read right now:

00:03:54 [BT]

In early May KX implemented changes to our free non-commercial licensing. This was in direct response to a significant breach of our terms of service.

We continue to support our valued developers with axes to free 64bit on-demand tooling, curriculum, and community platforms.

Over the coming days we will launch the new method to obtain a license but for now kindly ask that you reach out to trial@kx.com with any requests.

00:04:22 [BT]

And of course, we'll put that link into the show notes [05], and that was their response to the questions about their change in terms of service and that's… So, that's their announcement.

00:04:32 [CH]

Awesome, yeah, so probably, at minimum, when Stephen's back on in a future episode, we'll discuss it a little bit. Maybe they'll be a whole episode if KX wants to bring someone on. Or maybe they send Stephen. "Send" sounds weird, seeing as he's here usually, all the time. But yeah, stay tuned for updates in a future episode, as we'll probably be mentioning this at least again in the future.

00:04:55 [CH]

All right, so that means, today we're going to be hopping into sort of a roundtable discussion. I will be playing the role of a noob — for lack of a better word — someone, you know, beginner, on what I think is one of the more complicated topics in the array language paradigm and that is sort of the idea cloud of Rank Leading axis theory and, you know, adjacent topics.[06]

00:05:23 [CH]

I guess I'll start by saying that this is probably one of the things that I understand least well, especially when it comes to J. Although compared to APL and BQN, probably I know the least about J. But there's been many times where I've been trying to do something in J, and I think the idiomatic way is to use the rank operator, which is the double quote.

00:05:47 [CH]

And then I'll end up like failing to do it, and I guess we should just start by someone explaining what Rank is, maybe what the Rank operator is, how it exists in each of APL, BQN, and J — are there differences? And then from there maybe we can talk about leading axes theory; the development of that, how that sort of exists in the different array languages, because I know that there are differences between the two. At the top of the show notes, will have a link to the APL Wiki article on leading axis theory, which has a couple different sections. The one most interesting one, that I came across, is called the Adoption in APL [07] that shows that like certain glyphs, you know, they operate on different axes and when this whole leading axis theory started to develop, you know, certain glyphs, you could basically like… were compatible with this leading axes theory, but others weren't. Et cetera, et cetera. And across the APLs, you know, there's this nice little sort of green and red diagram.

00:06:48 [CH]

Enough said from me. Who wants to start by trying to explain what rank is, what the importance of it is, and how it ties into leading axes theory?

00:06:57 [ML]

Well, I'll start by saying for the rank operator the the shocking answer for the differences between J, APL and BQN is, there are pretty much none. It's, you know, probably one of the very few things where this is the case. Can't think of any other things.

00:07:17 [ML]

There's maybe, like, you know, the reverse function, but even then, APL has the two reverse functions and only one of them is the same as BQN in J's. I think Adám is probably the best person to explain, kind-of how you use the rank operator, most of the time.

00:07:33 [AB]

OK, I guess I can. I can try, but I would say that it has to be clear what exactly axis mean and and leading axis and defining a function in terms of leading axis first. Otherwise, the rank operator really doesn't make any sense. So, in — well, I don't know, what should we call them — "proper APLs", then data is stored in multidimensional arrays.

00:08:06 [AB]

And that means that they… you could define them in a — we call that — "ravel order"; just the order that they appear in, but then they're arranged along zero or more axis. There are slight differences in the array model between APL, J, and BQN, [08] but for our purpose here, that doesn't really make it a difference.

00:08:29 [ML]

Well, an array of numbers is the same in any of them.

00:08:32 [AB]

Yes.

00:08:33 [ML]

That basic concept is going to still work.

00:08:35 [AB]

Yes, and so the axes are ordered. You have a leading axis and then you have the next axis, next axis, until you run out of axes. So, for example, for a table or matrix, the leading axis, in all these languages, is considered the one that runs along the rows.

00:08:53 [AB]

So, then we talk about "major cells", which are… it's kind of like elements, but it's kind of like subarrays where you drill into an array. So, a matrix is conceptually made of vectors, it's collection of vectors of equal length. And so that traversing over these vectors where you look at the first vector and look at the second vector, which are then the rows in the in the matrix, that would be the leading axis. And then traversing over elements of each individual vector is, in this case, the trailing axis — there are only two of them — so, 1st axis and 2nd axis.

00:09:31 [AB]

And so the important thing is, that the more of a kind of a global scope that a function have, meaning it when you feed it an array as argument, it applies to the whole array, the more you can, in a sense, force the perspective of the function to look at subparts of the array.

00:09:55 [AB]

If the function only by itself naturally looks at smaller pieces of the array, there's nothing you can do to make it look at greater parts of the array. Even if you were to try then, it would still drill in and look at small parts. So, this might sound very theoretical. Let's make it very practical: Let's say, in all these languages, we have two matrices, and we add them together with a +. It's identical in all these languages, so matrix a, matrix b, and we do a+b. [09]

00:10:24 [AB]

Now, + is defined as in terms of nitty-bitty detail. So, it drills into these arrays until it hits atomic numbers and then it adds up corresponding numbers in in the two matrices. And there's nothing you can do to make plus have, so to say, a larger perspective. It will just look at individual numbers and add them up.

00:10:49 [AB]

A function that doesn't behave like this, would be the match or equivalent function (≡).[10] It looks at… It takes 2 arrays and then it looks whether or not they are entirely identical. So, it never looks… Well, it obviously has to look at the individual elements of the array, but it never considers them separate from each other. It only looks at them as part of the whole array.

00:11:11 [AB]

And here you could, conceptually, narrow down the scope of the match function, so that instead of looking at the whole array, let's say we talk about matrices, we could tell it to look at individual rows. So, if we have 3 rows in both matrices, then we can say, "compare row wise". So that's a narrowing down of the perspective of the match function.

00:11:39 [AB]

And that is where the rank operator comes in. It's a way to force any particular function to never see arrays of higher rank than a certain limit that we give it. Or, in other words, it's a way to make it, conceptually, loop over an array and process it piece by piece. So that is what the rank operator is all about.

00:12:05 [BT]

And there is one other thing with the rank operator, or not rank operator, but ranks, that you should know: We're actually talking about two different ranks. One is the rank of the object we're working on. And the other is the rank that's inherent in the verb or the function that we're working with, and the idea of the rank operator is to match one to the other. [11]

00:12:26 [BT]

So, by changing the rank operator you can match your verb or your function to the array that you're working on, so you can axes parts of the array. If you didn't have a rank operator, you're going to be relying on the function's rank alone, and it's always going to work the same way on the array and do the size of the array, or the number of dimensions of the array will then determine… Then if you didn't have a rank operator, you would only be working maybe with leading axis, and you'd have to change the number of dimensions, in order to work with a different axis — or transpose the matrix; move it around so your leading axis is something else.

00:13:06 [BT]

The rank operator allows you to zoom in, as Adám said, into part of the array, and say, "I only want to look, in the in the object, I only want to look at rows. I'm not concerned if there's 40 rows or 50 rows or one row. I'm only looking at rows and I'm comparing them to whatever I've got on the other side of my function, if I've got a dyadic function. So, that's what the rank operator did.

00:13:32 [BT]

I think Henry Rich said something about it being like an impetus match in electronics. If you don't match them up, you know I'm sure going to be sure about what you get and a lot of times it's just not going to work, so that's where, a lot of times, you see a length error or something like that, in J, that indicates that you actually haven't matched things up. You're trying to match two of one thing to three of another. The two of one thing might be rows and the three of others might be blocks, and you've just matched them wrong.

If you match them row-to-row, you might find they all match up again and that's what the rank operator allows you to do.

00:14:04 [ML]

Yeah, so there was some, I think, J-specific phrasing there. One thing that J has that that APL and BQN don't, is the idea that a function has this built-in rank that it uses. And so, in J you'll have, like the Raze function that combines 2 lists or that combines a list of lists together into one big list, that's given by J a rank of 1, so if you apply it to an array… No, that one…

00:14:41 [AB]

Maybe a better example, I think, would be it seems kind of extreme, but there's the Do verb in in J (".) [12] and it's very easy to understand. Right, so the Do verb is just an Eval; it just evaluates a vector as a line of code as J. But the important thing is of course, since J is a 1-dimensional programming language, then it will never. You never want to consider anything that has higher dimensionality than 1. And in J, then, since it's defined as having rank 1 if you give it a higher rank array it will just go in and split it up into little pieces that are all rank one. So, let's say if you have a 3 dimensional array, then it will consider it like a two-by-two… Sorry a 2-dimensional array of one dimensional arrays and then evaluate all of those as lines of J and collect the result together. Whereas, say in APL it would just error saying you cannot apply this function to a multidimensional array; it just isn't defined like that. You'd have to do it manually.

00:15:42 [ML]

Yeah, well, so I mean, there's… APL and BQN could define their evaluation functions to do something like this, but the difference in J is that each function has this concept of what is its rank, which other parts of the language can see. There's the normal atop operator written with an @ [13] looks at the function rank of its right operand, and the one with the colon (@:)[14] doesn't.

00:16:12 [ML]

So, in J, you have this built in function rank and you might even have a function that does the same thing. Like if you wrote execute rank 2 (". " 2) then J would say, "well this is a function with the rank of two", even though logically it does still have rank one, the J engine no longer knows about that.

00:16:31 [ML]

So, well, that's actually not a difference in the rank operator at all, but that's a difference in how rank is treated. So, J builds rank deeper into the language itself.

00:16:45 [AB]

But we can give an example of… very simple example of using then rank in, say, J where, even though the rank operator, sorry, the do or evaluate verb is defined as having rank one, so it will only ever apply to lines of code, you could use the rank operator to apply it to rank 0. That's individual elements. So, let's say you had a character array of digits, and you evaluate it each… then you would evaluate each digit in this character array and they would become all numeric instead. So, this is an example of narrowing down the scope, so the evaluate function will never see entire lines, it'll only ever see one character at a time and then evaluate those.

00:17:27 [ML]

Yeah, so I would still ask, "What are the rank-one cells?", like once the rank-Zero is applied and it maps down to a character, it asks, "what are the rank-one cells of my argument?" But then the argument is a single character, and it can't go down any further, so there's still that layer there, it's just not doing it.

00:17:47 [CH]

Alright, so this has been great. I've learned a couple things already which I'll summarize. So, one, and this was — I was going to ask the question that Marshall immediately answered before asking it, so thank you, Marshall — is that this is something that's always confused me about J, and I didn't realize it was different between APL and BQN, although maybe implicitly I knew that J is different in this regard, it has this function rank, and it's actually exactly what you talked about, the @ and @:, which I think you said are the Atop — I can never remember the combinators in J.

00:18:24 [ML]

Well, I think pretty much everybody agrees that those two are the wrong way around. Mostly, the colons are on the wrong one that should be the full rank version, and the ones without the colon should be the "close version" — is what it's called historically.

00:18:40 [CH]

Interesting, so there's even some agreement that there's a mix-up, but even what you just said right there, is that the fact that there are two different versions of these — and like, I absolutely love combinators, [15] I study them, like, in my sleep 'cause I love them so much — and like in between BQN and APL, like, I understand them like very, very well. The only time I ever get mixed up is the fact that BQN removed the double dot (¨) and so now those are two like opposites of each other. [∘ in BQN vs ⍤ in APL — Ed.]

00:19:12 [CH]

'cause the yeah the B1 Combinator Atop (∘) doesn't have the double dots, and anyway so… That doesn't really matter. Listener, you can ignore that! But the point being, is that J has this difference and it has always confused me, especially that one diagram, which we can find the link for, that shows a visualization of the sort of composition patterns of the different ones, [16] and there's a bunch of, like, arrows, like, I've never understood that diagram, even though I have to admit, I haven't tried that hard.

00:19:40 [CH]

But the point here being is that J is slightly different and so maybe we can come back to this in a bit, but I think we should circle back to the start of this discussion and just talking about rank and sort of the wording that Adám used is narrowing down the scope as I will sort of restate a couple different examples and then ask a question after this just because probably all our advanced array languagers that are listening, they've understood everything so far. Maybe they learned a couple things like me, but there might be a couple beginners that are just totally like, "what are folks talking about here‽", like narrowing down the scope, we've got different functions that operate differently, so the example that Adám used was the + which I think is kind of a… I mean, that's a scalar operation and I put like scalar operations in a different category, [17] which I also think… that'll be sort of my follow up question is, "what are the different categories of functions or glyphs in these languages?"

00:20:36 [ML]

Well, I have a quick answer to give to that. Well, so the only categories that matter for rank are really the scalars and everything else. But what actually… The direction I think Iverson was going when he introduced this rank idea was that, well scalars, of course, have rank zero. They only work on, you know, an atom or a unit or whatever at a time.

00:21:05 [ML]

So, Iverson wanted to, instead of having just two categories, have instead the rank of a function as a more general attribute that allows you to say what a function is. So, I mean, now you can say that there are infinity categories, there's rank 0, there's rank 1, there's rank 2. And for dyads, there's rank 0 0 and 0 1, and so on; you can have one rank for either argument. So, there are only two categories, but rank kind of generalizes that idea so that you can say something about the functions that aren't scalar functions.

00:21:44 [CH]

OK, so I'll have a follow-up question. Maybe I'll just ask the follow up question. So, and just to be clear, for once again the beginner that's listening when we refer to scalar operations we're referring to like the classically, like all the mathematical operations, plus minus multiplies divides. There's a bunch more, but like there's this category. We have operations that, at least from what I understand in APL, if you can add the two atoms 1+2, you can add a scalar, a 1, to any rank array, whether that's a matrix, a list, you know, N-rank matrix, and then it'll just add that scalar to every individual atom and then you can also… it works polymorphically if you have two arrays with the same rank and shape, then they'll just add them together.

00:22:30 [CH]

But I think after that APL stops working. Because you can't add a row like, you can't add a rank 1 array to a rank 2 array. You can't add, you know a vector to a matrix, although hypothetically speaking you could, right? You could just on a row-wise basis, add everything together, and I think that's where rank comes in. If you can use rank on scalar operations, but…

00:22:52 [ML]

Yeah, and this is going to tie into actually the one small difference that APL has versus J and BQN, which APL could still extend to get up to the functionality that J and BQN have. But right now, it doesn't do this leading axis agreement. Or actually, does the… the rank operator does it, doesn't it?

00:23:14 [AB]

No no no.

00:23:15 [ML]

No, it doesn't.

00:23:17 [ML]

It just has a scalar agreement for the rank operator. So, yes, in J and BQN and you can add a list to matrix or anything like that and that's the one difference there is still in…

00:23:33 [CH]

And can you do that in APL? I don't, I don't think… Or is there a way to do that? Yeah, so like.

00:23:36 [ML]

No, you…

00:23:38 [AB]

Well, you just… you have to use the rank operator.

00:23:40 [CH]

So, if you go 1 2 3 plus rank, insert one or whatever rank you need to, and then to a matrix. (1 2 3(+⍤1)matrix)

00:23:48 [AB]

Yeah.

00:23:48 [ML]

Negative one would be the rank that you want to do the same thing.

00:23:51 [AB]

It doesn't actually matter in this case, but yeah.

00:23:53 [CH]

OK, so negative one.

00:23:55 [ML]

No rank one would be the… well… it depends on what you want to do.

00:23:57 [AB]

It depends what you want to do, yeah.

00:24:00 [CH]

If you want to add it on a row-wise basis.

00:23:59 [ML]

So well, should we go to… Are we ready to go to leading axis ideas, yet? 'cause…

00:24:06 [BT]

Yeah, I think… I was going to say, I think leading axis, you sort of touched on it when you talked about scalars being added to matrices, because at least in J, scalars have an empty shape, and that means they'll match anything for their preface, which allows them to be added to like a, you know ,a matrix or two-by-two matrix because they match everything.

00:24:31 [ML]

Yeah, but the same would be true if you had a trailing axis agreement, so it's when you're adding a list to a matrix that you first get, in arithmetic at least, these leading axes ideas showing up.

00:24:48 [BT]

Yes, but yeah, you're right, it shows up as something that you can see, but the reason it works for scalars is because you don't see it, but it matches everything. And you're right if you had, say, a list that has a leading dimension of three, and you know, so it's a list. It's just a single, you know, 3 numbers and you're adding it to a two-by-three matrix. In that case, if you just try and add them, and you know, you try and add it, you're adding three [elements — Ed.] to two [rows — Ed.], right? But if you make it rank one, and you look at lists on both sides, now they match up. And the reason they match up… Yeah, go ahead, Marshall.

00:25:35 [ML]

Yeah, and so this is where our array programming will trip a lot of people up because definitely, NumPy, and I think Julia as well,[18] will match the trailing axis. Instead, they'll say, "if you want to match this list to this matrix of lists, well yeah, I'm going to match the rows up." And so, APL goes in the other direction with respect to the axes, but there's a good reason for that, so I'm sure we'll get to it.

00:26:06 [CH]

Let's… So, the question is, do we want to start talking about leading axis theory? I'd say let's come back to that, because I still think that I have a question for Marshall and everyone, but it was something the question came from something Marshall said, which was these categories of functions, and you sort of grouped them into scalars [i.e. scalar functions — Ed.] and then everything else. Yeah, and so we talked about scalars and how, in APL, for the plus operation and other scalar operations that's going to behave differently than BQN and J, and in order to get that behavior where you're trying to add a single vector to matrix, you're going to have to use the rank operator, which I just opened up a RIDE editor and sure enough, got it to work where I added 1 2 3 to a matrix of 1 2 3s, and had to add some parentheses so that, you know, the 1s didn't collide with the shape of the matrix, which is irritating, but it is what it is and… [19]

00:27:01 [ML]

It's not in BQN.

00:27:04 [CH]

Yes, that's right because of stranding. But so, "everything else" is that really the case? Because on the leading axis theory page of the APL Wiki… And this is actually, you know, I stumbled across this discovery or whatever you want to call it in the last couple months, is that I didn't really ever observe that there seems to be more categories in this "everything else" because a lot of the, what I think of as like bread-and-butter operations in APL, because they're the ones that I first stumbled across, are reduce, scan and reverse and rotate because I love rotate. It's got a lot of, sort of, jokes and memes in the C++ community of that's a rotate, and so when I discovered there was a rotate in APL, I fell in love with it. And all four of those operations reduced, scan, reverse and rotate, are all… Like, I referred to them in my head as axis-last operations, but it sounds like we just referred to it as trailing axis operations. I'm not sure if there's an actual word, but on the leading axis theory page it has this table and it refers to, like in one of the rows of this table, it says, uses first-axis form only and then proceeds to list off, reverse, rotate, replicate, reduce, expand, scan and catenate. [20]

00:28:29 [CH]

So, all four of them showed up in that single row of what I thought were like The not I think bread and butter is like the wrong terms, but like the some of the first verbs that I really learned and like associated with the language. And then I discovered later on that not all verbs or functions work that way on the sort of last axis — and we'll correct my terminology if it's the wrong terminology, in a sec — compared to like operations like match or unique that operate sort of — I didn't have the words for it, but it's on the major cells versus the last axis. So, I guess my question 1 is, am I using the wrong terminology? 2, is there more of like buckets of functions within the like the non-scalar operations that you sort of said "everything else"? Are there different buckets of functions that some work on what I call the last axis, some work on the first axis? Although that's the confusing part, is that this table that I consider the last axis functions refer to them as "use first axis form only". So, there's a lot of confusion here on that. Like, I feel like there's this kind of taxonomy, like implicit taxonomy, but my guess is that array programmers… it's not implicit for them, and they actually just… they know the categories of stuff anyways. Yeah, I'll stop talking and…

00:29:48 [ML]

Well, OK, so what's happening here is that each of these functions is actually — well, and operators — is actually 2 operators, so there's one that would have been considered at the beginning of APL, the normal version, which are the ones that you saw that act on the last axis. And then there's also a first axis form that's written with a horizontal bar somewhere. So, for slash (/) this is the slash bar (⌿), either slash [i.e. also \ and ⍀ — Ed.). For rotate this is the ugly one that's sideways [⊖ vs the last-axis ⌽ — Ed.]. And there are a few others, I think. [, and ⍪ — Ed.] Mostly, it's just that with a bar added to it. And so those are the first-axis forms of those functions, so I don't know if there's really any name for all of these, I would call them probably paired functions that have two axis forms, you know, just explain it.

00:30:42 [CH]

So, to be clear, just to clarify what Marshall just pointed out, is that the reduce and scan that are listed in this table that I'm referring to are actually the reduce-first and scan-first with those horizontal slashes [dashes — Ed.] in them, which I have completely missed in that those versions aren't actually the last-axis versions. They are the first-axis.

00:31:07 [ML]

Yeah, first-axis and first are leading, lasting are trailing. Those are those are just equivalent.

00:31:12 [CH]

First and leading and last and trailing. So technically, my terminology was correct? There's difference…

00:31:19 [ML]

Yeah, yeah, it's fine. You just didn't know about the… Or missed the first-axis versions.

00:31:23 [CH]

So why actually are reverse and rotate… Aren't those last-axis? Or are they actually first-axis?

00:31:32 [AB]

There're two of them in APL, right? There's the one in APL, the symbols for them a circle (○), with either vertical (|) or a horizontal (-) bar and that bar indicates the, so to say, the mirror surface of when you're dealing with a matrix, at least.

00:31:51 [CH]

Oh.

00:31:52 [AB]

So, the vertical bar, if you use them monadically, at least, a circle vertical bar, circle style (⌽), it mirrors right-left. And the one that's circle with a horizontal bar (⊖), it mirrors top-down, so I call it flip, sometimes, flips upside-down.

00:32:05 [CH]

So yeah, I have total… I've misread this one row in this table twice. I've missed the horizontal lines and also it says reverse comma rotate and then in parentheses the horizontal, as Marshall said, ugly, version of the vertical one ["Reverse, Rotate (⊖)" Ed.], and that applies to both reverse and rotate. For some reason, my brain read that reverse, and then there's a missing vertical line with a circle, which I don't know why my brain was putting that there and then when I saw rotate, I was like, "Oh yeah, that's the rotate glyph that's supposedly different than the vertical line one." [i.e. he read "Reverse (⌽), Rotate (⊖)" — Ed.] So, my brain just completely misread this. All of these are the first-axis versions of reverse, rotate, reduce, and scan. Interesting. OK, so a part of my confusion is that I can't read.

00:32:53 [AB]

I think it's true for all of the first-axis ones, whenever there's a pair of first- and last-axis, then in APL, all the first-axis ones will have a horizontal bar (-) as part of their symbol. So, you've got comma bar,[21] that's comma with a… and a minus on top of each other, kind of (⍪). And you have the circle bar (⊖). And you've got slash bar (⌿).

00:33:14 [CH]

And is this why, more typically, when I see array language people writing in APL, they prefer the reduce- and scan-first glyphs to just the regular slashes. Is this the reason? Is because…

00:33:30 [ML]

Yes, definitely.

00:33:31 [CH]

Interesting.

00:33:33 [ML]

So yeah, we haven't gotten into the reason why you would prefer this in the leading axis model.

00:33:42 [CH]

We can do that now if you want.

00:33:43 [ML]

I think we gonna have to talk about that now.

00:33:43 [CH]

We can do that now, yeah.

00:33:46 [ML]

So, the idea is we've said what the rank operator does. Which is basically that it allows you to… Well, it lets you to specify that you're working on the trailing axes, and another way to look at that is to say that you're mapping over the leading axes.

00:34:07 [ML]

So, the thing that these leading-axis functions do, like if you have your plus slash bar function (+⌿) which is going to do a sum. I mean, you might say it sums the columns of a table, but really what it's doing is summing the rows together. So, it adds one row to the next row to the next row. What rank allows you to do with that, is if you use this plus slash bar and you call that with rank one (+⌿⍤1), then it's going to sum the cells of the rows of the argument. Which is actually summing each row.

00:34:42 [ML]

So, this +⌿ allows you to sum in either direction of a matrix, and more generally, you can sum along any direction in a higher rank array. And the simple plus slash (+/), it's always going to sum the rows, and if you tell it the rank, it doesn't matter. Like if you say, "alright, I've got a rank-3 array. I want to do plus slash on rank 2 here (+/⍤2)." Well, then it's going to say, "alright, I'll do whatever I'm going to do on each block, on each 2-cell of the array." And the thing that it does, is to sum each 1-cell so it's still the exact same thing, summing each 1-cell.

00:35:21 [ML]

So, the idea, and this is the core of the leading axis model, is that having rank map over the leading axes, and then defining your functions to work on the leading axes, gives you this whole flexibility to work on any axis. If you combine them together.

00:35:36 [CH]

That is such a good… Oh sorry, I got a little excited there. That… I never… Yeah, I just totally… I hope a couple listeners just had the — I wouldn't say epiphany, but — understanding, from what you just said there 'cause that is I think extremely non-obvious based on the default behaviors of reduce — we'll stick with reduce 'cause I think that's the easiest to explain on a podcast — is that if you compare reduce (/) versus reduce bar or reduce first (⌿), and apply that, a plus reduce (+/), summing to a matrix. When you use reduce, it sums up row-wise, and when you do reduce-first it sums up column wise and so that's how I just think about those two, is like, "one does rows, and one does columns". But really, it's not doing that. Like, functionally that's what's happening, but it's what you said, you're summing the rows of a matrix or summing the elements of a row, which then end up having the same behavior as what I just described. But when you just stumble on the language and you look, you start playing around as, "Oh, what does this do? What does that do?", it's completely nonobvious that that's the actual behavior. If you're just messing around with sort of rank 1 vectors and rank 2 matrices. Bob?

00:36:56 [BT]

For reduce in J, what you're doing is you're, whatever your operation is, say it's addition. You're adding items. And items are the negative one rank of whatever you're adding. So, if you're if you're in a matrix, a table, your items are now rows. So, you're adding rows, 'cause you're adding items. When you have a, you know, a row, and all the items are atoms, now your items are now atoms and so reduce is actually adding, summing, in this case, items. And item can change depending on the shape of your array, if you have a, you know, a 6-dimension array, items are going to be your 5-dimension, whatever you'd call them, blocks, hyper-blocks. Those will be added together because you're adding items and you've got a 6-dimension array, so each of your items is going to be 5-dimensions.

00:37:54 [AB]

That's what I was calling "major cells" [22] before, which is a bit of a technical term. The idea is…

00:37:59 [ML]

Yeah, yeah, that's the APL terminology.

00:38:00 [AB]

Yeah, yeah, the idea is that you look on your array as a collection of arrays of a lesser rank, with one less dimension. So, there's like you can look at it as an outer dimension and then some, a bunch of inner dimensions. And so there…

00:38:12 [ML]

Yeah, this is really a non-obvious thing. Epiphany is absolutely the correct word for it. And specifically, this epiphany happened to Arthur Whitney on a train to the APL '82 conference in 1982. But to explain how not obvious this is, this is something that I found out kind of recently and I checked my memory on it today, Iverson and Bob Bernecky basically described., they actually did define the concept of function rank, but not the rank operator, in a 1980 paper called "Operators, Functions and Enclosed Arrays." ["Operators and Enclosed Arrays" — Ed.] [23]

00:38:54 [ML]

So, and in that paper, they described an axis operator that says specifically which axes you want to work on, and so you have to list out all the axes, which is kind of weird. And also, not only that, the argument axes can be… you can select any ones, but then once your function operates, the result axes all go to the end of the function so on the outgoing and it actually works just like the rank operator, but in the ingoing end it's much more complicated and weird.

00:39:26 [ML]

But they actually did describe function rank and they said the rank of a function is just the number of axes it works on. So, like a reverse-last, they would have said that that's a rank-1 function, and that concept was already there. But it took two more years and Arthur Whitney, which is, you know, throwing quite a lot of processing power at it, to develop the idea that actually all you need to do to have, you know, pretty much as much flexibility as you want, is to specify how many ranks the function has, and we'll just say these are the all the trailing ranks that it operates on, trailing axes.

00:40:04 [CH]

Interesting, that is…

00:40:07 [AB]

I think we should include that this was a reaction to a problem in the design of APL. So, we have to go way back to Iverson Notation.[24] So, Iverson's notation, which was just like a generalization and application of traditional mathematical notation — and it looked very much like traditional mathematical notation, and it used 2D paper to write on, write these formulas on — and he only ever dealt, I think he only ever dealt with — arrays up to rank 2, so up to matrices. And Iverson loved matrices. I have this from Roger Hui, that Iverson loved matrices. And so, when you're applying things, then there were only these two axes that you could ever work on. Right, so either you could apply at first-axis, or you could apply last-axis and that was it, and then you had… I think he used like slash and double slash, something like that, to choose the axis.

00:41:14 [AB]

And then, when they linearized Iverson notation into what became the APL programming language, meaning everything had to be discrete characters that could fit in a type writer, then they solved this problem by adding what's at least later has become as the "bracket axis",[25] possibly "bracket axis operator", but it's a little bit iffy to call it an operator. It's more like bracket axis notation. And that's the idea that after a APL glyph, you can write bracket axis, so you write open square bracket and then a number normally and then close square bracket then that tells that glyph on its left, what axis to operate on, but it's very ad-hoc to every built-in, so it might look like it does the same thing on all of them…

00:42:07 [CH]

Right.

00:42:07 [AB]

… but in reality, if you try to formulate what the rule is, you can't, because each one of those's separate… entirely separately defined, and you cannot generalize this. And so, this was, I think, quite early on, recognized as a little bit of a wart, and also something if you want to define your own functions, you couldn't use bracket axis because the system wouldn't know what it meant. GNU APL actually has generalized it so that you can — in the header of a function — you can specify bracket axis and then this function can choose what to do with that and then they kind of abuse it for just ss an extra argument.

00:42:45 [ML]

They generalize the notation. They didn't generalize the definition.

00:42:46 [AB]

No, no, because you can't, right? So that's why they felt the need to add it as an accessible syntax, that…

00:42:53 [ML]

Yeah, well, so that's why Iverson was developing an axis operator. His idea was, "I'll take this axis concept and generalize it", and that turns out to be a pretty bad idea. Like, yes, it's possible, it's very awkward to use and you've got this problem that not only do you want to know where the argument axes come from, you also really want to know where the result axes go to. So, if you wanted to specify all that, I mean, that's a whole lot of numbers that you're throwing at it to do one operation.

00:43:22 [CH]

Can you given an e…? Because I don't think I fully grokked the axis operator. Is there like a simple example on like a matrix? Like, it sounds like…

00:43:30 [ML]

Well, so we'll talk about scan,[26] so let's say… We'll say you're starting…

Actually, I guess it's going to make more sense if we say you're starting with the scan operator that works on rows. You just have the non-barred slash (/). So, then what you would do is give it… use the axes operator and say I want to run on axis zero, I think, whatever the first one is. And then what it would do, would actually be to split up the argument into the vectors that go along that axis. We would split it up into its actual columns in that case and do a scan on each column. And then here's where things get really weird, is that the results of scanning one column in columns, the list, so the result is a list. And then it would put those results together into an array. But then you'd have the results of scanning on a column would turn into a row because it doesn't know which axis they should make up in the result. So, that's kind of the model they had going. That was the state of the art in 1980.

00:44:40 [CH]

Wait, so let's make that example a bit more concrete. So, you're doing a plus scan (+\) and you specify axis zero. And let's say we have the matrix 1 2 3 1 2 3 1 2 3 and a three-by-three form, so it's 3 rows of 1 2 3.

00:44:57 [ML]

Yeah, yeah.

00:44:58 [CH]

What's the results of that?

00:44:59 [ML]

It would break off the column 1 1 1.

00:45:00 [CH]

Yeah.

00:45:02 [ML]

Plus scan that you get 1 2 3.

00:45:03 [CH]

Yeah.

00:45:03 [ML]

That's the first row of the result.

00:45:07 [ML]

Column 2 2 2 plus scan, you get 2 4 6, that's the 2nd row.

00:45:11 [CH]

I see.

00:45:12 [ML]

So, so it's actually the transpose of the first axis…

00:45:14 [CH]

Right, right.

00:45:15 [ML]

… plus scan as we had in APL.

00:45:17 [CH]

And you're saying the issue there, is that potentially if you're doing a plus scan column-wise, using this axis operator, you want it in the same form that you might see it using the scan-first in APL, where it wouldn't have the transposed result. Or maybe you do want it the way that it's doing it, but you just… you're at the…

00:45:35 [ML]

Well, almost always you want it the same.

00:45:37 [CH]

Right, right.

00:45:37 [ML]

You want what APL does now.

00:45:39 [CH]

Right.

00:45:40 [ML]

So, I mean the problem is you have an arbitrary function. It takes some dimension of array in, like for matrix divisions, say, it's 2 dimensions in and two dimensions out, and that's of course twice as bad, 'cause now you have two axes that you have to figure out where they go. And so Iverson wanted to be able to say, "I'll take, even for a matrix product — this is actually an example they gave — a matrix product with rank like…" Actually, yeah, the one that was in the paper was an axis operator [0 1;1 0], so it's going to take the first two axes of the left argument, and then the first two axes of the right argument, but reverse those axes so it does a transpose, and then you do the matrix product, and then, I guess, those axes go to the end of the results so that's just really complicated, and I mean if you try to implement that, you can't implement that efficiently. There's no way.

00:46:40 [ML]

And so, another nice thing about the rank operator is that because it always splits its arguments into cells which come together in memory, is very easy to pick a cell out of an array, and that means that if you write a function with the rank operator, it's going to have at least like a minimally efficient implementation.

00:46:57 [CH]

Right. Yeah, that is very interesting. So, I'm also starting to realize too, that I think my mental model of going into this was not entirely wrong, but just I had this view of the taxonomy of functions that I said existed, really what it is — and you can correct me if I'm wrong, Marshall — that in BQN, really it is just scalars and then everything else which works with the rank operator. I'm sure there's some functions that don't work with the rank operator, but all the ones that do sort of behave in the same way, which is why there's really only two categories. Whereas in APL, if you skip back to… I guess depending on how far you skip back, you know, the rank operator wasn't introduced into Dyalog APL… Or I'm not sure if it was introduced…

00:47:42 [ML]

In Dyalog it was it was 2013 or so.

00:47:47 [AB]

Version 14

00:47:48 [CH]

OK.

00:47:49 [ML]

So, actually pretty recent.

00:47:51 [CH]

And so, it's that APLs weren't designed initially with this idea at all, because it was a decade and a half before it, you know, Arthur Whitney came up with it, and then since then, which is why they have this table, they've evolved the language such that they've added second versions of operators and functions so that they have basically like the leading axis theory version of it, which they call, you know — they used to have reduce, now they have reduce-first. They used to have scan, now they can't scan-first.

00:48:25 [ML]

So, that is actually the wrong way around. The…

00:48:27 [CH]

So it's wrong way round?

00:48:29 [ML]

Yeah. These paired forms with the two axes, they recognized that it was useful, and so they had the two of them already. I mean, maybe you want to… And that goes back. Yeah, even the paper A Programming Language [27]had these pairs where, for example it would have slash and you'd write actually two slashes for a reduction along the first axis of a matrix.

00:48:54 [CH]

Interesting.

00:48:55 [ML]

And in that way, you wouldn't even… Because Iverson's, at that time, was really doing just matrix arithmetic, pretty much. You never needed to say the axis, really. I mean, I think that he may have defined a bracket axes thing or some sort of axes specification, but he certainly didn't use them much in that paper, and he wouldn't have when he was teaching with APL.

00:49:22 [ML]

But those paired forms are there from the beginning and the leading axes idea is that, well, you actually only need one.

00:49:30 [AB]

Well, you ac… You only really needed one even when they added the bracket axis right because bracket axes will completely ignore whatever if you give it again, it's ad-hoc. So, it doesn't matter if you say plus slash (+/), so that's reduction, sum reduction, or plus slash bar (+⌿) which is plus reduction in the first axis. It doesn't matter if you put bracket axis after it, they will just… then you'll just override whatever its inherent thing is, and then along some other axis. That was then a solution to the problem they had, that Iverson was using a single slash and double slash. And that wouldn't work in a generalized system with any number of axes, I mean, that would be unbearable.

00:50:10 [ML]

Yeah, but it's in that case, the language isn't… Now, it's not just providing you with two operators, it's providing with whole family of them. So, leading axis, you actually just define one thing and then, as a matter of definition, can apply it to others. I mean, that's an aspect of composability in APL.

00:50:31 [AB]

That doesn't mean they're not useful, by the way. Yes, they are sufficient.

00:50:37 [ML]

Yeah, it just means that there are more of them. It's a more complicated language. You have to know more things about the language to be able to use it. What does axes do on this function?

00:50:45 [AB]

Yeah, but it does provide for some…

00:50:48 [ML]

And I mean, of course it was still revolutionary at the time, but I, and a lot of other people, think that this leading axis model is… well, objectively it's a simpler language it leads to, and I think it's a better tradeoff in terms of your language design.

00:51:04 [CH]

So my next question, I guess, is, and hopefully the listener is still following along, and then maybe Bob, after Marshall answers this, you can comment if there's differences in J, is that surprisingly, in BQN, if you're coming from APL, you'll try and do plus slash which is really plus fold and it's a superscript operator (+´), and if you try and do that on a matrix, it'll go, "bam-bam, sorry that doesn't work", and then you're like, "what, this works in J, this works in APL", and then very quickly, because the documentation for BQN is phenomenal, you go to your little BQN to Dyalog APL translator page [28], and then you look it up and you have to add, I think, what it's called, cells, which is another superscript operator. So all the [monadic — Ed.] operators in BQN are superscript, and so that means we have, basically… and which cells is like, I don't know how you want to describe it, sort of…[29]

00:52:08 [ML]

Cells is just rank -1. That's exactly what it is.

00:52:11 [CH]

Or I was gonna say like visually it's a like…

00:52:14 [ML]

Oh, it's a breve symbol.

00:52:17 [AB]

It's little moon.

00:52:19 [CH]

What was it called, the "grave" symbol?

00:52:21 [ML]

Breve; B-R-E-V-E.

00:52:23 [CH]

B-R-E-V-E.

00:52:25 [ML]

So it's kind of like a circumflex but rounded.

00:52:28 [AB]

I think it's a half circle.

00:52:29 [ML]

It's a that opens upwards.

00:52:30 [AB]

Yeah, the bottom path of a circle but superscript (˘).

00:52:32 [CH]

Yeah, we'll call it a cup. So, in order to get it to work, you have to go plus slash, which is the fold and then the cells (+´˘), and so also you have, in terms of the superscript operators — and maybe you can add to this list — but the… so, we have basically fold (´), scan (`), insert which is basically like 2 slashes (˝), and then the cells (˘).

00:52:54 [ML]

Yeah, and insert is actually the one that you want, for talking about leading axes model. So the fold just works on lists. It's not actually a leading axes thing at all. Insert is the one that works like J (/), which is the two ticks (˝). So, I mean, that would be pretty similar if you're applying to rows. But that's the one that does the leading axes stuff, is the insert.

00:53:23 [BT]

And you were asking about asking about J. As soon as Marshall said, you know, cells are, you know, -1 rank; that's items in J,[30] and that's how J handles it. Just refers to items.

00:53:39 [ML]

Well, I mean rank operator negative one is the definition of the cells…

00:53:43 [BT]

OK.

00:53:44 [ML]

…in BQN, and it's called a modifier., but…

00:53:45 [BT]

But in J, they just assume that you're working with items.

00:53:49 [ML]

Well, yeah, but if you want to, like if you want to sum the rows of an array, you have to, in J, you have to write the number 1, or -1.

00:53:54 [BT]

Right. Yes, yeah, yeah, yeah, right.

00:53:54 [ML]

And in BQN, you just have one nice symbol for it. That's it's hard to describe, but it's easy to see.

00:54:03 [BT]

Yeah, you've got a symbol that represents the minus one rank, as opposed to J, which you're actually putting the number in.

00:54:06 [ML]

Yes.

00:54:07 [BT]

Yeah.

00:54:08 [ML]

Which is very nice and is what you need to use like maybe 2/3 of the time even. So it's very convenient.

00:54:16 [BT]

Yeah, items or cells are very useful. No doubt about that. If you if you set up your… The other thing that is worth mentioning in this, is, all of this presupposes that when you got your information into an array format, you've put the information in the right order in terms of dimensions, because if you mix up your dimensions, you can work completely against what you're trying to do with items or cells or leading axes, and you're going to have to do a transpose to get it back to where you can actually use it efficiently.

00:54:48 [CH]

Wow. All right, I just tested something while there was conversation for the last 30 seconds. So, I just discovered… So well, where do I start‽ So, the double tick (˝) which, is called insert in BQN,[31] is the same thing as reduce-first (⌿), correct? In APL.

00:55:11 [ML]

Not quite. It is the same as SHARP APL's reduce-first, but… Which is where Iverson was working. But the APL2 family actually does something slightly different, which is, it works element wise. So, actually, the Dyalog reduce-first is equivalent to J or B… well, you can only really say it in BQN and it's equivalent to each insert (¨˝), so it works on one element at a time. Well, the evaluation order I think is different, but it's the same basic idea.

00:55:49 [CH]

It works at one element at a time, opposed to APL, which does what? Or Dyalog APL?

00:55:55 [AB]

It doesn't matter if you're using plus, because plus all is already working element at the time, as I started off with. But if you use anything else, like let's say you use concatenation, [32] then there's a huge difference, right? If you, let's say you have a matrix — three-by-three matrix [1 2 3;4 5 6;7 8 9]. Now, if the insertion here, or reduction, considers each row as a whole entity and you then reduce, then you would have 1 2 3 concatenated with 4 5 6 concatenated with 7 8 9 (1 2 3,4 5 6,7 8 9). OK? So, that gives you one long vector. Whether or not it's enclosed is not so important. If you do it elementwise, then it's kind of like concatenate-each, so it would be 1 2 3 concatenate each with 4 5 6 concatenate each with 7 8 9 (1 2 3,¨4 5 6,¨7 8 9). So, essentially what you get is 1 4 7 are concatenated together and 2 5 8 are concatenated together and then 3 6 9. Those are the columns, so essentially in APL, if you do — so, concatenation is , — if you do ,⌿ — that's reduce-first — on a matrix, then it will give you the columns as vectors. Whereas in J or BQN, if you do the equivalent [,/ and ∾˝ resp. — Ed.] it will just merge together the rows into a single vector.

00:57:28 [CH]

OK.

00:57:30 [AB]

Now you can achieve the APL results just by adding an each to two J or… [BQN; each and ¨ resp. — Ed.]

00:57:40 [ML]

Yeah, which is why I ended up… So I think of these as three different kinds of reductions. There's their pure list thing that only BQN has. Well, K also does that [33], but BQN among multidimensional array languages has that, and then there's J's insert which comes from SHARP APL, and there's this APL2-family element-wise reduction. And I decided that the best combination was the first two of those, because it's the easiest to get that element wise version out of the insert.

00:58:13 [CH]

So, say those three versions again, there's the…

00:58:18 [ML]

There's a pure list reduction, I mean what you would see in Lisp or another functional programming language that doesn't really connect with array.

00:58:27 [CH]

And that's your BQN fold that doesn't exist in Dyalog APL or J, but does exist in K.

00:58:34 [ML]

Yes, that's the only one K has because K doesn't have… I mean it represents multidimensional arrays as lists of lists, so it doesn't have any native multidimensional array type, and so the only thing that makes sense is to do a list reduction. Although you might actually say that's the same as J's insert, I mean it sort of collapses these concepts together when you're representing raised this way. Because you might say that, well, the elements of the list are actually the cells of the array, if you think of it as a multidimensional array. So, K kind of has both, but in terms of what the implementation's doing, I would say that it's the list fold is what it does.

00:59:16 [ML]

And then, so BQN is basically case version that's split up into its two things: Well, what if you think of it as a list of lists versus what if you think of it as a as an array of cells.

00:59:28 [AB]

Is your head hurting yet, Conor?

00:59:31 [CH]

No, no. I mean like… I feel bad for the… I'm well… I don't… there's two categories of listeners: Like I said, there's the array programmer that thinks it's great that there's a podcast that talks about their favorite paradigm now, or one of their favorite paradigms, that's probably going, "Oh yeah, this is, you know, some bread-and-butter stuff." And then there's the beginner whose heads probably being like, "You know what, I'm done with this paradigm, because this is… seems overly complicated." But that's just I mean, admittedly, this is a difficult conversation because we've got, you know, primarily, we're focusing on J, Dyalog APL and BQN here, all of which have sort of overlap. Like there's a Venn diagram of these operators and understanding that Venn diagram requires understanding this concept of rank and leading axis theory. I thought that the… So, just for the listener, BQN once again, their fold is the superscript slash (´) and then the insert is a superscript slash-slash (˝). I just realized I looked up on J NuVoc, that insert is what they call the slash in J.[34] So we've now got the double-slash in BQN is called insert, and that follows basically J's insert which I didn't even realize that JS insert and now BQN's double-slash insert are both, basically, the leading axis theory ones, and you said there was a slight difference between the Dyalog APL reduce first, which is sort of the element-by-element versus — what's the terminology for not saying element-by-element? Treating them just as items?

01:01:21 [ML]

Heh, we don't know.

01:01:21 [CH]

We don't know. We don't know what the terminology is, but…

01:01:24 [ML]

It just does, uh, does whatever it does.

01:01:26 [CH]

Which is confusing because, well, the reason it's confusing is that for certain operations, the behavior is identical, which is you know,

we had to switch the…

01:01:35 [ML]

Yeah, well for everything you could possibly do in APL\360, which was which was just arithmetic operations, reductions, they're exactly the same; there's no way to tell the difference, which is why, when APL kind of split into this SHARP family and the APL2 family, the flat versus nested models, they were able to extend this in two different ways. And either language could have chosen either direction, I think. Well, I don't think that the element-wise thing would make much sense in the in the flat model and in SHARP APL.

01:02:14 [BT]

No, I think the leading axes gives you a lot more power than the element.

01:02:18 [ML]

Well, they both work on the leading axis, it's just done…

01:02:21 [BT]

Oh yeah, I guess you're right. Yeah, yeah, OK yeah, yeah.

01:02:23 [ML]

It's just you connect elements across the leading axis, or do you just split it into itself?

01:02:27 [BT]

And Conor when you're talking about what a beginner is going to get from this, I think probably the best advice is probably pick one of these languages and work with rank on that. We're right now, we're talking about comparisons between the different languages and that becomes quite difficult, although if you know one of them, I'm learning a tremendous amount listening to how BQN and APL do it, because it clarifies a lot for me because I understand J. If I was to suggest anything for somebody to understand, J's rank is probably… I'm trying to think of the chapter numbers [6 — Ed.], but there's two chapters of Henry Rich's book that really go into detail on rank of objects and then rank of verbs, which is what we call functions in J.

01:03:13 [ML]

Yeah, I think "Verbs Have Rank" is the name.

01:03:16 [BT]

"Verbs Have Rank", and we'll put links in, [35] but they're excellent. And when you start out, it's going to take you several readings to get a sense of it and see… If you really want your mind blown, you can stack the applications of the rank and do some very interesting things with that, so you can break your object into chunks and then take those chunks and break them again, and especially if you're working with something like catenate which is infinite rank, you can do some amazing combinations with it. But that really is the power of rank, but that's not where you start out.

01:03:55 [CH]

So what are the… I've come to the realization now that there are two different ways to sum the rows in a matrix with what is provided in BQN. And maybe it's the same case in J. The first way is plus slash, which is the fold, superscript fold, and then cells (+´˘). How do you spell it with the…? So that's número Uno. Número dus, dos — I actually don't know what the next one is. Número two. I apologize to our Spanish speakers

01:04:32 [BT]

Oh, that's Spanish‽

01:04:35 [CH]

Is it? I don't know.

01:04:37 [BT]

We're making up languages now.

01:04:39 [ML

It's "dos" in Spanish.

01:04:40 [CH]

Thank you. I was surprised no one helped me out there. I… Just left me out there to try. How do you spell it with the plus insert? And then I tried rank one (+˝⎉1), but that does not do… Or I'm not spelling it correctly. Is there… I assume there's a way to do it.

01:04:56 [ML]

That should work.

01:04:58 [CH]

Oh no, yeah it does. It does work, I just don't know math. Right.

01:05:02 [ML]

And you can use either one with cells too, with the cells modifier. [+´˘ and +˝˘ instead of +´⎉1 and +˝⎉1 — Ed.] The difference is that when you take… When you do a sum with plus fold (+´) of a list, the result of that — and now this concept, you can't even express in APL and J — the result is a number that is not an array. So it removes a layer of depth. When you do plus insert (+˝), what it's actually adding together are not the individual numbers, but zero cells that contain the numbers and so the result is an enclosed number.

01:05:40 [ML]

And so what you want is actually, when you're doing a reduction on array — like if you want to add, sum the rows — you want to use insert (˝) because the depth of the argument is the same as the depth of the result. And the reason why this is practically an issue, is that if you have not a matrix of numbers, say, but a matrix of lists of numbers, say you've got a matrix of coordinate pairs. Then when you do fold on one of these lists, it's going to add all the coordinate pairs and the result is a coordinate pair. But then if you take that with rank, then the result is going to say each of these coordinate pairs is a row of the result, so it's going to merge those all together into one N-by-2 matrix, which is probably not what you want. Especially given that like this matrix could actually… maybe it would have lists of length 2 on the first row and length 3 on the second row, and so on. you do want plus insert cells, because then the result of each reduction is going to be an enclosed coordinate pair and the final result when it merges those together is just going to be a list of the pairs.

01:06:54 [ML]

Mind blown.

01:06:59 [BT]

And is that because it's a based system Marshall? [36] Is that where that comes from? Yeah.

01:07:04 [ML]

Yeah, that that's the reason. Well, you would have like… If you introduce this list reduction function to APL, you would have the same problem, but the difference that the based system makes is that there is a difference between an enclosed number and a not enclosed number. So you can actually see the difference even for lists of numbers.

01:07:26 [ML]

For APL, the… I would say that the… for lists of numbers, the problem is kind of like forcibly removed by saying that a number is the same as an enclosed number. But you still have the problem with a list of pairs of numbers or something like that.

01:07:40 [BT]

And for J we get around that by boxing because we're on flat.

01:07:45 [ML]

Some sort of. Same thing. I mean, if you had the a function that took a list of boxes and summed the elements together, you would end up with that same problem, if it didn't return a boxed value and you just don't have… Yeah, the way you get around the problem for a plain list of numbers is that you can't have a… a list of numbers has nothing like inside it, in J there's… I mean underneath you can say there are numbers, but if you try to take an element of a list, you still get a rank 0 array. You don't get like a plain number. There's no such concept. So, it'll implicitly force it up to rank or to depth 0, because that's the lowest depth it has. Which would correspond to depth one and BQN.

01:08:30 [CH]

I'm thinking now, about how I feel about all of this. Because a part of me like…

01:08:43 [ML]

Me too.

01:08:43 [CH]

I think the reason that I fell so quickly in love with APL was, you know, just going to tryapl.org or .com or whatever it is, it's .org, right? [37]

01:08:55 [AB]

Yeah.

01:08:57 [CH]

And then I just went, you know, plus slash iota (+/⍳),[38] I think I did iota first because we have that in C++ and I was like, "oh, that was, took me one character, whatever, plus a number. Where it's std::iota(…". And then you have to — before even that — you have to initialize a, you know container — usually a vector and then you gotta, you know… It's just a mess. Then you define the iterators and now we have views, but the point is, took one character versus like 20 in C++, plus a whole hell of a lot of knowledge about where, you know, things live, in which headers and etc. etc. And then in order to add that list, I just went plus slash, and I was like, "holy, that was easy again" and, you know. And so the reason I'm saying I'm sitting here trying to wonder how I feel about this is, that experience, it's extremely, I think, easy for a newcomer to just like… I think there is actually something special about coming from C++, if you are not like, you know, if you are curious person that is open to different paradigms and you know aren't going to be like, "well, how… where is the performance graph on this? Is this faster or slower?" If you're if you're just, if you're interested, coming from C++ really sets you up to sort of be wowed, because of how succinct everything is, and there's a lot of stuff that maps from our algorithm header. But with this stuff, so like technically in a leading axis theory pure array language, you know, maybe BQN, you might refer to it like that, but like if you were to remove the fold and just have the insert and you wanted to sum the rows of a matrix, which I think, from my experience, is very common thing you want to do when you're solving little, you know, fun trivial LeetCode problems. It's a very common thing that comes up. You now went from plus slash (+/) and just putting the matrix there to plus insert rank one (+˝⎉1) which, like, I can see the argument of, you know, this is, it's the better model. It's what Aaron Hsu would say about when I complained about the powerset expression in order to generate all possible combinations of whatever length. And I went to APLcart,[39] and it was like I don't know 16 characters or something [{⌿∘⍵¨↓⌽⍉2⊥⍣¯1⊢¯1+⍳2*≢⍵} — Ed.] and I was like, "oh, come on!" Like, 16, like, this is way more complicated than I thought it should be. And Aaron had this response talking about, you know, it's when you really learn the language, all the little tools and pieces that you need to build up that expression, like, give you so much more power at the end of the day.

01:11:45 [CH]

But then there's this always this argument, or there's this line of like how much power do we want to give them? Do we expect a beginner to, like, learn… Anyways, I feel like my monologue of how I'm feeling right now is becoming incoherent, but the point being is, like there's a tradeoff between, like, complexity and beginner's simplicity and like, what's the best version of this?

01:12:13 [ML]

Yeah, so I have some similar doubts. I think I blame a different aspect on this. I would say, well, the thing with rank, is that… I guess I'll go into the move from axis to rank. I mean, so yeah, rank definitely works better than axis did, as APL started, however, axis is a whole lot easier to understand, and I would say that's because it's more explicit in a sense. So the way I think of rank is, sort of that it's for array programming, it's a lot like assembly language because it leaves everything implicit. It forces you to work out what is the number or the rank I want. And then you encode that in this very compact way. You say there's a single number that says what I want to operate on, but the axes of your argument arrays are something that you have to keep entirely in your head, so it's not like, like, say, assembly would deal with types. Each assembly instructions tells you what types you want to work on, but you don't have any high-level picture of what type is this value, what type is that value?

01:13:26 [ML]

You just say add as floats, these two registers; add as ints, these two registers. So I think the leading axes model in that way is very low level, so it's a really elegant representation of a low-level computation. But I am really also interested in looking at how would you express this at a high level.

01:13:46 [ML]

And the tool that I think does a really good job of that is, it's not a general tool, and so making it a general tool is still a challenge. But Einstein summation notation,[40] which you'll see in a NumPy package like einsum, which does give you… it give names to all the axes. So I think that naming axes is a much higher level view, and I'd like to see a programming language that really takes advantage of that, which I mean, the best we have now for that is really the old school Fortran paradigm where you have each index loops over, you know, this axis or that axis and you index everything with brackets. But I think there's room for a for a much higher-level view that takes array programming semantics but encodes them in a in a really different way to get…

01:14:40 [ML]

And your code would be longer definitely, but it would also be more explicit and more naturally intuitive, and it would say, you know, what it's working with, rather than just what it does to it.

01:14:50 [CH]

Have you seen the program or heard of the programming language Dex? [41]

01:14:55 [ML]

Yeah, so that, that is a…

01:14:58 [CH]

Does that do anything something close to that?

01:14:59 [ML]

A little bit. I mean it does have named axes, but I think it doesn't really… Like you still manually line up all your correspondence between axes, so one of the ideas I've been thinking about is, what you really would like is to say your function takes these two — like if you have matrix multiplication — we want to take a matrix with axes a and b and matrix with axes b and c. And then like you should be able to add these matrices together and it should know, I mean, maybe you have to say that "I want to pair the equal axes", but it should know that axes b go together, and a and c go on their own. So that's an idea that I've been looking at, that I don't think Dex has anything like.

01:15:45 [CH]

Adám, do you've thoughts on BQN versus APL, like versus a… Because like I…

01:15:55 [AB]

I mean it's kind of double mind about this, because, yeah, it seems more pure, like J already, and then and then BQN maybe takes it to an extreme, but then it comes down to practical needs, actually need to get something done then I often find that, "OK, so APLs might be less powerful or they have more build-ins that are technically speaking redundant, because you can easily do the same thing, but it's really convenient." I find that APL is much better at being at making these common actions easy, so the fact that… Let's say I want all the sums of the columns and the rows of a matrix. So, it's like to check some like magic squares and things like that. The fact that I can write things like, in APL, plus slash bar comma plus slash as a fork (+⌿,+/). So that plus slash bar (+⌿), that's the column sums, plus slash (+/), that's the row sums, and they just concatenate together. Now I've got all the sums. That's really convenient. Even looks good in my opinion.

01:17:11 [ML]

Yeah, and so that is where BQN cells (˘) helps, but it doesn't get you back to APL, but it gets you closer because you write plus fold join with plus… Well, you want insert not fold: Plus insert join plus insert cells (+˝∾+˝˘) and so you've only got that one extra character in there. It's not quite as good as APL, but it's pretty close, whereas in J, if you've got the rank one (+/,+/"1), it's not quite as nice.

01:17:42 [AB]

Oh, but it goes much further, right? Let's say that we're dealing with like rotations of a matrix or flipping a matrix. And then, then it gets kind of ugly when you need two special decorators everywhere for the all the different combinations of it.

01:18:00 [AB]

So Iverson actually… I described this circle with a vertical bar (⌽) or horizontal bar (⊖), and there's also the backslash (⍉), which is transpose,[42] and Iverson originally designed a ton of these symbols, like every combination of vertical bars and horizontal bars and things that you can flip and mirror and rotate, whatever, everything into whatever possibility exists.

01:18:21 [AB]

And APL pared that down to just the three of them, but you can pair them up two-and-two to get what you want. And there even more so it gets kind of ugly to have to have extra decorators. I totally understand the reason why you would want that. If I was to make things from scratch, I might also do that. But there's still something about it just being so convenient and so intuitive that a circle with a vertical bar it does things horizontally and circle with horizontal bar it those things vertically. It sounds opposite, but I mean it's the bar is indicating the line of that you rotate over, the mirror surface.

01:19:00 [ML]

Yeah, and there's really nothing I can say about that. Yeah, it is nicer.

01:19:03 [CH]

Yeah, it's a… Yeah, there's something. I feel like a lot of the times what I want is like BQN plus Dyalog APL. Like there's like a Venn diagram and like what was it yesterday I tweeted? The formula for number of permutations, which is like n!/(n−k)! factorial and I realized that those two factorials, that's the over, that's the psi (Ψ) combinator. And then n shows up twice, so you can use a dyadic fork a.k.a. Ψ₁ combinator (⊢÷⍥!-). And then, proof! In like 5 characters, and then I was like, "alright, I'll go do this in BQN", and then BQN changed the factorial (!) function to be an assert, and then I went to the page and then it was, "There was no glyph for factorial", and then I was…

01:20:03 [ML]

Yeah, you gotta implement it yourself.

01:20:04 [CH]

And then I was just like, "Oh well, this is an expression I like better in APL." But then half the time, because there's Before and After which are like… I mean, I think just because of before and after,[43] like BQN edges out above any other array language. Because they're just so convenient and like necessary in my opinion. If you're a combinator fan, but anyways, my point being here — 'cause I'm just rambling — is that, yeah, I a part of me is like this this trade-off of, you mentioned Iverson had, you know, originally designed all the different symbols, is like, I'm just like, "let's just have all the symbols! I just want I want everything to be a symbol." But then obviously that's… at some point you… there's a diminishing, whatever, returns of what you're requiring. You know, obviously 3000 symbols is too much, or is it? I don't know. Chinese: you have to learn like 8000 symbols to speak Chinese, so maybe it's not.

01:21:03 [BT]

I think there's kind of an analogy here between modern automobiles and older automobiles that what we've developed in automobiles is very efficient, very powerful, very controlled, items that we really can't work on anymore because if you lift the lift the hood it's all hidden to you. And I think when you first meet one of these languages, what you're seeing is that kind of a thing. You see this thing that does this amazing thing for you. Whenever you want, does it really fast and really simple. I do this. But then, you decide, "I don't exactly want to do that. I want to do something slightly different." And what that requires, is you have to go in under the hood and figure out what's going on in there, and then figure out how you're going to do it. And each of these languages gives you the tools to do that, but that level is never going to be what I would refer to as simple, because you actually have to know how things are working under the hood, as opposed to hopping behind the wheel and hitting the accelerator. And I think that's probably what you're hitting. And it doesn't make the languages harder to learn on an initial basis, but what I found and what I really actually enjoy about J, why I still stay with J, is because that learning curve just keeps on going and going and going; there's always something more to figure out, at least for me. I think probably Marshall and Adám are beyond that; they're, you know, running off, and, you know, designing their Ferraris and stuff, but I'm still learning that stuff that's under the hood, and it's just an ongoing adventure to find that stuff. You have to be able to want to do that, to really enjoy those aspects of the language. I don't think it will ever be easy, but the languages are still very easy to use on the surface if they're doing what you want them to do.

01:22:57 [AB]

I think there has to be some kind of level. Yeah, sure you could have a super simple language that can do anything, technically speaking, except the humans can't keep up because it's because it's too much work, like some Turing tarpit language. Yeah, you can do anything, of course you can. But you can't actually practically do anything. And then on the other end of the spectrum, where you provide enormous amounts of built-in things. And then the human can't keep up anyway, because he can't keep that vocabulary in the head. So you have to find the balance there somewhere. And it's not an exact science, and it probably depends on the individual, so… And so, I don't think there's much we can do other than making up some arbitrary constraints. Like K has a famous constraint; it's the ASCII symbols you can type on like a normal keyboard; the printable ASCII — it has to stay there. And numerics and letters, those are out 'cause they use those for numbers and identifiers, and so all those, the specialized glyphs, those are… that's the entire language basically. And I think both BQN and APL are limited also by the keyboard in a sense, but like you can only have two extra symbols on every key on a normal keyboard and that's it. So we… Both of them are getting to that limit, and that's…

01:24:28 [ML]

Well, BQN hasn't really run up against it and I'm not planning on going any further, so I would not say BPN is limited in that way.

01:24:36 [AB]

If I look at the BQN key map, pretty much every key has two symbols on it: extra BQN symbols.

01:24:42 [ML]

No, but there are a pretty good number of keys left. If I wanted to add things, I just don't want to add.

01:24:46 [AB]

Yeah, and the same thing for APL; there are a few more holes there, and I think that's a pretty good number. It's also interesting too, because think about the human interface things. Why do computer keyboards have the number of keys they have? A standard keyboard; 100 and something. That seems to be like what humans can deal with. If you give them a large lot of keys, then they don't know where to find them anymore. If given too few keys, then they're frustrated that requires a lot of fiddling with shifting keys and so on to get to where they are — or sequences to get what they need — so it's some balance like that.

01:25:25 [CH]

Alright to… We've… I totally lost track of time because I've just been 1. enjoying this conversation and learning a ton, but also 2. just like I'm lost in thought, 'cause I kind of just like Matrix-downloaded a bunch of stuff into my brains and now that I understand it that, that's what I'll be thinking about for the next week while I'm going on runs so…

01:25:44 [ML]

I know rank too!

01:25:47 [AB]

But Conor, did you notice that we've been speaking all about needing axes and the rank operator inside — we haven't even really gone into transpose, and how important it is for all of this. So I vote that Marshall has to come back to continue this with the second part, which is transpose.

01:26:05 [CH]

Yes, Part 2 to come. I didn't even know that there was a… Well, I haven't gotten to the point where I understand that transpose is important, but so the two things that I'll say now is that I think my big sort of eureka/epiphany is that it's these different ways — so I already — there's these different ways to spell doing things on a row-wise basis a.k.a. rank one. So previously, I had sort of read out — and I said mistakenly that it didn't work, but it definitely does work — so there's plus fold cells (+´˘) that works on a matrix. There's plus insert rank one (+˝⎉1), and that's the same way you would say the J solution is plus insert rank one (+/"1) and then in APL it's plus slash (+/) or so I'll say plus reduce is one way to spell it. There's also plus reduce first rank one (+⌿⍤1), and then I also tried plus reduce rank one (+/⍤1), which I didn't expect to work but does… When you use rank on… 'cause reduce-first is the leading axis theory version that is rank-able, for lack of a better way to describe it, but can you also is the reduce one also rank-able, or is that just… it works for rank one and doesn't work for other ranks or?

01:27:24 [AB]

It technically works, right? You can, but it has an inherent… So, the normal… You're talking about reduce normal slash, right? That just looks at the rows, right?

01:27:31 [CH]

Looks like yeah, which is it's completely redundant to say rank one after it because it's already, that's the…

01:27:36 [AB]

Yes, but you could do — I mean not that it makes much sense — but you could do a rank 0 on it and it will sum every number on its own, which would be no difference because a number, itself, added up, is itself. But yes, it's still… rank operator is completely general, applies to any function. Including plus slash, it's just not very useful 'cause like you never want to do that.

01:28:05 [BT]

If you use the zero as your rank operator with that reduce and…, could you create each number being a list by doing the catenate in front of it?

01:28:16 [AB]

Well, you…

01:28:17 [BT]

Could you create list that way of atoms single atoms to the lists or does it work that way?

01:28:23 [ML]

Reduce on one element never does anything, it just returns it.

01:28:26 [BT]

Yeah, right OK.

01:28:30 [ML]

Which is actually kind of an inconsistency sometimes.

01:28:34 [BT]

Well, in J lists of one element returns a one item list, right?

01:28:34 [AB]

No…

01:28:42 [BT]

If I catenate.

01:28:45 [ML]

I don't think so.

01:28:46 [AB]

No, I don't. Everybody is frantically typing into the J interpreter.

01:28:53 [CH]

While Marshall types and figures that out… So the point here…

01:28:56 [ML]

No, no it gives you an atom.

01:28:59 [AB]

Yeah.

01:29:00 [BT]

OK.

01:29:01 [AB]

So, it doesn't… So reducing over a single element never does… over a scalar…

01:29:05 [ML]

Well, in…

01:29:06 [AB]

… never does anything.

01:29:07 [BT]

OK.

01:29:08 [AB]

And if you do rank zero then you'll… the function will only ever see scalars, and then not do anything, so it's not useful.

01:29:14 [BT]

Yeah

01:29:15 [AB]

Does it apply? Yeah, technically it applies. They'll probably make performance be really bad, doing nothing — with terrible performance.

01:29:24 [ML]

Although I do have to say that BQN just makes any reduction on a ranked zero array an error. Usually it says, I mean, if there's no axes, you can't do anything on that axis.

01:29:33 [CH]

Interesting, so we've got reduces, reduce-first, inserts, and folds, and really the reduce from Dyalog APL is the odd one out, kind of. Because the reduce-first/insert are the ones that sort of, you know, we talked about the differences, or Marshall explained the differences. Anyway, so that's, sort of, my eureka. If you were a beginner and following along and you managed to have that eureka as well, then you've…

01:30:08 [ML]

You're no longer a beginner!

01:30:10 [CH]

Yeah, so the last question…

01:30:12 [AB]

I usually say that rank operator and dyadic transpose, those are the keys to the array kingdom.

01:30:23 [CH]

Woah.

01:30:24 [CH]

Right, if you master those, then the rest is just vocabulary, because those two together give you access to manipulate — like like in literal sense of the word manipulate, like literally twist your arrays around, exposing their insides, in whichever way you want — and now you can apply things. What you apply is a matter of vocabulary.

01:30:46 [CH]

It's like…

01:30:47 [AB]

Those two things are essentials to be able to apply things.

01:30:49 [CH]

It's like doctors… or go ahead!

01:30:52 [BT]

And just just to finish up what I was thinking about with the with the catenate, it was not reduce catenate item, or rank 0, if you just catenate rank zero, that's what will create lists of each for each individual one.

01:31:07 [ML]

Oh yeah, yeah, that's ravel though.

01:31:08 [BT]

Yeah, that's travel. Yeah,

01:31:10 [CH]

Yeah, so dyadic transpose, rank, sounds like the Eye of Agamotto from Doctor Strange. You've got that, you can turn back time. And I'm not sure if we have last time, for this last question, but it's less about the theory of this stuff and more I think… Was it Joel Kaplan, on our last episode, or it might have been Stevan Apter, I can't remember which one of the two, but made a comment with respect to Arthur Whitney. I think it was Joel Kaplan [44] who said that Arthur will throw away ideas when he, you know, he'll come up with something and then you know he's not afraid to let go of it, and made that remark specifically with respect to rank and that sort of, as you mentioned in '82 he was the one that came up with it, but then rank doesn't exist in K. Is there, does anyone have any insight into Arthur? Does he think that rank was a bad idea and and like came up with it and then the rest of the array community ran off with it and then he later on was just like, "Yeah, that was a bad idea." Or?

01:32:17 [AB]

No no, not at all. I've seen the emails from him where he's even considering adding a rank or it would really be a depth operator to K as well so it does have… and just kind of like BQN has these these shortcut monadic operators which are just their their big brother equivalents with the right operand of negative one, so you have cells (˘), which is really a specific instance of the rank; it's just rank negative one (⎉¯1). Then K has has each (') like APL and BQN has as well (¨), and J has as a keyword — not keyword, standard library word.

01:33:09 [AB]

So, K has each (') and then it has each-left (\:) and each-right (/:) and each-left and each-right are very much specific instances of using the rank operator and that is pairing up the entire one argument with each element on the right or each element on the left, and then that means that you can then increase the rank on one side by adding multiple of these, you could say each-right, each-right, each-right (/:/:/:) thing, but it's… they consider it fairly rare to need that kind of thing, but there I've seen cases where they wanted that and it became ugly to do it in K, and then Arthur reacted with, "Well, maybe it's time to introduce the general rank operator again", so I think he keeps it in back of his mind like it might be a possibility if necessary.

01:33:58 [ML]

But there's no denying… I mean, this is just a question of tradeoffs and K is definitely an easier language to learn and use than APL. In some cases where you have high-rank arrays, K is going to be a lot more awkward to use, but it's also more straightforward to figure out what you need to do properly. So I mean, K is a smaller, simpler language in terms of the primitives it has. And so there's… You definitely can't say that Whitney just made a mistake in removing rank. It's just that he chose to go in a different direction.

01:34:30 [BT]

And I might be wrong about this, but I always get the sense when people talk about Arthur designing languages. He's designing for a purpose, and when he designed K, it's like you're trying to design AA/Fuel Dragster, right? You're not going to put coffee cups in it, it doesn't need them.

01:34:47 [BT]

Take them out!

01:34:48 [ML]

Yeah, take them out.

01:34:49 [BT]

And then maybe, maybe down the road there's something, you know, "you know what, but I could use power steering on this thing, so maybe I'll put that back in. Maybe that's worthwhile." But it's because his purpose is not to do a general purpose I think, with K It was to be able to go in and deal very quickly, very efficiently with huge amounts of data, specifically, I think time-based and that's what it's meant to do, so if it didn't need something to do that, out it goes.

01:35:18 [CH]

Coming soon to a theater near you is an amalgamation of K, BQN, J, APL — all the languages, all the glyphs. My comment earlier on the fact that Chinese has, like, you know, you need what 3000–4000 to read a newspaper or something, and they all type in using Pinyin or like I even used to when I spoke Chinese, and lived in China. You know you you type these symbols in using little Pinyin. Maybe we should have a 3000 character Array language, you know? I'm starting to… that idea's starting to grow on me.

01:35:53 [BT]

But for — I don't know Chinese — but there's a basis of the ideographs right? They're not just random.

01:36:01 [CH]

Correct, yeah.

01:36:02 [BT]

It you know.

01:36:03 [CH]

They're radicals, are what they're called. They have, you know, a radical for hand, a radical for wood and stuff, and you can find those and things that are wooden or things that require your hands and stuff. And like people is… Actually, I'm going to get that wrong, because I'm thinking of the name of their Facebook which is Renren (人人) which is just person-person [meaning "everyone" — Ed.], I think. But anyways, yes, they have like patterns that you can sort of, you know, you can guess what, you know, "Oh, this has radical A and B and so I'll take a stab at it that this is this, and you're probably wrong, but you might be close.

01:36:39 [BT]

And I'm guessing because of your interest in combinators, your foundation of those characters would be combinators.

01:36:51 [CH]

Maybe definitely they'd tie in somehow. Although combinators, I mean they're the hardest things to find symbols for in my opinion, because it's very abstract what you're doing with them, but…

01:37:02 [CH]

Anyways, at this point we're at like the hour-and-a-half mark. I'm not sure… Our episodes just keep getting longer and longer, but any last things we want to say? This is awesome. Thank you for coming on, Marshall, and thank you for ed… Well thank you to all of you for educating me. Hopefully the listener or some of the listeners managed to pick up one or two things and we didn't completely scare them away from the array paradigm. Adám?

01:37:26 [AB]

I'll just say to the listeners: Don't give up. It might seem really complex, but once these concepts click — you'll know it!

Let's just say to to this this don't give up.

01:37:37 [CH]

I mean, if they've made it to the end of this hour-and-a-half episode, hopefully… I mean we probably should have said that at the beginning. Maybe that's what you can do; insert that clip at the beginning, Bob.

01:37:50 [BT]

I thought… what I was going to say is, whoever is still listening, who's a beginner here, congratulations, you're amazing. I'm not sure this was a beginners episode, but it might give you a sense of where to look and what to think about when you're using this. You can still use the languages on a sort of a more surface level, but if you really want to dive in, this is what it is, what you're going to start to learn. And as Adám says, when it clicks, boy, it's amazing; you really can do some amazing things with it.

01:38:20 [CH]

And there's hope because I mean, I've made it, what, a year and a half. I can't… 2019 or two and a half years without fully understanding the rank operator. Clearly, for whatever amount of understanding that I've developed, I've done it all without really understanding the rank operator, so this definitely isn't something you need to learn on day zero. It's like Adám said, it's… you unlock the full power of the matrix that APL and array languages have to offer, when you can figure out the rank operator.

01:38:49 [ML]

Well, Dyalog made it about 30 years before they added this operator so.

01:38:54 [CH]

Oh yeah, that's a good point.

01:38:57 [ML]

There's that.

01:39:00 [CH]

Yeah. All right, I guess if that's the last thing to say, we will finish this episode by saying happy array programming.

1:39:07 [all]

Happy array programming!