Transcript

Many thanks to Rodrigo Girão Serrão for producing this transcription
[ ] reference numbers refer to Show Notes

00:00:00 [Conor Hoekstra]

I always feel weird about that when I'm like I'm super excited to announce the Oh yeah you guys have already heard the title of this episode, so I mean...

00:00:07 [Adám Brudzewsky]

Our guest, coming up soon...

00:00:10 [AB]

Who's the guest?

00:00:10 [CH]

Our surprise guest that was not a surprise as soon as anyways

00:00:14 [Bob Therriault]

this is all going into the cold open.

00:00:18 [BT]

So by the time they hear this, they'll know exactly what's going on.

00:00:19 [CH]

You get a long cold open.

00:00:22 [AB]

Let's just do it.

00:00:34 [CH]

Welcome to another episode of Array Cast. I'm your host Conor, and today we have with us 3 panelists. We'll go around and do a short introduction starting with Bob, then going to Adám and Marshall.

00:00:43 [BT]

I'm Bob Therriault.

00:00:44 [BT]

I am a J enthusiast and I'm working on the J Wiki and it's a wonderful project, but it's a lot of work.

00:00:50 [AB]

I'm Adám Brudzewsky, full-time APL programmer, also do various types of social media and content creation.

00:01:00 [Marshall Lochbaum]

I'm Marshall Lochbaum.

00:01:01 [ML]

I'm the author of BQN and in the past I've been a J programmer and a Dyalog developer as well.

00:01:06 [CH]

And as mentioned before, I'm your host Conor day-to-day C++ developer, but in my free time I'm a language and combinator enthusiast and super excited to talk about our topic today. But before we get to that, we're going to have two short announcements. We'll first go to Adám for one, and then Bob for our final one.

00:01:21 [AB]

Right, so there is one week left to submit in the APL problem solving competition. [01] If you haven't started yet you might not be able to solve everything, but you still have a chance to win a phase 1 prize. And together with that one week left is also one week left of the early bird discount for registration to the Dyalog '22 user meeting in Portugal. [02]

00:01:47 [BT]

And my announcement you may have already caught on that, Marshall is going to be one of our new panelists. So welcome to Marshall and thank you for agreeing to come on the Array Cast and be a regular panelist. We found we were talking so much about BQN that we had to bring you on and and addition to the expertise that you bring in your background, it's great to have you on and I look forward to having you in future podcasts. And I should also say today we're missing Stephen Taylor. We hope to have him back for the next podcast. He's off getting mended right now, hopefully nothing serious. He just said that he had to take go in for an operation and was not available for this.

00:02:28 [CH]

Yeah, I'm super excited about this because I have felt because I can't actually remember when we started this podcast, but I think BQN was on my radar, but I definitely have learned so much about it since having started this podcast what, over just over a year ago now and since having learned about it, I felt that because when we were missing sort of we had, you know, APL, J, k/Q, although I'm not sure if we should put the slash there, but you know there's there's a dotted line or a solid line however you want to draw it, and then also BQN now which is uhm, yeah it's a I mean Marshall can talk all about it, but it's it's kind of like a hybrid kind of something completely new and different, and we're going to discuss of some of that today with our topic, which is control flow structures and keywords if I've got that correct, which I know very little about, so I'm just going to throw this to whoever wants to take it 1st and talk about 'cause I guess I'm not going to say anything. I'll throw it to whoever wants to say, you know, how these exist, are they you know, idiomatic and go from there. Adám, go ahead.

00:03:30 [AB]

Well, I'd like to just start off with a historical background. So if you go way back to the very early APL days, or array programming days before there even was APL, Iverson notation [03], where it was this a mathematical notation that was also able to describe a flow for algorithms. It was great friendly, algorithm description language. And Iverson used conditionals, and then, since everything was like drawn on paper, or or blackboard, you use these conditionals and then you draw an actual arrow going from the conditional up to the place that you would continue an execution if the conditional decided so, so it's kind of like a flow diagram together with the code in the as one thing. And then, when Iverson notation was linearized, it was made into a one dimensional sequence of equally sized characters in the computer. And then that became a line based thing with the right arrow taking a line number and then you could, if you gave it an empty list of line numbers to go to then it wouldn't go anywhere. If you gave it a line number, then execution would jump there and then you could use the structural functions of APL to create either an array of a line that consist contain the line number or no line numbers and you could conditionally go around there and so for many many years this was the only way of controlling flow in APL. Basically it's a GOTO [04] . But there aren't any conditionals and there isn't any ternary or anything like that. The only way you could control whether or not they would branch would be to create an array that had either line number on there or no line number as it used to be like you would reshape a line number to length one length zero that you could use [inaudible]. And so that was for a very long time the only thing. But today things look a little bit differently. Various dialects of APL added ALGOL-like control structures or kind of like basic with key-, keywords: if then, else, endif and J also added that. [05]

00:05:54 [ML]

J was first.

00:05:55 [AB]

Yeah J was, J was first. And they have a slightly different, the way that they work is slightly different between them, i'm sure, we'll we'll get to that. So this is like just the historical background for that.

00:06:06 [CH]

So does that, the first question i guess I have is that I had no idea that that was the history. Does that assignment to an array with the line number where you jump to is sort of GOTO, does that exist in Dyalog APL today? Or is that's, that's gone?

00:06:19 [AB]

No, it's not. It's not an array assign, so you create an array, and then you feed that kind of like an argument to a function where the function is the right arrow. It's the GOTO arrow.

00:06:28 [CH]

I see...

00:06:30 [AB]

And then, of there is...

00:06:31 [ML]

Well, which may or may not be considered a function.

00:06:34 [AB]

Yeah, so it it looks like a function application. It look it's syntactically speaking it's it's very much like a monadic minus for example.

00:06:40 [ML]

It goes through the parser like a function, but you're not necessarily supposed to know about that.

00:06:45 [AB]

I'm not sure every implementation does it the same way that like does it, there's like a like a function could be special since like, but the important thing is from the APL perspective it's kind of like a function you apply to it and that causes control flow to or the execution to to jump to a different location, and in general, APL implementations are backwards compatible, and indeed in Dyalog APL, as well as new APL, aPL 2 and APLX, although that still works, even if in some of them the preferred thing is to use actual keywords, but yes, it all works.

00:07:14 [CH]

That was my second question. So it does exist today due to wanting to maintain backward compatibility. Is it recommended though?

00:07:21 [AB]

I would say yes and no. In general you'd want to avoid spaghetti programs and GOTO is considered harmful and you really shouldn't do that.

00:07:31 [CH]

And for the listener, Marshall Marshall shook his head to indicate no.

00:07:36 [ML]

I have never heard an APL programmer recommended the use of GOTO. Admittedly, I haven't spoken to that many APL programmers.

00:07:42 [AB]

But there are certain cases where it is still the norm. It's fairly common, not that I prefer to do it, I I prefer to have a single flow from top to bottom, and if you want to skip out of everything early then I would put everything in a giant if statement, but it is fairly common to see people wanting to quit the function early, and so APL had this convention that line 0 is like the header line of a traditional function. And if you go to line 0, then that just means return the function with whatever value the return variable has.

00:08:15 [ML]

Yeah, which that one.

00:08:15 [AB]

Yeah, that one is fairly common to do. Another thing that still is used to say you have some loop or you want to jump out of that loop. [06] Then you could conditionally branch out of it to a label that you had set-up, and that's also sometimes used where writing that will control structures could be awkward. But still, if you see the traditional way of formatting APL functions is that all code lines are indented one space and then labels are not indented, and if you see a-, and then of course since GOTO it doesn't return a result, it just takes something on this right, so whenever you do a branch, you would, you generally have the right arrow as the first character on the line. If you open up in a some APL code, as in professional APL, and you see a bunch of lines begin with a right arrow with some long expression after that, and a bunch of lines begin with a label then immediately go “Yeah, this is not going to be fun.”. So no, it's not recommended.

00:09:15 [BT]

That brings me to my question, which is why are they there in the languages? Because you can do a lot of this stuff without having to rely on these control structures. But as you said, J has an extensive group of them. I mean it's got a "select" statement, it's got, it's got "try" and, uh, "catch", it has the GOTO [07]. I've actually never seen anybody use the GOTO, but you can actually, you specify it as GOTO underbar label and then you you loop you you name your loop loop underbar label so you can call your GOTOs wherever you want and go back and forth. I've just never seen anybody use that. Part of it I have seen people use a lot is "try" [08] where you try something and if there's an error it kicks back to your "catch". Or you can even call another function and do a "throw" back I haven't seen anybody do that, but why are those there? They seem to me to be, if I had to guess, they're there because they're not hard to implement in C, which is the underlying language that you know J is implemented in, so and it's convenience, and I suppose for someone who's got a more imperative programming background, ALGOL-like background, that's comfortable to be able to see your program broken in, if you've got a bunch of tacit or even explicit, you know sentences, you can break them up by using these control structures to have "if"s, "else"s, "elseif"s, "whiles", "whilst"s, j has a whilst [09], which means you do it once, all the time.

00:10:48 [ML]

Yeah, well, it's the equivalent of C do-while, that's a pretty strange name.

00:10:55 [CH]

Is it whilst or whilst?

00:10:56 [BT]

Whilst.

00:10:58 [CH]

I I don't know, I literally I'm just asking.

00:11:02 [ML]

Well, I've I've seen it expanded as wild skip test. Maybe Henry Rich invented that. I'm not sure about the derivation at that point.

00:11:13 [BT]

I think you're right, the pronunciation is probably whilst, I'm not going to claim a new way of pronouncing those letters.

00:11:21 [CH]

So maybe maybe we should do 'cause the so the question is why do we use them? But maybe what we should do first? Let's go through to the extent that we can based on what folks know and enumerate maybe the keywords slash structures in APL then J, so we'll go, so APL for Adám. J for Bob, and then BQN from Marshall. And if we want we can circle back to Adám for Q and then we can talk about, because I definitely know that there when you mention there's alternative ways to do things, but some of them I think are very idiomatic, but others are awkward and maybe we should, once we sort of enumerate what the different ways to do these in the different languages are, we can talk about which are the ones that you know, the three of you, to reach for. If any of them at all and when. When do you sort of, instead of doing some while loop, do you choose some other sort of more array idiomatic array approach? That sound good?

00:12:14 [ML]

Idiomatic and awkward are not mutually exclusive. Idiomatic code is usually the least awkward way, but that doesn't mean it has no awkwardness.

00:12:23 [CH]

This is true. This is true. Maybe we'll put a Venn diagram in the many of your blogs have very nice Venn diagrams, marshall, I'll get you to do an awkwardness versus idiomatic Venn diagram for us.

00:12:34 [ML]

With that out of the way.

00:12:35 [AB]

I think we should. We should limit our scope slightly. Also you mentioned control structures and keywords and and while the control structure may or may not use keywords in whatever language, there there are also keywords that are not control structures that I don't think are so interesting in in in this aspect, like for example J has an assert keyword.

00:12:58 [CH]

Right, right, the ones that I'm primarily thinking in my head and potentially the listener are most interested in our, uh, conditionals AKA like if statements specifically sort of expression based ones where you have an if and then an else and then, also, I think the loop you know most array programmers, even beginners, very quickly figure out how to get around, but a lot of while loops, especially ones where that can't be mapped to sort of building up a mask and then doing a reduction, where you need to conditionally grow something like so, for instance, if you need to do multiply or square a number until it's above a certain value, that's like a very classic while loop, and then you multiply it by two. There's ways to do that, but a lot of times when I'm coding up a LeetCode or small problem, I'll just like choose a number that I know is large enough to like, solve all of them, and then just like do it, take whatever, or find that value. Anyways, I'll stop talking. So yeah, maybe try and focus on whiles and ifs, but there's, if there's something close to that, that you think should be included definitely definitely feel free to do so. Let's start with aPL.

00:14:07 [AB]

OK, it's probably limited to a particular APL dialect, just with note that not every APL does support this, if I remember right, new APLs do not have control structures. [10] It closely follows APL2 which doesn't have any of these sort of thing at all.

00:14:22 [ML]

That's right.

00:14:23 [AB]

But are there common APL implementations, APL Dyalog, APL, APL X, and APL plus, definitely have them and today they, I think they're they're very close in their vocabulary are just mostly familiar with with Dyalog and and so

00:14:40 [ML]

yeah, so I did a bunch of research on this and there was like a it was at some point in like the mid 90s, all the APL vendors suddenly came around and decided, wait, we need to add these control structure things that everybody has been using for the past 15, 20 years and and so then one of them, it may have been APL X, one of them found this colon syntax and they they all jumped on that very quickly.

00:15:02 [AB]

Yeah. So the way it's spelled is pretty much with traditional names, for these that you see in C style languages, UM or yeah, but with keywords so you'd have an if and and if and and then in order to preserve the APL axiom that there are no reserved words, then it uses a colon from the code, and it's traditionally used in APL for control flow. So you have the traditional label would be labeled name, colon at the beginning of a line and here you just begin with colon, instead, so it's colon, and so we've got "if" "while" "repeat", which is just starts a loop without any condition there, and and then you can, both "while" and "repeat" can either just end like "end while" we just keep rolling and "end repeat", uhm, you can, or you can have an "until" so you can have a a condition at the end of the loop as well. Then there are for for loops and there's both "for in" and "for each". So you can have multiple variables that are being updated every time around the loop. They can, it's kind of like a transpose of where you pick them up from the "select", which I think is generally called switch in other languages. It's like it's a case statement, so you just select this thing and then there are multiple cases and and there's "else" as well. So it's like a fallback for anything in both "if" and and "select". And then there is is a bit special, there's a "trap", one which is kind of like the try and there is a difference between APL implementations. But the idea is you you run some code inside this block and then if anything happens you can go to handle that, that was actually pretty cool. I think in the way that it can classify errors and take different action depending different errors. And then there's some keywords that help you break out of them. So there's like you can "return" from the function. You can leave the current loop that you're in, or you can continue with to the next iteration of this loop without finishing it up. And finally we have something called "Colon section" which is kind of like an if true, so it doesn't do anything, it's just it's just a control, it's just a a block to help you organize your code, but it doesn't actually do anything. So that's the vocabulary. There's some dialects can do slightly more, some can do slightly less, but that's that's the idea.

00:17:28 [CH]

All right, so that's for APL, Dyalog APL, specifically. And for J, Bob, what do they have? Something similar?

00:17:36 [BT]

Yeah, I'm, pretty pretty similar to that. [11] We've got, we've got the "if" [12] and we also have an "elsif" and the structure is you got "if" and then you've got a statement which is going to be your control statements going to be true or false. It'll evaluate to true or false. It actually is dependent, it's actually not true and false, it's zero and anything else, and not only that, it's only the first item in your, whatever you create. So in other words, if you create an array of, you know 100 ones, it's going to be true because that first one is a one. If it was a zero at the beginning, it would be false.

00:18:12 [AB]

Oh wow, I didn't know this.

00:18:13 [ML]

I didn't know this.

00:18:14 [BT]

Yeah, so it can get, it can get kind of tricky. You know, like if you don't know that.

00:18:19 [AB]

Well there, So what if it's empty?

00:18:20 [BT]

If it's empty, then it's true. You leave something empty, it's true.

00:18:27 [AB]

Wow, so it's the absence of a zero in the first element or something like that. Not zero in the first element.

00:18:32 [BT]

If you put an empty and if you, so as you go say and and the way it works in J is the control words are always followed by a dot. Period. So if period dot is a control word, if you type in if then you create a variable that's named if but "if." is a control word and all control words are followed by a dot, so you can, you can spot them pretty easily. But if you have "if." nothing then you follow it with "do." and then whatever you followed by. In this case, it will be followed afterwards by "end.", so you've got your, your sentence is kind of broken up into these these sections, but with nothing in that first area it's going to be true and it will do the last part of it every time.

00:19:20 [AB]

Well, that's really interesting, and I think that that I didn't mention this when you said Elsif, yeah, APL has a elsif as well, but, uh, there's, I've seen in in various language discussions what's consider truthy, what's considered falsy, and JavaScript has is a mess, and I i really like the aspect of APL, at least, i like APL, that only a single zero is falsy and only a single one is truthy and nothing else. So if I end up saying if 2. Then I'll get an error, and that's a I i find that that's a big rescue net. It will generally catch me in doing some having made a mistake as opposed to those languages where just, Oh yeah, that's that's true. Or or if empty string, oh no, that's false. OK, let's go so, so I really like that aspect you can you can have various shapes on that, uh, that 0, 1 so it could be a one element vector, or it could be a scalar or a and or even the matrix and so on. But as long as it's a single element but that works out fine.[13]

00:20:30 [BT]

What would what would happen if it was a matrix? So could you or say you had you know?

00:20:34 [AB]

It just has to be a singleton, just has to be a single element.

00:20:37 [BT]

OK.

00:20:38 [AB]

Of any shape.

00:20:39 [ML]

Dyalog does that a lot, where it it accepts singletons in a lot of places where you might expect a scalar.

00:20:45 [AB]

But I've never had that be an issue with that and and you sometimes you end up with a with one element vector that really stands for a single true false value. That works fine.

00:20:56 [BT]

So the other one is kind of interesting. I think is is the "for." or "for under bar" and then whatever you want is your variable in your for loop and that means you can, and if you do the under bar and the variable name it will track within its for loop, it will track that variable name, so for instance, and what it's doing is for is doing each item of whatever you give it. So if you give it 3 numbers, it's going to do the first number, the second number, the third number. If it's just "for" dot, the loop won't know what those numbers are, but if you do "for underbar" and then the variable name you can then access that variable name and the index of that variable name within your loop, which is kind of sometimes pretty useful. It just gives you an...

00:21:49 [AB]

Wait, how do you get the index of it? How does that work?

00:21:51 [BT]

With the index, you well, you just you look for that name and then you underbar index.

00:21:59 [AB]

Also, you'd have two underbars for underbar variable name underbar index and then so every time I run the loop it will give in...

00:22:06 [ML]

No, no, it just automatically creates,

00:22:08 [BT]

it automatically creates underbar index for that variable name.

00:22:12 [AB]

So, so that's a reserved word, kind of. If you had another variable with that name, it would just what gets overwritten.

00:22:18 [BT]

Uh, my suppose if you used underbar index, sure if you were in the middle of a for loop, I guess it would. Yeah, yeah.

00:22:22 [CH]

It seems like very verbose for like they named the language J and they didn't go underscore i. I'm just very surprised that when you said underscore index I was like, bob must mean like just underscore or like underscore i, there's no way they added like 5 letters, but clearly I was wrong.

00:22:40 [AB]

But being it's a reserved name, you want to kind of make it long and obscure so you don't by mistake like clash, but it's pretty natural. Have something like called K underscore I or something like that.

00:22:50 [CH]

I mean, that's true, but most of the functions, like P colon for primes and stuff like they didn't, you know, i I guess,

00:22:58 [AB]

oh, but those are not reserved.

00:22:59 [CH]

Yeah yeah those have colon yeah OK I I see the point I see the point.

00:23:02 [BT]

I am checking right now.

00:23:03 [CH]

But I'm still surprised.

00:23:05 [BT]

Just to make sure that I'm being accurate with this.

00:23:08 [AB]

Also, that means if you have, if if you have two nested loops that use the same, variable name, which otherwise wouldn't be a problem, you just can't see the other one. Then you can't get the out the index of the other one either.

00:23:21 [CH]

That's like a problem with every language though, like, don't name your, don't shadow your outer loop variable name with the same.

00:23:27 [AB]

No, no, but but my point is, you don't, even if you choose the same name because you don't need the outer one, now you you cannot choose the name of the index variable.

00:23:36 [CH]

I mean I yeah, I think technically technically you're right, but I I don't think and I was going to mention to for those of our listeners that are polyglot. This is kind of similar to Go. [15] It's one of the very, I shouldn't say very few. It's one of the nicest things about Go when I learned it is that their range based for loop syntax by default comes with like a tuple that's destructured that always comes like, so the enumerate in Python that bundles you're index with a sequence so that you can destructure it to whatever be index comma element that's like built into every single Go range based for loop. And if you don't want the index, you just use an underbar to discard it. So this is kind of similar in that when you have this version of your for loop in J, the underscore index is there for you to access if you want.

00:24:23 [BT]

And I checked and it is underscore index. It's whatever your variable name was underscore index is actually the index that you're working on at that time, and if you had nested loop, 'cause I think you could do on the the innermost loop you would just use the name of the outermost loop and find out what that index was. I would think you would do it that way.

00:24:44 [CH]

Yeah, I think Adám's point was though, is that if you use the same name for all of your indices...

00:24:49 [ML]

Which you should really never, ever do.

00:24:51 [CH]

Yeah, which which you should I think is just a bad like technically he's right.

00:24:54 [AB]

Best type of right!

00:24:57 [CH]

Even in like or I shouldn't say even in like most languages, when you've got some triply nested for loop, it's you know i, j, k, or column row, whatever. You know it's so it's not an issue.

00:25:06 [ML]

Yeah, but but I definitely don't like the idea that the language is is making up its own names that my variables are going to have. It's kind of strange.

00:25:14 [AB]

Yeah, yeah, I I kind of like the way it is in in JavaScript [16]though as much critic as I have for for for JavaScript there if you do a, uh how does it go like a a map,

00:25:28 [ML]

yeah, so it passes i think 3 arguments to the to the function, that's the mapping function. It gives you the value and the index, and then the entire array actually.

00:25:38 [AB]

But the thing is, you can give it a function that only takes one argument and then you don't need to get rid of that.

00:25:42 [ML]

[inaudible]

00:25:44 [AB]

yeah, so so that works out really nicely, but the way I would do it in APL of course is i'll just have two variables, then have the the value array and then iota tally and the value array and then would it just does an in each.

00:25:58 [ML]

And I I do like being explicit about that, but i mean, yeah. I get that if you're using the indices a lot the JavaScript way is a little simpler? I mean, it's.

00:26:09 [CH]

Yeah, it's interesting that it sounds like JavaScript and you know, listener, i apologize, I don't know anything about JavaScript really. It sounds like they're overloading like just they have three different map functions. One that takes a unary operation, a binary, and a ternary, whereas in other languages, clojure, [17] they have like different names, so it's like map and then map with index and I don't think they have map with index and also copy the array for every single iteration, but it sounds like JavaScript is doing something like that.

00:26:35 [AB]

I, I think JavaScript is just done that makes that it you can just if you if you try to call to use a function that doesn't take enough arguments, then it just won't be fed those extra arguments and that's it.

00:26:48 [ML]

Well or or you could say it is and it just ignores them anyway.

00:26:51 [AB]

Yeah, either way.

00:26:52 [ML]

So every function really takes an infinite number of arguments, but it only uses a prefix of them.

00:26:57 [AB]

Yeah, right?

00:26:58 [CH]

Right, right Bob, you were going to say something. We should stop talking about JavaScript before we get a bunch of our expert JavaScript listeners being like. What do you guys have no idea what you're talking about?

00:27:07 [BT]

Well back to just that first letter of JavaScript, so we're back to J now. The question I've got is honestly was with why would you use, i mean why would I use a for loop? Like I I have so many other ways I can do it in an array style. Well, why is that there?

00:27:28 [ML]

Yeah, that one has never made a lot of sense.

00:27:29 [AB]

Yeah, well let's get back to that, right?

00:27:31 [BT]

OK, you don't want to answer that question now.

00:27:34 [CH]

We should finish with BQN's, BQN structures, and then we'll circle back to. Why do this? Maybe the answer's just don't, and then we'll end the episode.

00:27:37 [ML]

All right? Yeah, so BQN, bQN has no keywords. [18] Every piece of built-in BQN functionality or the, I guess the one exception is the system values which are written with the dots, so they're more like names. There's this filled-in dot and then you'll write a name. It's like APL squad. Other than that, every piece of syntax and every primitive is one character, so there are no keywords and there are no control structures either. The closest thing we have is the, BQN's kind of explicit functions, or equivalent to dfns, are called blocks [19]. And these have a system where every block can have multiple bodies in it. And it runs on, generally it only runs one body, so there might be a header that tells you what arguments the body applies to, and there's also the closest thing that has to control structures of predicate [20] which is written to the question mark. So you'll write an expression and then a question mark and it'll it'll normally be evaluating code. It'll evaluate the expression and then the question mark tells it, was that zero or one? If it's one you just continue, and if it's zero, you exit out of that body, you say well? This this wasn't what I was looking for. So one way to use it is is it's kind of like an extension to a header like the header can't declare maybe that one of the arguments has rank two or more in a predicate. You can just say well it's 2 less than the rank of my argument, question mark and so that, that sort of extends the header and you can also use it as like a ternary statement and an if-then kind of thing, so it's pretty flexible. And it's a lot like APL or Dyalog aPL guards in dfns as well. Uhm, that's the closest BQN gets to a control structure though. And the idea is, I mean this I, i wouldn't say this is necessarily complete, but the way things work is that you should be using, you shouldn't be trying to use these imperative constructs in, you should be using generally functional programming and trying to use things like recursion or use well in the case of, I can say the JS for-each loop and this also works in APL and J, the... Instead of that you can just use the each operator or modifier and that works just as well. So there are a lot of different ways to do control flow and having first class functions helps a lot, and there's a page of the documentation where it shows you, well, you can build all these control structures as first class functions that take, they don't work on blocks of code, but they work on block functions and that looks pretty similar. You just have to say each block has, ignores its argument, and so you get something that looks a lot like control structures, but it's built out of first class functions and that means it fits better with this functional style of programming and a more declarative programming as well.

00:30:53 [AB]

You can kind of do this in APL, and people have been doing this for decades. And writing fake keywords control structures that are actually operators, and so they might, so, for example, you could do and an if else operator [22] that takes 2 operands. Uhm, which are just functions or they could be long functions with multiple lines and then it takes one argument. It takes that argument and says if it's true then it runs one of its operands, and if it's false, it runs the other operand.

00:31:29 [ML]

Yeah, and so the big difference in BQN is that you can just write a list of functions, since BQN has this explicit stranding syntax, one of the really nice things is that you can strand, say, two block functions together. [23] These functions have curly braces and then you just put one strand ligature character, the little smile thing, between them. And then you get a list of two functions so that even looks reasonably like the syntax you'd get with an if statement you have if and then some curly braces with something inside, not parentheses, and then the ligature and then more curly braces, and you can spread that second function out onto multiple lines and you get something that looks a little like imperative code. So that way you don't have to be using operators, which give you kind of a weird weird ordering for everything.

00:32:21 [CH]

And just maybe illuminate a little bit more what Adám said, 'cause this is something that for the longest time I never understood even till like a couple months ago is that when Adám said operands and arguments and out if you can clarify or correct me if I'm misarticulating what you're saying is that there's basically there's different types of arguments to functions in APL. So in APL, you've got omega and alpha as your argument arguments and you have double omega and double alpha as function arguments. So when he said operands he was referring to arguments that you take as functions and when he said argument he was referring to argument arguments, I think. Is that correct? [24]

00:33:07 [AB]

So yeah, the way I like to think of it is like like operators are like function factories, they take one or two parameters and and construct a function from that and those parameters can be either arrays or functions, or one could be the train one as a function, and so and those are called operands, so operators take operands to derive a new function and all functions, derived or not, take arguments, so that's the vocabulary we use, and so the way I was envisioning this is and syntactically, the way it would be is that a dyadic operator is an operator that takes two operands that, this it's called a conjunction in J and it's called a 2 modifier in BQN, but it's syntactically exactly the same. It takes one function on either side, so you can write function A, my operator, function B, and then an argument. So that's so that's what Marshall means that the the order of things is a bit funny, so you have the if the if operator is in the middle there for example true clause is on the on the left and the false one is on the right and the the conditional is on the far right. The actual value that determines the how things and you could set it up?

00:34:30 [CH]

Like that, yeah, and that's at least what was confusing for me for the longest time is that you have in your head while you're learning the fact that you are in a simplified world, if you ignore forks and stuff, you're limited to two arguments to you know unary and binary, or monadic and dyadic functions, and so I never understood that you could use basically both operands and arguments in a single dfn, so it's like if you see double alpha double omega, yeah, and then alpha and Omega. Technically, that's like 4 arguments. If you use argument as an all-encompassing term.

00:35:06 [ML]

Oh, input input.

00:35:07 [AB]

Yeah, input. Yeah, or parameters if you want.

00:35:10 [ML]

We will all stay much thinner.

00:35:13 [CH]

What it is, oh I see. Use input for the all-encompassing term for arguments and operands. Gotcha, yeah. So there's four inputs to that dfn, but like I had never read a chapter that like explained it or anything and I don't think it was until I was watching like a Dyalog webinar that I saw someone building one of these functions and they were like Oh yeah these will be your your functions you're passing in and these will be the data that your faux passing in. Like oh, that's pretty simple like it's just you have to realize that you can pass in a maximum of two arguments or two operands, or both at the same time, and that can be up to four inputs. And whenever you see the operands that makes your dfn functionally an operator, correct?

00:35:55 [AB]

It's called a d-op.

00:35:58 [CH]

Yeah, and that's actually something that I learned recently is that we refer to dfns like I always thought, whenever you see the braces, that's just a dfn. But that's not true. Braces with alpha and omega is a dfn, braces with alpha and omega and potentially an omega omega or alpha alpha is a dop which the fun is for function and the op is for operator which is, anyways we're sort of... [25]

00:36:19 [ML]

Yeah, so that's why I had to go with a block in BQN and it's just that it is all-encompassing.

00:36:24 [CH]

Interesting, so what's the difference in BQN from 'cause 'cause, or I'll let you describe it. But you don't have the alphas and omegas, you've got little sort of scripty, Fs, Gs, indexes and stuff.

00:36:34 [ML]

Yeah, devil struck.

00:36:35 [AB]

Blackboard just but.

00:36:37 [ML]

Yeah, yeah you can call them that too. I mean, there's not a huge difference, there is there is a difference in that and, and this is also where BQN is more like j. In APL you always have to take, you always have to take the arguments before doing anything, so if you have your block and you only use the operands, [26] so that's a dop but it'll take its operands and then it'll still just sit there, even though it doesn't mention the arguments, it's waiting for them. So in BQN you have immediate blocks, you have both a really immediate block that has no arguments or operators, and that's a lot like that that, uh, control structure that Adám mentioned that's not really a control structure, it just groups code. Uhm, that can also create namespaces, which is nice. So you have an immediate block, but you also have immediate modifiers that if they don't mention their arguments then they can just take operands and return a function immediately. So you might have like an immediate operator, immediate modifier sorry, that returns a train and then you don't need to mention the arguments. But there are, mostly the differences are in the details. The basic idea of dfn or dop versus BQN's block is pretty similar.

00:37:59 [AB]

I'd like to mention an example, so I said we can, we can define our own kind of control keyword, the kind of operators and apply them, but all of these languages, these three at least, BQN, APL and J, they all actually have one simple one. That's there as a glyph right out-of-the-box. And you might not realize you can use it like this. That's the power operator. [27] It's the power operator for all these languages. One usage of it is identical function on the left of the power operator and on the right you have a number indicating how many times to apply this function to. The overall argument. Now if you restrict the domain of the number of times to apply it to 0, that is a condition because remember in all these languages, true and false are just zero and one, then effectively, if it's a one, we apply the function once. If if it's zero, we apply the function 0 times, that is not at all, and that is just an if statement. So the power operator, a subset of its domain, is an if. Apply this function on the left if condition on this argument.

00:39:12 [BT]

And the addition with J is if you use -1 you get the inverse.

00:39:16 [AB]

Oh yeah, well, that's all of the all and in between 2 so so that yeah. And and you can if you can give if you do 2 it will do it twice. So it's it's also a repeat N type structure and all of them have that out-of-the-box. The idea is just that is one operator that behaves in a control structure anyway, but you could make as many operators you want to do with those kind of things, you can make them ad hoc to do exactly what you need today.

00:39:41 [ML]

Yeah, and that's that's one of the big things in BQN is, i mean, I looked at control structures in all these languages and I was like, i mean I i think from the beginning I was kind of on the fence about whether I wanted to eventually do control structures. But I looked at all these languages and said, gosh, there's so much complexity in these control structures there is, you know, like how does switch work? What are like are there fall through cases and crazy things like that? And also, there's a lot of subtlety in the way that variables are scoped, like does, if you have a for loop, can you introduce a new variable in the in the for part and have that be defined in the block? So there's a lot of confusion there, and so it's at the same time it's simpler in if you don't have all this stuff to find that you have to learn. And of course it's definitely going to be like a little different from pretty much any other language, because they're all different from each other. And at the same time, it's more flexible because you can define the thing that does exactly what you want. So you might say I want this sort of fancy switch statement that actually it takes a list of interleaved condition and function or something like that. Or you could take a an N by 2 array or, you know, organize things, you want use the conditions however you want, and so on.

00:41:06 [AB]

So before we go to whether or not you should use this or there's cases where you you would want to use this, uhm, I'd like since we don't, we don't have any any K or Q people here, just at least mention what Q supplies and of which a subset of that is what K supplies. [28] If I have understood everything right, I'm not a K or Q programmer. And and and so from the documentation I can see from Kx there are basically 4 control structures. There's a thing called condition conditional evaluation. [29] Then there's do if and a while. [30] [31] But the way that they work syntactically is very different from what you have in any of the other. Everything which is, the way they work is, is that, so K has these multi argument function applications that we don't have in J or APL or or BQN and the way it works is that you have the name of the function or symbol for it in this case and then square brackets and then you have semicolon delimited some inputs to to that function. And that means it's not, it's neither a prefix or an infix application, it's a special K construct with that's a bit more similar to the type of function application you see in in generally popular languages. Often they'll use like round parenthesis with with comma delimited arguments, and here it's square bracketed around and semicolons between but that it's pretty much the same, and so the way the conditional works, and that's a dollar sign and that also existing in K If I understand right, is that the first argument is the condition, the test, and then, uhm, it it has more statements, but the first one is for if it's true, and the second one is if it's false and I believe that it can even take a higher number than 0 and 1 and it will go to and and pick out the specific expression or something like that. And and that, that's something, details so that might differ between implementations of K. And then there's do, which is just doing it a certain type number of times. It's kind of like a power operator, or or repeat things. So the first argument is just what it's supposed to do, and then the rest are just expressions that it, it does them in this many times over and over again so so that's iteration like that and then it's got something called if, and if takes a condition. So it's very similar to the conditional thing. Uhm, but then it takes multiple expressions and it just runs out, there's a whole block, so it's kind of like a block thing. If this condition then run all these expressions, otherwise don't. And then there's while, and it's very much just like, it's obvious thing, takes a condition and then a bunch of statements and then it runs through all those statements, as long as the condition is true and then then stops.

00:44:09 [CH]

So I guess the question is now, why?

00:44:10 [ML]

Well the question there is of course, why use BQN?

00:44:12 [CH]

Or or if you're in bQN you don't really have to choose 'cause there's only. There's only one, so.

00:44:18 [AB]

Or APL 2, for that sake, you also don't have a choice. I'd like to insert something actually, why provide these these constructs? Not why why you should use them or not. We'll get to that in a moment, but why provide them from the language implementers' perspective? I think a lot of people will fall back to this ALGOL-y way of doing things. Yes, ideally you should express everything in an array based thing in a single pass and everything. Firstly, some things you just can't. It's impossible, algorithmically speaking, and other times you need to get your work done, right? It's all nice and beautiful or this theoretical thing, but I have a job. It needs to be done and the performance isn't especially important right now or I can't spend a month thinking about a better algorithm for it. I just need the thing to run. And then people would, will reach after these kind of things. Now if you do not provide them as in the good or bad, old APL days or in even in today in APL 2, what do people do instead? [32] They will find solutions they can't just go use a different language necessarily. They will find solutions. And so what you will see in all code is that you will use, they will use the replicate function with a condition, so replicate can take normally you would have one element on the left of replicate for each element on the right. So for example 1 0 2 replicate A B C. It will get you one A no Bs and two Cs if you supply a single element on the left, it will be used for all the elements in right. So one replicate ABC will give you ABC and 2 replicate aBC will give you AABBCC and 0 replicate aBC will give you just an empty character vector and then they will take an APL expression, in quotes, as a character vector, and now you can't do any kind of analysis of your code, and you can't do syntax coloring or anything, because it's just a character literal. They'll stick it on the right of the replicate on the left they'll have their condition, and that will either if the condition is true, it will it will yield an APL expression if the condition is false, it will yield an empty character vector and then they can sit down now for this... Evaluate that they'll throw, execute, or do at that. And and that will conditionally run this expression and it could have the diamond statement separator. You can have a whole bunch of expressions being evaluated one after another. That's horrible, right? That's just terrible, that was but that was what people were reaching for. And even if we say, well, it's not so nice and it kind of bloats the language and it makes it more complex, but the alternative, either you give people proper control structures and Morten Kromberg, the CEO, sorry the CTO of Dyalog has even said that yeah, they are bloated, they are verbose. They are and they have this end while and so on. That's all long spells out and not just braces that are more lightweight. They really stick out. Because it's kind of like another language, kind of like a meta language for the flow. If that's what you, if you need, that kind of flow, yeah, sure, then you use that. But don't go evaluating and have a conditionally emptied out character vector.

00:47:45 [CH]

So wait, what were you trying to do there? Like I understood the replicate. But like what was the initial thing that you were trying to do that wasn't possible with APL?

00:47:54 [ML]

You're trying to conditionally run code, and this was, so the power operator was not in most APL's for a long time. I guess it it first appeared in like some APL's in the early 80s, but I don't know when it was actually.

00:48:11 [AB]

Not every APL has it even. APL 2 doesn't have it.

00:48:14 [ML]

Yeah, so APL probably doesn't.

00:48:16 [AB]

It it possible So what people would do then is they would get the length of the APL expression plus another couple of characters for a diamond statement separator, then reshape that statement to n times its length so you get statement, diamond statement, diamond statement, dollar statement as a giant character vector and then execute that, there you go. That's power N right?

00:48:40 [CH]

So, but what you're saying here right now is basically before we had facilities or glyphs or language features to uh, provide this you know, functionality, this is what they did, but then they introduced the the power operator and now having to replicate a string that is executable at some point by the execute glyph to have a bunch of repetitions of the same piece of code, we don't need to do that anymore, so I guess that the alternate version of the question is, are there some of these keywords or constructs that are necessary in light of like the the best in class, sort of APL's or array languages that exist today.

00:49:26 [BT]

I think, and Adám has come to, i think he's explained it really well, part of the reason these constructs are there is because as you point out, the languages evolve and there's different ways to do this now the point is, if you didn't have the construct, you could see all those same techniques show up in code to do the same thing. You would have people creating, now it sounds crazy to create a string and then make it a zero string or a length of itself string and then evaluate it, that sounds nuts, but you could do it. And if you didn't have a construct that was "if", you might find people did that, and and the idea is, well, it made more sense to use the power. And at the point I've you know, worked with J, we had the power operator, so I always thought, well, that's the way to do it. And that's I can read that it's clear, but the point is, if you if you provide a construct, then, you know people, if they don't know how to do it, they'll fall back to that, and at least it's a little bit more readable than somebody going way off and going oh, I can take a string and I'll make it a zero string, and then I'll evaluate it and it's nothing. Or I'll make it a one string or one length string and then it's a string and evaluate and I'll get a result. The point is, in the languages, you can create those constructs, but if the constructs are available especially I guess in what would be considered maybe boilerplate code, I see this type of language used a lot in the in the definitions and the definitions of the Z locale [33], whether it is,

00:51:02 [ML]

that's kind of J's base locale.

00:51:04 [BT]

That's J's' based locale and you'll see things in there, and even in the startup of the whole language, you'll see a lot of controls structure, but it makes sense, because when you're first encountering language that actually does organize it in a way that if you went back and created this all with power operators and things like that, you would look at it, you go, i I I I kind of understand what's going there, I think it's a facility with the language and in some areas you might go to a control structure just because it makes it a bit more clearer, and I suppose in terms of implementation, if you know that's how you're implementing it, you can optimize for that too.

00:51:43 [AB]

And that brings us to should you or why would you use these things? And and that I say, and sure, for pure algorithm mathematical thing use all the array facilities. I find myself, a lot, writing APL code that is not doing APL, should say, best target area I'm writing lots of utility code and GUI code and where there is a flow and there is, there are often side, parallel side tracks that the flow will take, and then come back together again. And the whole thing with operators and and and even dfn guards, [34] which are just these "if" this condition then terminate with this value or or this expression otherwise continue execution. It doesn't, it's not a good fit there, and the control structures are a perfect fit. They literally my my IDE will indent them and I can see where the various things go, and uh, and I can i can press a key in my in the IDE and jump to the next part of that control structure, which if I had to implement it myself there would be no support for that, so I guess I guess.

00:52:55 [CH]

That's the thing is, I definitely, we, at least when I'm writing code, try to adhere to the functional core imperative shell, like a lot of you know, the guessing game that many textbooks you know, guess a number between 1 and 100 and then you have to implement you know higher or lower, et cetera, et cetera. That is a very very like input-heavy, like low computation type of program that it's nice to have some sort of for loop or while loop. So so that just says, well, you haven't guessed the number, just read in and keep telling the user they're wrong until they're right. But I guess my question is, is like, do you actually need, 'cause so a lot of the things like I never really reach for the control control flow statements. That being said, I haven't written a huge APL application, but like, can you get away? 'Cause like so Marshall hasn't added them like are there, are there ways to 'cause that's back to the like. How do you find the first square greater than 1000? But like you want to do it in an iterative? Like obviously you could...

00:54:00 [ML]

You want to be able to stop early, is the issue.

00:54:01 [AB]

Yeah, like I don't, i don't think, the power operator can do that in in APL and something just...

00:54:06 [CH]

That's how I've seen pieces of code like you mentioned that you can have it with a number, but I've also seen it in the game of life [35] where it just iterates like... So that's something that I don't know yet, so maybe one of you can explain how to do that, but I guess the greater question is, is is there always sort of a way to avoid while loops, or like can you do the imperative shell part with the power operator and with, you know, the APL glyphs or the the J digraphs like, is it possible to write a large scale application that at some layer is ingesting some input or reading stuff and and then at the at the core of it, sure, it's doing all the heavy lifting, matrix multiplication reductions, transforms, etcetera. Or or do you like, sure, it's convenient because we all know what a while loop is, we learned it in Python or whatever, we like, the question is is like can you get away with it and it seems like potentially you can, if,

00:54:56 [ML]

yeah, sort of.

00:54:57 [CH]

If BQN doesn't have it.

00:54:59 [ML]

Well, I mean the way I think about this is that you have you have various programming constructs or styles or paradigms you can use. And they have a certain amount of power to them. There's there's a set of programs that you can write using those, and so quite a lot of mechanisms are just Turing complete. Can write anything at all. But that's that's not necessarily a good thing, because that means when you write a program, it could be anything at all. So often you have a lot more power than you want. It doesn't really guide you to writing nicer programs or faster programs, or or, you know, just just programs that do what you want. Like if you make one mistake, it tends to, it tends to make it easy to write programs that are, say, subtly wrong that are just a little wrong. So one thing that array programming does a lot, and I was pretty strict about this in BQN, is it actually restricts what programs you can write. So the thing that array programming is good at is fixed size loop. So like you've got an each loop over a function or a reduction or a scan. Those all do, and like you can calculate the number of iterations that you do in advance, for each, it's just the size of the array, so it does fixed size stuff. And in BQN actually I was very strict about this. I cut out like the APL power limit thing that Adám mentioned. So that primitives can never express even infinite computations. So BQN's like, pure tacit programming, is actually a very limited kind of computation 'cause you can't even do unbounded loops. [376 You can't say, well, keep going until the user enters an A or whatever. But that also means that you, I mean, most programs are, and you can actually see this pretty easily by array programming, and finding that in most of your programs, you very rarely have to step outside of this pure tacit paradigm. Or you might add functions or stuff, but but not in a way that fundamentally expands the space of things that you can do. So in fact most problems fit into this space of array programming, which is a really cool thing about array programming is that it's at the same time, it's a very limited style, but it's also very powerful in that it fits most of the things that you want to do. And then you can expand that some... And control structures, i mean they are Turing complete, but in in some sense they're more constrained. You have to work more to express anything in them compared to things like GOTO and so at the at the very end, there's a level of gOTO and recursion that really you can express anything and these are the ones that lead you into spaghetti code because they're so so simple and basic that you just write oh go here, go there, and a similar thing happens with recursion. [37] Maybe it's more manageable, but it's not really that different. You just say go here, go there and you know you you can't narrow down the space programs easily to the ones you actually want to write as opposed to the ones you can write. So what I did in BQN was I said I decided well I'm going to have this limited array language. And then when you step outside of that, usually your tool is recursion, and that gives you access to the broader world of functions, but you have this stack, so you can pick the appropriate level in the stack for your problem. So and mostly it is possible to stay in the array world. Other than that, i mean I, I don't have this level in between that control structures give you, so that's almost an issue and it does come up where you say, well, a control structure would be a lot nicer for this particular problem, but instead I have to step out and use recursion or I have to use, we do have one, uh, thing that's a system operator in CBQN, and that does a, that is, basically APL power limit that does a while loop that says until this condition is met, do this thing. [38] And it's the same like it it gives you unbounded programming, but that might not really be what you want so. I mean, yeah, it works, but at the same time it does feel like there's a. There's a level in between that's missing and I wasn't willing to go with at least so far I haven't wanted to introduce all the complexity of control structures. But it's a hard choice.

00:59:52 [AB]

Have you tried writing, or has has anybody written in an interactive full stack with frontend program in BQN? And 'cause that that's where it comes in a lot, right? I have the user is interacting with the GUI, on the, behind the covers of that GUI there's the program is running around in a certain flow, popping up a question to the user. Users answering something going down a certain route, asking something coming as until the everything is done, and doing that using recursion. It sounds painful to me to say the least.

01:00:31 [ML]

Yeah, well, I think you'd have an outer, an outer infinite loop. And then that makes things a lot easier. One of the things I did notice is that, yeah, the, when you want a control structure and don't have it, it's tough. The absolute number of loops where this happens is very small, like even for programs where you'd think of them as really as imperative in nature, it's usually just one loop, maybe two. So I mean, yeah, in that sense it's worse that you have to for these particular loops go into the broader range of recursion or the infinite while loop, but at the same time it happens very few times for the program, and that's also one of the problems I have in developing other solutions is that I have so few examples of these that I can't really figure out what works best in most cases 'cause I just haven't seen it that much.

01:01:30 [AB]

So that's where APL and J just provide a fairly large vocabulary of traditional control structures, and then people will use whatever they want. Clearly you don't need both a repeat and a while loop, but yeah, why not? Just give them all of that.

01:01:48 [ML]

Yeah, I mean so they used this whole system that was developed in ALGOL when when that was really the system that you were going to use, you didn't have this array or you could even say function oriented programming where you're mapping functions or doing reductions and things like that, function-level programming. They didn't have this level, so they developed a very expansive control structure vocabulary because that was how you programmed. And then with BQN, I'm saying, well, I i mean, yeah, this level has its uses, but do we really need to introduce all this complexity to the language in order to have access to this level? And so far I'm sticking with no and I i mean, I'm still hoping to find like the predicate was one that came in pretty late and that helps with a lot of things because you can do, that gives you basically access to an "if else" kind of thing. So I'm still hoping, you know, to find more simple additions to the language that I can do, that give you, that make it more expressive in some way, but I don't feel like I want that to i want to bring in all without going..

01:03:00 [AB]

You you mentioned that just using primitives in BQN you you can't really write unbounded, uh, code, you can write infinite recursion I suppose, and...

01:03:12 [ML]

Well, recursion doesn't use primitives, it uses blocks, so

01:03:14 [AB]

right, OK?

01:03:16 [ML]

So tacit programming is programming without blocks, and that is actually, it's not Turing complete. You can't express infinite computations this way.

01:03:25 [AB]

I suppose in in in the Dyalog APL, the power operator does provide you with completion. [39]

01:03:33 [ML]

Yeah, 'cause you've got this power, although I think that's a pretty like, if you want to be doing general purpose tacit programming, power limit is a really awkward foundation.

01:03:43 [AB]

Oh yeah, but what happens if you do use the power operator in BQN and then use infinity? Will not just run forever?

01:03:51 [ML]

Infinity specifically, it will give you an error. A very large number it will run for a long time. The issue that you'll have then is that it will not actually stop, so you've expressed a very long finite loop and you have no way to get out.

01:04:05 [AB]

Oh OK, yeah because J has something like that where you can run an infinite loop and then there's a special symbol to get out.

01:04:12 [BT]

Yeah what what J does is if you put infinity into it, it will run until there is no change to its result. [40] So it's comparing the previous result to the current result, and if there's no difference between them, then it stops and so that way you can actually nest your power operators and you can, and you, conor's original question, how would you multiply or or double all the all the numbers in a in a list until they get over 1000? Well, that's how you do it. You've essentially look at each item on its own. Run each is its own little separate loop, and each of them go up to the point where they get over 1000, and that's an inner loop and the outer loop is, run it till there's no change. Well, if you say over 1000 is your your inner loop control on your power operator, then when it goes over 1000 it's going to be 0 and then nothing happens. And then it stops. And because you're doing rank rank zero, each item will be on its own, or each atom will be on its own. So you could get all your atoms would run their own separate little loops, and that's how it uses the infinity is just, it just looks for no change, and when there's no change it stops. So you use that nesting and and I i think that's actually a really powerful little construct that that I use fairly often.

01:05:31 [AB]

That's equivalent to power match in in APL.

01:05:35 [ML]

I guess, though, I mean I used it when I was a J programmer because you kind of have to, but I feel like, I feel like it is tangling things together, sort of. I mean same complaint as APL, but it doesn't feel really foundational. 'cause then you have to be managing whether each step in the loop returns the same value as the previous one or not, so I mean like if you've got a random loop, if you want to, generate things randomly and keep going until until you fit a certain condition, then if you generate the same thing twice, then it'll stop even though you're not really done. And also you have to, if there's no natural way to, if it doesn't naturally like reach a limit and converge, then you have to add this extra layer of asking if i'm done. Adding another power operator so...

01:06:30 [BT]

You're definitely using the concept of a limit to create your your loop, and if you're not working with something that would have a limit, like a random input, then I I would agree you really can't use it that way and then rely on it.

01:06:42 [ML]

Yeah, I mean so I feel like that's just not the... I mean, it's definitely useful for mathematical stuff. The the other thing that it felt to me was like a lot of the times you want to tweak little details, and that's a warning sign to me that something doesn't really belong as a primitive in BQN if you would say, well, this thing is great, but I want it to work a little differently. That should really be a function that you define so that you can go and write your own version of it instead of being a primitive. Where the language is telling you this is the function you want because if it might not be, then the language shouldn't tell you that.

01:07:22 [AB]

So that happens with the power operator in APL. So and it runs, so what it does is similar to the power infinity in J. It takes 2 consecutive results of applying the function over and over again and compares them. We would call it compare, but I mean really applies them to the right operand as arguments. And then that right operand has to return a Boolean true or false whether or not to the condition that's been fulfilled. But again, you it'd be in coding and they can use a variable from outside as well to decide that or even do a whole bunch of stuff and ask the user if we're done yet or something like that. And so the common thing is to do power match. Problem is if you do some kind of condition like say I want to to square it or whatever it was, double it until it's larger than 1000... What if it's already larger than 1000 when we start? [41] Yes, it will stop, but it will stop too late. Because in order to compare 2 consecutive result, it must apply at least once so they can compare the initial value to the to the next value, and by that time we've already gone too far and you can't backtrack. So then you have to build some ugly construct where like apply this function until this condition if the condition isn't already fulfilled, so you can do that using two power operators. It's just a bit ugly to the...

01:08:49 [ML]

Yeah, well, but APL has the has the sort of issue that the the power limit thing takes up the case of a function operand on the right so in J, when you have a function operand on the right, that's just applied to the arguments and it tells you then how many times it runs, so it's applied once at the beginning. And that's it. But in APL, the function does this checking everytime thing, so you can't really use it directly as, like, you can't have a function to [inaudible] arguments, you could have, you could compute a zero or one and stick that on, it... But you can't...

01:09:30 [AB]

We have to put it in parenthesis and then separate it... Yeah, it's it's a bit ugly, but it can be done. But then again I mean, in such a case i can use control structures and it's not going to run slower because I'm using control structures because the other approach is not array based either. It's also loop. And and I don't think, uh, it's it's worth forcing people into something that's just harder to read where where, especially if you come from other languages that use keywords like that, it's immediately readable and you can see the structure and and and and another another case with such a loopy thing that I often find, let's say I'm I'm processing a huge list of things and so we'll reach for the the for the each operator or rank something. [42] And that might look like you are avoiding a loop. But of course internally it is just a normal loop. Now, let's say that I'm doing a lot of processing on each element, it could be writing files to the disk or whatever. Midway I hit an issue, execution stops, and so APL, especially of these various program languages, very much try to make the experience of interactive programming pleasant, and so the interpreter will stop midway, and then you can modify your code even and continue execution. And there, having it in an explicit loop, a for loop, for example, for me at least, generally is it's much more pleasant to work with than being suspended inside an invisible loop. I can see what what exactly it is I'm looping over. It's, everything is immediately available. It doesn't become a question that's I'm applying an anonymous function. Uhm on each, so what does it even mean to edit that function in the middle of the loop? Now what gets applied to the next element? 'cause we've already kind of created this derived function of my function each and we're in the middle of applying it. Having a normal for loop, I can see exactly what state I'm in, uhm and and continue or back up and start the loop over again. Or just skip out of it and continue and and and the same thing is, if if I have control flow that goes wrong and I'm I'm tracing through my code, then I can fix stuff and manually move the the active line in the debugger to the right place and continue from there. If I was constructing everything with derived functions based on operators that would be completely impossible. And so I find that, yeah sure, maybe it's not as pure, maybe it is a bit bloated. Maybe it is a bit ugly. But sometimes I just need to get stuff done and it's really useful for that.

01:12:26 [CH]

So we're over by a few minutes. But I've been trying to figure out 'cause, so we've talked about power match. And we mentioned in BQN power limit and also in J power infinity and the documentation and the Dyalog aPL site mentions that when power is used with either match or equal, it's sometimes referred to as fixed point, which those from mathematics background or avid readers might have come across that. So I guess maybe maybe this is a bad way to end the episode, but I haven't been able to figure this out and I from my understanding, is that this actually this power match thing or power limit is actually quite, no pun intended, limited, uhm so say, let's see if there's a way to spell this in any of the array language, if I want to start with the number 2 to the power of two repeatedly until it goes above 1000. [43] Is that actually possible? 'cause from my brief, you know, I've been mostly engaged in this podcast, so I haven't really, really been able to deep dive, but from what I can tell is you're limited to the case where your right argument is the thing that's being sort of inserted repetitively? So if you're trying to use that as a part of a predicate check, that's not something that's a fixed point like, you know, greater than 1000. I don't actually want the thousand to be involved in my repetitively squaring of the 2, but so so I basically want to do 2 squared is 4. How would you spell it?

01:14:05 [AB]

I can tell you how you would spell it. So first I would do the square which I would write as self multiplication. So multiplication, selfie multiplication, commute power operator. Then I would write a little dfn. The dfn would have an alpha, that's the left argument, greater than 1000, close brace and then 2. So this says apply the self multiplication over and over again until the current value is greater than 1000. That's it 65,536.

01:14:47 [CH]

So this was multiplied selfie, power operator brace alpha greater than 1000, end brace, 2? 'cause I got 65,000.

01:15:00 [ML]

I think that's right. That's two to the 16, right so? So the previous one was two to the 8, which is yeah.

01:15:09 [AB]

That would be less than 1000. Did you actually mean squaring, or do you actually or you meant multiplied by two?

01:15:20 [CH]

Yeah, maybe I meant multiplied by two. [44]

01:15:21 [AB]

OK, so in that case you would write that two times power operator open brace alpha greater than 1000 close brace 2. It just says multiplied by two and then gives you 1024.

01:15:33 [CH]

Right, right?

01:15:34 [AB]

If that's what you meant.

01:15:35 [CH]

So Adám is doubly correct, and Conor is singularly wrong. That was my mistake there.

01:15:42 [AB]

So I think that's pretty clean.

01:15:44 [CH]

And this this is actually what I would have expected something like this.

01:15:50 [AB]

The problem here is what happens if you, if instead of 2 you write 1001. Then it should return 1001, but it doesn't. It returns 2002 because it it starts by applying the functions so that it can even see if we reach it or not.

01:16:08 [CH]

So what is, i mean, that is an issue, but it's much it's a much smaller issue than not being able to write this at all.

01:16:14 [ML]

Is it, though?

01:16:15 [CH]

Uh, I mean for certain problems.

01:16:18 [ML]

I mean it, it's it's kind of dangerous in a language if if you write something and it's wrong in a few cases 'cause then you have a program, i mean 'cause then you ship it and then it breaks so it may or may not be better.

01:16:35 [CH]

That's actually, that's a good point. Understanding this though, there's no omega in this dfn. So what's actually happening, the, what, the the fact that there's only an alpha like what is what's going on here?

01:16:49 [AB]

So what I would suggest to you too is insert into the right inside the brace the open brace insert quad gets alpha omega diamond. [45] So now every time around the loop it print its left argument and its right argument. So there's a right argument, but you're just not, we're just not using it, and so what you see here is every time around the loop it gets the next value as left argument and the current value, well, a previous value if you want, as right argument. And so by only looking at the left argument, we can stop on time. So basically what it does is it runs this until whether condition, then returns the next value. So we want to to to write our condition in terms of the next value. That's the one that will be returned.

01:17:42 [CH]

What is quad gets? I typed that G. Is it capital G?

01:17:46 [AB]

But no, no quad left arrow quad assign.

01:17:51 [CH]

I did not know that that you colloquially refer to that as gets.

01:17:54 [AB]

Sorry so.

01:17:56 [CH]

Oh, that's nice.

01:17:57 [AB]

So then now it prints out every time around the loop, it prints out what its left and right arguments are so you can inspect what's going on, as a side effect of running, but it actually the return from the function is of course alpha greater than 1000, and so we can see what the function sees every time around the loop. And yes, there is this edge condition, but it's not, it's not if you're aware of how power works, I don't think it has ever happened to me that I actually fell for this edge condition, it's just something you have to be aware of.

01:18:26 [ML]

Well so, two things happen you you you didn't, two things have to happen for it to break. You have to write the code that's broken and then you have to run it in a case where it's, where it needs to do 0 iterations, so it may be the case that you have fallen for it and just didn't didn't hit that case in real life and so you have bugs still sitting out there.

01:18:51 [AB]

Yeah, that's true, but the...

01:18:53 [BT]

Ghosts in the machine waiting to waiting to snatch you, yeah.

01:18:56 [AB]

Yeah, it's true, and maybe it could have been defined in such a way that it would start off by applying to the original argument, twice but it kind of needs a right and left argument, so it's a little bit, if you if you if it say apply if you use the same argument that's left and right argument, then you're trying to do a power limit. Then it would immediately stop because the argument is already equal to itself. [46] If it didn't give it a left argument to begin with, then you have start off with a value error. You're going to have to give you the default value... It can get kind of ugly as well.

01:19:29 [BT]

Yeah, to me it's it's an affordance you you, with that infinity in J you get a chance to use that power, no pun intended, in a way that's kind of useful in a lot of cases, but as you point out, Marshall, there can be cases where it comes back to bite you. You have to be aware of that, but I think that's true of a lot of things that you use the power of or the functionality of it can work against you as if you get into the right situation or the wrong situation, it'll blow up in your face, so you have to be aware of those things, but generally I think the functionality of being able to use the infinite that way to compare the last two iterations, i find really, really useful.

01:20:10 [ML]

Yeah, I mean so it's a, it's a pretty messy world when you get into all these programs that they want to stop at some, for some reason, I mean, there are all sorts of reasons you would want to stop. So I mean, that's one thing I'm not claiming i'm better 'cause I don't feel like I have a good handle on what programming constructs you really should be using here. I think there should be better ones, I think they're probably out there... But yeah, it's it's a lot messier than designing array primitives, I feel.

01:20:44 [BT]

Well, I would say the one thing if you took control structures out of the language, then you really are enforcing array programmers would have to use array programming.

01:20:55 [ML]

Uhm, well no, they generally have to use functional programming.

01:20:58 [BT]

Or functional programming. You would use that kind of mindset.

01:21:02 [ML]

And there is actually, so in CBQN, at least we have this this system function dot underscore while underscore. So that's a 2 modifier. That's what the underscores say, and that says, it well, I guess it's most similar to J's power condition power infinity. So it just repeats the function on the left as long as the function on the right holds. And that way with this system function you get the word that explains what it does, so that's kind of nice because in the end I just don't feel like this functionality is too related to the idea of repeating a function a fixed number of times, because you have this extra thing that you're putting on. Also, you want to test whether this condition holds.

01:21:50 [CH]

Yeah, this is definitely definitely something I will we will leave, maybe maybe a couple different links, not just to the example that we talked about at the end, 'cause I've just been thinking about that since since I've had the code on my screen, but it's definitely food for thought. 'cause I agree with Marshall that I like at the moment I'm happy now that I know how to solve my leetCode problems that require me to like eagerly do some kind of take while where you know half of those problems you can do some sort of pre-computation to figure out the number of times you need to do something, and so you can, you can fit it more into the array model, but there are problems where that becomes a lot trickier or it's unrealistic to do that pre-computation, because then you're just doing things twice. And so yeah, we'll leave links to like little TryAPL snippets for folks that are interested in this, and maybe even we'll do some J playground ones. And for folks that are interested in this sort of power match or power limit or power infinity idea, 'cause it's definitely. I mean, I wouldn't put it up there right next to dyadic transpose and rank, but I definitely think that this is stuff out on a beginner beginner topic and like I said I've made it whatever two years or however many however much time I've been studying the array languages without actually figuring out how to do this. So yeah, we'll leave links. I'm not sure if Adám you're about to say something things we want to close off on, tell the listener.

01:23:14 [AB]

Just as an example of of this pre computing in the example you gave here with multiple multiply by or double until greater than 1024. In case it's not obvious to to the listeners, you could use logarithms, so the the two logarithm of, [47]

01:23:32 [ML]

yeah yeah, the the APL solution is ceiling under log base 2.

01:23:38 [AB]

Yeah, you could you could do like that or or you could compute the number of times in advance, right the way you do it. You can get the two logarithm of two when 1000 and subtracted from each other and and round that up. That gives you the the number of times you will have to multiply by two, and you could do it like that. Or you can do the math, obviously with logarithms and so on and powers. And that's preferable, but not in every case you can do that, and then something we will even link to is is adding that extra power operator to do the initial check so that you don't end up doing one time too much in in the edge condition it's, uh, it's not the most beautiful, but it works and you can factor out the condition so that it looks a bit better.

01:24:27 [CH]

And yeah, that pointing pointing out the ceiling logarithm solution is actually, a great example, not necessarily 'cause that's more, just math, but like straight up I think I was on the APL farm discord solving something in BQN and then Marshall pointed out, you know you can just there's log like that's a that's a thing and I was like Oh yeah, I forgot about that from math like I think I was literally doing like the inverse of expo– like and he was like what? This isn't even APL or BQN at this point. This is just math 101, but even though that might be a sort of a convoluted example, there are ways of like trying to bend your brain differently to solve things the array way, and I think that's what we should all sort of be chasing, is a lot of the times we think oh, you know I need i need a eager while or something like that and one really is you just, you need to explore the different ways to solve these problems and a lot of times, not all the times, but some of the times, for sure, there is a nicer sort of array solution that until you've seen sort of that, that kind of solution, you're not going to immediately think about it. Any other things we want to say to the listener?

01:25:32 [BT]

Oh maybe get in touch with us if you like. You can get touch with us. You can reach us at contact@arraycast.com. [48] Alright, and we look forward to that. We've had lots of responses, especially from our 30th episode because we were asking for that. A lot of people have responded to it and actually this topic that we're working on right now came and I I went back looking for it, i'm pretty sure it came from a Reddit thread, but I haven't been able to find it, but somebody did suggest this to to control the controls structures would be a good thing to look at across the different languages, and I don't know who that person is, but thank you for suggesting that because we did make that the topic of this show.

01:26:08 [AB]

And if there's any subject that you would like to get you discussed in the context of array programming, then feel free to reach out to us. We do read carefully every single e-mail.

01:26:19 [BT]

And and there has been a lot of feedback saying people really enjoyed the more dense transpose [49] and and tacit programming discussions. That's interesting, but, but the fans of of that group are really fans. They really enjoy that kind of discussion. I guess you don't get it anywhere else, so that would be why they're most interested in it.

01:26:40 [CH]

Yeah, I feel like there's some like moral hazard, or you know, there's one of those cognitive bias, where we're self selecting for the folks that, you know, like that kind of and you know, maybe it's because.

01:26:53 [ML]

Those are the people most likely to email.

01:26:54 [CH]

Yeah, they made it to episode 30 because we scared him away in episode seven with those those conversations that all the feedback we're getting at this point is just the people that hung on.

01:27:04 [ML]

So if you're one of those people, if you're a person who's less likely to e-mail us, please e-mail us, right?

01:27:12 [BT]

I'm not sure if there's a logical fallacy in that, but we're going to continue to do interviews, and those are amongst our most popular episodes by the number of downloads, so don't feel like we're going to go romping off into the the minutia of the of the array languages 'cause we're going, there's lots of great stories to tell about people who are involved with the array languages, and those tend to be less technical.

01:27:36 [CH]

They'll be, they'll be a mix for sure. I enjoy both the interviews and the deep dives as well, so. But I think with that, we will say happy array programming programming.

01:27:45 [All]

Happy array programming.