Transcript
Transcript prepared by
Sanjay Cherian, Igor Kim, and Bob Therriault
00:00:00 [Madeline Vergani]
Pretty much from day one I wanted to make my own APL because I found it really interesting that unlike Python, there's multiple implementations, right? There's Python and PyPy and whatever, except for some internal things. So you'll do the same thing. They all have the same functions. And APL is different because every Dyalog, every implementation has its own features, right? And that was really interesting to me.
00:00:16 [Music]
00:00:31 [Conor Hoekstra]
Welcome to episode 88 of Arraycast. I'm your host, Conor. And today with us, we have three panelists and a special guest who we will get to introducing in a very short period of time because we have no announcements today. But first, we're going to go around and do brief introductions. We'll start with Bob, then go to Adám and finish with Marshall..
00:00:49 [Bob Therriault]
I'm Bob Therriault. I am fresh off Iverson College, as you've probably heard. And I am a J enthusiast.
00:00:57 [Adám Brudzewsky]
I'm Adám Brudzewsky. I'm also fresh off Iverson College, which you've probably also heard. And I do APL for a living.
00:01:04 [Marshall Lochbaum]
I'm Marshall Lochbaum. I know you've missed me desperately in the past four weeks. I've worked with J and Dyalog in the past. I work with BQN and Singeli now, which are my languages.
00:01:15 [CH]
And as mentioned before, my name is Conor. I am a massive Array Language enthusiast fan and super excited about our guest and our conversation today because we have alluded to this language and the author of this language several times, and we're having her on. Her name is Madeline Vergani, and she is the author of tinyapl is, [01] I believe, the, you know, correct pronunciation of the language. I think we mentioned this language at the end of two episodes ago, maybe it might have been 86 or 85. And there are actually it wasn't so much on the this podcast. I'm getting my podcast confused. It was on Tacit Talk when I was interviewing Adám on episode four. I think there was a solid 20 or 30 minutes that we ended up turning a 90 minute episode into a two hour episode because Adám mentioned tinyapl and how it was going to get the modifier trains from the J language, which I was like, are you kidding me? There is, first of all, there's another APL out there. I think tinyapl was on my radar, but I had no idea about the combinators that were coming in tinyapl. I'm going to stop talking, throw it over to Madeline. Maybe you can introduce yourselves, tell us your story and path to the Array Languages, and then we're going to dive into everything tinyapl. And we're going to talk about this new APL that is breaking news. Another APL is out there.
00:02:40 [MV]
Hi, my name is Madeline. I got into Array Languages sometime during the pandemic. I'm not sure how. My first time I found out about them was when I was quite young. I was maybe like 12. And I was using the SNAP! programming language, which is a block based programming language, kind of like Scratch. But it's better in my opinion, because it has first class functions and first class lists and a lot of other useful features. And one of the things is that it has a standard library, and inside the standard library is a module called APL Primitives. And inside it, there are some functions that had like a weird unique glyph in yellow and then an English name of what they do. But I didn't really think much about that when I found them. I just kept in the back of my mind. I thought, that's interesting, maybe I'll look about it later. Then we jumped a few years later. Now it's during the pandemic again. And I was looking through Unicode glyphs, as one does, I guess. And yeah, so I saw in the Miscellaneous Technical, there was an old block about some glyphs that were called APL Functional Symbol and then a name. So I remembered about the other time I saw about APL. I thought, are they the same thing? So I looked it up on Wikipedia. And well, it wasn't really... It showed me a bit about array programming, but it wasn't really much informative. So I went to YouTube and I found Conor's videos. And my first experience with APL and with array programming language in general was Tacit. So I learned Tacit programming before I learned defuns, I think. So then I downloaded RIDE. I started playing with it. I never actually followed like a tutorial or something. Most of it was I learned to experience. And pretty much from day one, I wanted to make my own APL because I found this really interesting that I like Python. There's multiple implementations, right? There's Python and PyPy and whatever, except for some internal things. So you'll do the same thing. They all have the same functions. And APL is different because every Dyalog, every implementation has its own features, right? And that was really interesting to me. So I thought I wanted to make my own. And I had a draft of a very big project that I think I called Maple for like modern APL. It wasn't really modern. It was just full of features that didn't fit the array paradigm. I never got to implementing it because I didn't know how to parse it was the main problem. So this project didn't go anywhere. But then, this is August last year, and I started thinking of doing an array programming language again. This time I wanted to start with something I knew I could do. And that was like making a library for a normal language that had the APL-like arrays. And I chose Haskel to do that because I had recently learned it and I found it really interesting. And then I also decided to write a series of blog posts about the development. Well, mostly just because I just made the blog, so I wanted to put some stuff inside it. But also because I thought that writing about the things I was doing would help me reason about the things I'm doing. And I had to explain what I'm doing, right? So it had to make sense to myself. Yeah, so I started doing that. I think the first blog post was just about implementing arrays, then I implemented functions, I implemented some primitives. And then I tried actually doing the parsing again because I had asked around and I was pointed to the Bunda-Gerth parsing paper. And I kind of understood it, but not completely. So I had an implementation of it. I released it publicly for people to try around and find mistakes and whatever. And then I abandoned it for like from November to March. I didn't do anything because I couldn't figure out some bits of parsing. I can't remember what now, but something didn't work and I lost motivation because of this. And also I wasn't really feeling that well mentally at the time. So there were some other issues, but eventually I got back my motivation. So I started again. I figured out the parser and from then I just kept implementing primitives and features and it's coming off real strong. And especially in the last year, month or so, like all of August, I implemented over 45 primitives. So a lot of them were easy, but some of them were not. And it was really nice to see that now tinyapl is a usable language. Until August, it didn't have catenate, right? It's a very fundamental primitive. You just couldn't join to arrays. Yeah, now you can and there's a transpose and abstract product and stuff like that. So you can do a lot of things now. Because I learned Asyp server in my APL path, I guess, I had a strong focus on combinators and eventually trains. Combinators were one of the first thing I added. I think very early in the design, I planned for a combinator, it's called mirror, which I didn't invent myself, but I came up with independently. And basically it does, it calls one function with both arguments and the same function with both arguments, but swapped. And then it was another function with the two results. It's useful sometimes when you want to like pre-process both arguments in a way that depends on both of the arguments. So you like, right at the top, whatever, and then you put it as right operands mirror. Later, I came up with even more combinators, left fork and right fork. Maybe I'll talk about them later. And then I also added trains recently, which are very interesting, I think, and I can talk about them later.
00:08:12 [CH]
All right. I already have so many questions. So, I mean, we'll start with mirror. I mean, we're going to dive right into it because you've like baited me with, you know, all your combinator talk of left fork, right fork, mirror. So I recall finding my way to the tinyapl.rubenberg.com/docs/info/combinators. So is the mirror that you just mentioned, is it there? Because this is kind of a comparison page to the Uiua has a combinators page with little diagrams in place as well. Does this list all of them or is there a better place to go to look at the full set of combinators?
00:08:55 [MV]
No, that's all of them except for left-next and right-end, which I implemented like this morning or even this afternoon, I can't remember. So yeah, apart from those two, they're all there. Yes. The mirror is the third to last. It's the minus with the dots above.
00:09:11 [CH]
Oh, okay. So it's, yeah, it's a minus sign with the double diarisis and it's known as the parrot. Yes. Interesting. And this is the P combinator. And so you're saying it's basically a dyadic fork.
00:09:28 [MV]
Yes. The fork, G commuter, SG, basically.
00:09:33 [CH]
Yeah. So it's kind of actually a combination between the psi combinator, aka what APL and BQM call over and the dyadic fork. So each of the tines is the same function and you just swap them.
00:09:47 [AB]
It's not surely not over.
00:09:49 [MV]
But yeah, it's like over, but.
00:09:51 [AB]
Over preprocesses each document separately. This one takes both.
00:09:55 [AB]
Documents.
00:09:55 [ML]
Well, it's a bit like over, except the missing argument is also included.
00:10:00 [AB]
Yeah. No, that's true. Yeah. It's like over. But the preprocessing function gets the other argument as left argument.
00:10:05 [CH]
The reason I say it's like over is because in over, aka the psi combinator, you're applying the unary function twice where, and in this case you're applying the function G twice just to...
00:10:15 [ML]
Yeah. And actually you can easily get over because you do F mirror parentheses G atop right. And then that's either over if it's right or left, I'm not sure.
00:10:27 [AB]
If G is a function that takes a left argument, but doesn't actually use it, then it's equivalent to over.
00:10:33 [CH]
Yeah. Is this something that came up or are you just doing this for kicks? Because you really liked the dyadic fork and one time you needed to swap the arguments. Because this, like it makes sense when you walk through it. But off the top of my head, I can't think of...
00:10:49 [AB]
I've got a few instances on APLCart that need it.
00:10:54 [CH]
Was there a motive motivating case, Madeline?
00:10:55 [MV]
Yes. When I was doing the APL competition or challenge, [02] I'm sure it was called last year, there was one challenge where you had to like process only the part of the array that you got to array two vectors and you had to only look at the length of the shortest ones for both. So it's a cost one to the length of the shortest one. And I wanted to have mirror there because what I wanted to do was a minimum over length take right. And then for the other one, you shape the same thing but with left. And in the middle, you have the actual function of solve challenge. I think I actually submitted a version that as an operator and it had mirror defines it as a DOP inside. So I had the function and then the DOP and then minimum over a tally take right. I wanted a useful operation that you needed every time you want to process one of the argument but based on something that depends on both. I use it also to promote or demote to the correct rank when the two arrays have different rank. And I think I couldn't find any other use though. So it's cutting to the length and cutting to the rank basically.
00:12:10 [CH]
Basically, so yeah, there's a bunch of bunch of motivating use cases here.
00:12:15 [ML]
Another one would be the symmetric difference of sets where you want all the elements that are in exactly one of the two sets. So you take the difference like A minus B and then concatenate that to B minus A.
00:12:25 [CH]
Interesting, yeah. Well, my next question is the left and right forks but maybe we'll pause there because I've totally, you know, I totally, like I said, I got, you know, what is it, nerd sniped or baited into asking these questions. Maybe we should take a step back and talk at a high level about the differences because if you go, we'll make sure that we link this combinators documentation page in the show notes, obviously. But up until I think the Bluebird or the B Combinator, this looks I think similar or if not identical to Dyalog APL, but then you hit the Q which is a different combinator and then there's a slight differences and then it looks like you've even borrowed the symbols from BQN for monadic and dyadic before and after. So maybe even outside the combinator space, maybe let's like summarize or have you summarize the sort of major differences between Dyalog APL and, you know, ideas that you potentially borrowed from the other array languages like BQN and we'll go from there.
00:13:36 [MV]
Well, the main difference is definitely that parsing tinyapl is context free. Like for example, in Dyalog, if I write A space B space C, you can parse in a lot of different ways. Like if there are other ways, it parses a strand, but if B is a function, it's like a dyad application. If A and B are both functions, it's two more applications. To resolve that, basically you have to, while parsing, check or at least while binding and making a syntax tree, you have to look at the names that are defined and change the way you parse based on that. I didn't really like that. I thought it was too complex and BQN already doesn't do this. They have basically a case in convention, a naming convention for the names of functions and arrays, etc. So an array is lowercase, first letter is lowercase, a function first letter is uppercase, a first letter is an underscore and in a conjunction the first letter and last letter are both underscores. That's exactly the same as in BQN except that the four classes are completely distinct. Like I can have a lowercase array and an A uppercase function that are unrelated. In BQN you can't because they refer to the same variable but in different contexts, I guess. So I took that and I extended that to other constructs. For example, dfns, you write the same way you do in Dyalog, but if you want to write the adverb, I guess you can call it, you have to prefix the opening brace with an underscore. If you want to write a deconjunction, you prefix and suffix with an underscore. This is so that binding is easier. You can definitely find out whether it's a modifier or not by looking inside the code and seeing if it refers to the alpha, alpha, etc. But I found that to be too complex. I want the code to be parsable by the reader at first glance. I don't want you to look inside the defense and see, okay, it's an adverb. So I go back and redo all my parsing knowing that's an adverb. I want it to be clear up front. I also don't have stranding. I prefer array notation to that. Recently I also added ties, what I call tie syntax, which is basically the joiner thing from BQN. I also have first class functions and operate modifiers. So yeah, they're useful sometimes. And also trains are not invisible like they are in other languages. They use a specific syntax that you can see that it means a train. I also have modified trains from J. I don't have last axis versions of the replicate or reduce or rotate, etc. I only have the first axis versions. I think those are the main differences.
00:16:29 [CH]
Interesting. So you borrowed the casing from BQN minus the fact that you can overload them to, whereas BQN doesn't allow that. Did you borrow the syntax for forks? Because I think I saw that if I'm not mistaken. Is it just a coincidence or did you borrow that from KAP? Because I see you've used the same glyphs for that.
00:16:52 [MV]
It's inspired by KAP, but in KAP it's a syntax thing. Whereas in tinyapl, they're normal operators. So the binding strategy is different. Forks operators are not really nice to use because most of the time you want a long function, I guess, in the middle time and the right time and another composition. And you can't do that because it parses wrong. So you have to parenthesis. And that's why I introduced train syntax, which is nicer to do that, I think.
00:17:23 [CH]
So you said that there's the prefix and suffix underscore. Does that mean that you don't have to use the double alpha and double omega? You can just use a single one because you still need a way to disambiguate between the, I guess you call them operands instead of the function arguments versus the array arguments.
00:17:48 [MV]
No, you still use them, but in Dyalog, the way to know whether a diff is a function or an operator is that you look inside and you see if there is alpha, alpha, omega, omega. And in that case, it becomes an operator. You don't have to refer to them in tinyapl. Maybe I want to write an alphabet that ignores the operand, and I can do that without needing weird syntax like you do in Dyalog where you write omega, diamond, alpha, alpha to make it an alphabet. The alpha is ignored because omega returns. But it turns it into an alphabet. You don't need to do that. You can just do underscore, open brace, omega, close brace, and that becomes an alphabet. Also, because each name only has one class thing, you have to distinguish between function and array operands. Array operands use double alpha and double omega, and function operands use double underscore alpha and double underscore omega.
00:18:44 [CH]
Okay. And last question, and then I'll throw it over to Marshall and Adám and Bob because I feel like I'm just rapid firing. In KAP, because you don't have the three train, you end up with this ability to compose unary functions and have it fall out naturally. But that's also because it does have the two train. Did you do away with all train programming? Obviously, you mentioned that you have modifier train, so probably not. But is it just that there's no three train and you still have the two train similar to Dyalog and BQN, or are there no two trains either?
00:19:20 [MV]
No, there's both three trains and two trains, but they are explicit. I can talk about the trains work. Basically, instead of having visible syntax, you put three functions together, or n functions together. You do open, white, round parentheses, which is really like parentheses, but the two ends are connected by a straight line. It looks a bit like a half moon. So it's like open moon, and then you put the function separated by diamonds, and then close moon, and that's a train. It has four advantages, I would say, over the syntax of all the other array languages used. The first one is an advantage just for me, which is much easier parsing. I tried to make invisible train things work. I couldn't figure it out. But that's not really an advantage to use. The advantages to the user are, first of all, the modified trains are really easy, like both for me to implement them, but also to write them. Because if you want to make a train that results in an adverb, you just do underscore and then open moon, just like for a dfns. If you want to make a train that returns a conjunction, you do underscore, open moon, close moon, underscore, of course. The third advantage is what I call any position in - sorry, any class in any position. So basically, in Dyalog, there are two types of forks. There's the FGH fork, which is like function, function, function, as the normal fork. And there's also an AGH fork, which is array function, function. And you can interpret it two ways. The first way is to think that the array is an implicit operand to a constant, so like tilde dot. So this train becomes a constant GH, and that's parsed as a normal fork. You can also think about it as the array being bounded to the function, so it becomes A bind G adopt H. What you can't do, though, is an FGA train, so function, function, array. It's expected to just bind the argument the other way, but it doesn't work because FGA is parsed as two monad applications, so F called on, G called on A. It doesn't return a function, it returns an array. With my syntax, you can't do that, right? If I write open moon F diamond G diamond A closed moon, that's parsed as a normal train because it's inside train syntax. So you can do like, no, if you want to subtract one from a list and then reverse it, you can write a train reverse minus one instead of having to write like one minus commute reverse or reverse minus one constant or something like that. The last advantage is nothing syntax. I call it nothing syntax because of a similar feature in BQN. [03] In BQN, there's this nothing pseudo value, I guess you can call it, because you can't really put this in an array, etc. But if you pass it as a left argument to a function, it's called monadically. So if I do nothing minus two, it returns negative two. It ignores that the left argument is nothing. One of the things I can do with this nothing is put it in trains. If you want to write a long chain of atops in a fork in a train, you can't because if you write like, no, reverse minus sign, that's parsed as a fork, not as a top and a top. So the way you can solve it is nothing. If I write reverse nothing minus sign, that's parsed as the sign, a fork, nothing minus sign, and a top with reverse. But because the left hand of the fork is nothing, it's interpreted as a monadical. So in effect, it's the same way as you're doing reverse a top minus a top sign. And I don't have nothing as an actual symbol, but because I have all the tines separated by diamonds, what I can do is just leave one tine empty. So I do diamond, diamond, and that's interpreted if it's left tine, of course, otherwise it's just a syntax error. But if it's left tine, it's interpreted as force the next function to be an atop. The reverse minus sign thing becomes reverse, open moon, reverse, diamond, diamond minus, diamond, sign, closed moon. Some of this came up after I already made the syntax, but the worst, especially any class, any position was what convinced me that the explicit train syntax was actually useful and not just an easy parsing cop out, I guess.
00:23:56 [AB]
Conor is asking about what happens if you actually make what he would call a two or a three train, meaning if you actually put two functions adjacent to each other, isolated context.
00:24:07 [MV]
Yeah, it's an error.
00:24:07 [AB]
And it's an error.
00:24:09 [ML]
And to maybe make one more distinction clear, the APL problem that some people have with trains that it doesn't solve then is this odd even problem where if you have a big long list of functions, the odd ones are treated differently from the even ones. So I guess that's something that the KAP syntax is good for and that Iverson's APL syntax and the tinyapl syntax are, they don't try to solve at least.
00:24:33 [AB]
Right. You still have the odd even. You could, I mean, what do you use as separators? Diamonds. You could potentially have every other symbol being special, being different than diamond, left diamond, right diamond, whatever, that in order to show what in the same way as the KAP symbol is. It's also, I find it interesting that you add, you find a better way, a different way of doing things, but then you keep the old way around, even though, I mean, I suppose you don't really have a user base or you don't really care about backwards compatibility at this point, right?
00:25:06 [MV]
Well, it's mostly because sometimes if I wanted to do like a reverse set of minus, I can write reverse set of minus. There's no point in using a train because it's longer, right? If it was implicit, it would be shorter, but because I have explicit train syntax, it ends up being like two characters longer, I think, and it's used as clutter where you can just write reverse set of minus or whatever. So I keep both because depending on what you need, they are clearer in different contexts, right? So if you have long functions in the top, I think it's clear as a train and also you need parentheses around the right operand, so it's also more cluttered, but if it's a simple function, I think at top it's easier and the same goes for fork.
00:25:47 [CH]
Yeah, so there's this really good, in terms of understandability or trying to get to understanding, these combinators versus trains, which we'll link in the show notes documentation page, which is the same link I said before, but instead of combinators, it's combinators underscore trains, and it has basically conversions between the two, and for a lot of them, the combinator version, aka not using the explicit train syntax that, yeah, has the two symbols that kind of look like moon, two crescents, a lot of them are shorter in the combinator form, but like you said, there might be use cases where you prefer one over the other. I noticed that you, so you have on the combinator page, you've got symbols like for the delta combinator, which is the before, I believe it's the dyadic before in BQN, you have both the symbol from BQN, which is like the little circle with the left line out of it, and then you also have the hopefully coming in Dyalog APL 20.0, jot underbar, which is, I think the same thing, hopefully going to be coming that's not existing there yet, but it is an extended Dyalog, although I think we talked about that.
00:27:08 [ML]
It's actually in dzaima APL is where it showed up, and then KAP after that.
00:27:14 [CH]
Right, and it is in KAP as well. I didn't know it was in dzaima APL, so there is precedence for this. Is that just because you want one array language to rule them all; you want everyone to be comfortable coming from whatever array language they're coming from, or what's the story behind having two symbols for that?
00:27:30 [MV]
My monadic "before" doesn't do the same thing it does in like dzaima and KAP. It's not a hook, and neither is "after", like it is in KAP. Monadic-before does basically reverse function composition, so it's the "mirror" combinator . It's basically G atop F, but "G compose F" (instead of "F compose G"). So the jot underbar and the left hook symbols, you can use both because if you want to write a monadic hook, you have to use the left hook symbol ("before" doesn't work for that), but the dyadic meaning is the same in both cases, so it just happens to be the same. You can also see if you go to the bluebird combinator (the normal function composition) there's three different combinations that do the same thing, and it's also in Dyalog; it happens the same thing. They are different when they become dyadic. The same for "before" and the left hook; they become different when they're monadic . If there was like a competition for APL with the most combinators, I think I would win. By my count, I have like 11 or something like that. They're not all useful all the time, but sometimes it just comes up that you need that specific thing, and it's nice to have a combinator instead of a train, I think. I like combinators more than trains.
00:28:51 [CH]
Yeah, this is super, super, super interesting. We'll pause here if anyone else, Bob, Marshall, Adám, has questions before we pivot. Bob's got his hand up.
00:29:02 [BT]
I've got my hand up because you've taken J's modifier trains, which I think are, at this point, the underused part, and you've incorporated them into TinyAPL, and there's a lot of people in J who do not use them. I think it's just because they're not used to them, but you're putting them into the new language. How are you using the modifier trains?
00:29:20 [MV]
Well, the main way I've used modifier trains is to do away with the hyperators in NARS 2000. Like if I want to commute a conjunction, I either have to introduce a whole different class of types; so not an array, not a function, not an adverb, but not a conjunction. It has to be like a hyperadverb or something. And with modifier trains, there's an easy way to do this; you just do a 3-train: dex, and then the conjunction, and lev. Lev and dex are also from J. They are like the identity conjunction. So lev returns the left operand, and dex returns the right operand. And I mostly use them for that. Sometimes I also use the hook to modifier train, which takes an adverb, it applies an adverb, and then it passes it to a conjunction, and on the other side, it initiates a change. That's also been useful sometimes, but I admit I don't use them really often. I just think they're nice to have when you do need them. And also, because of my special syntax for trains, they're like a natural extension. There's no reason not to support them. So I just do.
00:30:32 [BT]
Yeah, and I guess they're a bit of a toolkit when you're trying to take one combinator and making an adjustment to it. Without having lev or dex, you're really going to be working harder to try and do that same thing.
00:30:42 [CH]
So once again, we will link this because, I mean, kudos to the listener that is hanging on here for hopefully not dear life, but almost probably dear life [chuckles]. There's a third documentation page; the first one was "/combinators". The second one was "/combinators_trains". [04] And the third one is just "/trains". And this one has: 1-trains, 2-trains, and 3-trains as partially described before with ... we'll call them crescent. Do they have an actual name that the Unicode kind of ... [sentence left incomplete].
00:31:19 [MV]
Unicode calls them white parentheses, I think.
00:31:22 [CH]
... wide parentheses. All right, we'll call them wide parentheses.
00:31:25 [AB]
White, I think; the color white.
00:31:27 [MV]
Yeah, "white".
00:31:29 [CH]
Oh, white parentheses. Interesting.
00:31:30 [ML]
So I guess it's like an outline.
00:31:31 [CH]
Yeah, that is interesting.
00:31:32 [AB]
They're like blackboard bold, but for parentheses.
00:31:33 [CH]
Oh yeah.
00:31:37 [MV]
I found out later that there is like a better glyph for that because [for] the one I used, theoretically it should have both lines of the parentheses curved, whereas there is another one, which is like Z notation something, which actually has one straight line, one curved line. In tinyapl 286, I use the straight line for the curved line symbol, but I think it's clear enough. I don't think I have to change that.
00:32:03 [CH]
So in this documentation page, you've got the underscore A representing what I assume is adverbs, underscore C underscore, which is what I assume is conjunctions, and then you've got different functions, F, G, H and X, Y, U. Walk us through the color coding scheme, because I think I've kind of gotten it, but in some of them, especially in the three trains, you'll end up with: underscore A, underscore B, underscore C. At which point I'm lost. I guess those are three different adverbs. I guess, anyways, instead of me guessing, let's have you walk us [chuckles] through what the color coding and kind of notation is trying to represent here.
00:32:52 [MV]
Yeah, well, in this page, there's two ways you can tell what class a name refers to. The first one is every class has its own case and convention, so you can tell what types things are just by looking at the name. So the first three train I see is "x y z", where they're all lowercase, and that's array, array, array train,. But also there's color coding (syntax highlighting, I guess). Arrays are red, functions are green, adverbs are blue, and conjunctions are yellow. In the interpreter, the colors are slightly different, I think. I use colors in the same (they're not the same actual hex code) but they're the same generic color as the one in BQN; I just stole it from there.
00:33:36 [CH]
Okay, let's walk through the first one of the three trains that has a U in it. So it's U underscore in front of the white parentheses or crescents (crescent is shorter, we might call them crescents). So we've got: the underscore, left crescent and then underscore A for an adverb, diamond, G, diamond, H, and then end crescent. And this maps to not a 3-train, but a fork where U is the array here, is that correct?
00:34:14 [AB]
No, no, it derives a monadic operator or adverb or 1-modifier. So what's inside the crescent?
00:34:21 [ML]
So U is an array, but U is an array that's used as an operand.
00:34:25 [MV]
It's kind of interesting, U and V can actually be functions too. It's just like D, they are the operands, but they're just there for explanation purposes. Like inside this train, you always have the correct name classes. Outside it is just generic, but otherwise I have to duplicate; I have to do U underscore, etc, then new line, F underscore ... [sentence left incomplete]. Because they do the same thing, whether they are arrays or functions, I just use U and V as placeholders for both arrays and functions.
00:34:54 [AB]
Basically, imagine the underscore A was just another function F, so you get a normal "F G H" train, but F isn't _quite_ defined yet. What we know about F is that F is derived from underscore A.
00:35:07 [CH]
Right, right.
00:35:15 [AB]
And the only thing we need to complete it (our knowledge about what's going to happen when we apply this) is to know what the operand to underscore A is [Conor agrees] so it's like there's an empty slot there, and once we fill that, then we got our complete "F G H" train.
00:35:24 [CH]
So yeah, if you had this common pattern ... in my head, the obvious one would be a reduce. If you had a reduction as your left function in a fork, but you didn't know what binary operation you wanted to pair with it, and that was a common enough pattern that with G and H (your two other functions) you wanted to name that or use that, you could spell that pattern, and then the quote-unquote "argument or operand that modifier train takes" would then form the final ... [sentence left incomplete]
00:35:53 [AB]
It's too simple [an] example, because there's an easier way to spell this, but just to be able to imagine how this works: imagine if we had the fork: "reduce equals right-tac" (now, because of the right-tac, it could be simpler, but that's for illustration purposes). So now, this fork, if you give it a "maximum" operand (because it's a monadic operator) then it indicates all the maxima. If you give it a "minimum" function as operand, then it indicates all the minima. And if you give it a left-tac, then it indicates all the ones that are equal to the first element.
00:36:32 [CH]
Yeah, that's a good way of explaining it or visualizing it. Interesting. So you have clearly a whole set of these.
00:36:38 [AB]
Is every combination represented?
00:36:42 [MV]
No. If you only use arrays and functions, yes, and some of them are quite silly. For example, the first one you see is "x y z", where they're all arrays, and they just return the middle tine as a constant, and there's no point to doing that.
00:36:56 [AB]
Sure there is. What are you talking about? Don't speak down about the list language like this [chuckles].
00:37:01 [ML]
You can use them for documentation; x and z.
00:37:04 [AB]
No, but if you want some side effects happening of those outer functions, and you want to make sure they're happening in a specific order, and then returning the middle value.
00:37:14 [ML]
But in the first case, they're not even functions.
00:37:15 [AB]
No, they're expressions.
00:37:16 [ML]
Oh, okay. Yeah.
00:37:17 [AB]
So you can have any expression, and you want the side effect of the expression without disturbing the fact that you're returning eventually another value. And that middle expression may depend on things that have been side-effected into being by the outer ones. Certainly useful.
00:37:33 [MV]
Yeah, if you use adverbs or conjunctions, not all of them are defined. I have more than J has, because some of them suffer from the same problem of: they would parse as function application or conjunction application, etc. So we have some more, but I don't have all of them, but there are a lot. I tried to make some [but] I can't find a way to make them make sense, so I just don't have them. But if you see that there's ones that's missing, I can add it.
00:37:58 [AB]
Or send me a list of all the ones that's not ... [sentence left incomplete]. I could probably do this; generate them myself. We'll come up with something. We've gone this far, might as well take it to its bitter conclusion.
00:38:06 [ML]
Yeah, well, there are also some that could have two interpretations, right? So there's one where it's an adverb, and you have "array conjunction adverb". And so there's kind of two empty slots, like the array is supposed to bind to the conjunction, that makes sense. But then you have the right operand of the conjunction, and the left argument of the, dyadic operator, and the left operand of the monadic operator, depending on what your preferred terminology is. So you could do it in either order; you could pass that operand into the monadic operator first, or the dyadic one. And so you've chosen the monadic operator. Is there, like, a system for that, or is it just case by case?
00:38:49 [MV]
I think I just took them from J, and I trusted that that was a better choice.
00:38:54 [ML]
All right, yeah.
00:38:55 [AB]
You can't trust!
00:38:56 [ML]
I've got no idea how the J ones work [chuckles].
00:38:58 [BT]
Well, and I know Henry spent a lot of time thinking about which ones he would put in and there were a few that were suggested that he categorically denied [Marshall agrees]. He said: "No, that's just not the right way to think about it." [05] And I don't know all his reasons, but I know there were a lot of discussions when the modifier trains came in about which ones would go in, and there was a couple suggested that he just said: "No, that doesn't make sense to the way I see this working," and so they've never been put in. So it was definitely thought about.
00:39:27 [AB]
I wouldn't blanket trust J when it comes to trains. Just look at the hook disaster [Bob laughs].
00:39:35 [CH]
They were just experimenting there, you know? They were the first ones, Adám. You gotta take it easy. Take it easy on J, okay? Roger admits, it was a mistake.
00:39:43 [AB]
Yeah, but that's why you shouldn't blanket copy it.
00:39:46 [ML]
Were the modifier trains designed before or after that was realized to be a mistake? I don't know.
00:39:52 [CH]
Oh, yeah, that's a good point.
00:39:53 [AB]
Yeah, and it could be that the design of the modifier trains is trying to mitigate some of the issues coming up because of what the 2-train is in J, and therefore you don't want that when you are doing modifier trains.
00:40:04 [BT]
That's a really good point, because I'm not sure how developed modifier trains were when the hook was put into place. So depending on which one sort of showed up or whether they showed up at the same time, there might have been mitigation involved.
00:40:18 [AB]
Surely the hook is way before. The 2-train being a hook predates J, right?
00:40:24 [ML]
But yeah, the question is not when the hook was added, but when Roger decided the hook was not the thing that should have been added, which hasn't been corrected in J, of course, because that would break anything written in J.
00:40:39 [AB]
Break some code? [laughs]
00:40:42 [BT]
Pretty much everything.
00:40:42 [ML]
Yeah, I have the short version history on APL wiki, and I don't see modifier trains being introduced the first time on there, so I don't know when that happened.
00:40:54 [BT]
Yeah, I'm only going by when they were taken out, which I think was around 2004.
00:40:58 [AB]
But it doesn't really matter, right? Let's assume that modifier trains were originally designed before the realization that the hook was a mistake; that the 2-train being a hook was a mistake. Still, the modifier trains would have been designed to work as best possible. Should have been designed to work best possible with the 2-train being a hook.
00:41:22 [ML]
Yeah, so it's more a question of how much you should distrust, rather than whether you should distrust.
00:41:28 [AB]
Right, and if the modifier trains were designed after the hook was realized to be a mistake, then they would be designed, ideally, to try to mitigate some of that. Either way, it doesn't seem appropriate for an APL that doesn't have hook 2-trains, whether it's designed for or designed against, but you can disregard it altogether. So you should really rethink from scratch and make sure you get the right thing. But I'm with Marshall here: when there's different ways of doing it, maybe (I mean, there certainly warrants a lot of thinking) but maybe in that case it's best not to have it because it's ambiguous.
00:42:04 [ML]
Well, I have no particular suggestion for this. I don't have any idea what I would do [chuckles].
00:42:08 [AB]
Yeah, modifier trains in BQN, when?
00:42:11 [ML]
Never! [laughter] I would take out user-defined modifiers before I did that [chuckles].
00:42:17 [CH]
So I guess an interesting question, at least for me, is to get your thoughts, Madeline. One of the thoughts in the back of my head is that in, I think it's Henry Rich's book (I'm not sure if he's the first one to refer to them) but he refers to trains as invisible modifiers, and in that they are these adverb-y, conjunction-like things, but the patterns (the trains, whether they're function trains or modifier trains) they're invisible. You have made trains not invisible. And I think actually this is the first array language that has made the invisible modifiers no longer invisible. I will say that there is some of the appeal of the modifier trains in J is that they are invisible. Already we can have a discussion of the practical [aspects]. Is it a practical thing to use or scatter into your code? And then it probably only becomes worse by it being invisible. So does the fact that they're not invisible, that they're explicit (and you even use the same syntax with the prefix and suffix underscores); does that affect the readability of these things do you find, or have you not used them enough because you said you use them sparingly?
00:43:41 [MV]
Well, first of all, KAP had added explicit train syntax before me, at least for some trains, but I think that the explicit syntax actually makes them clearer because if you have longer expressions in a train, it's easier to tell when one ends and the other one begins, right? So I think my trains are clearer than Dyalog's. They are more verbose in simple cases, but the verbosity is an advantage in more complex cases, I think.
00:44:08 [CH]
Yeah, I could totally see that. So KAP technically added explicit syntax for the fork. Is that the same thing? I'm trying to think. Is that the same thing? Because they technically just got rid of the 3-train and then it was the fork. Is that the same thing as adding syntax for forks in general? Or it's not forks. It's for trains.
00:44:33 [BT]
I'm just going by Henry's book, "J for C Programmers". I'm pretty sure when he talks about invisible modifiers, he's just talking specifically about 3-trains. I don't think he's talking about modifier trains at that point because they didn't exist.
00:44:47 [ML]
Well, so to be clear, there were the adverb trains and you could do a partial application of a conjunction. So there were some very limited forms, but I don't know if the book even covered those.
00:45:02 [AB]
Even Dyalog has that.
00:45:03 [BT]
Well, it has things like "each" in it. It explains that, but it's almost like those are just sort of leftover details that are really useful rather than something that's a construct of the language, which when you put modifier trains back in, suddenly you see the full power of them again. And whether or not they're super useful, I guess, is yet to be determined.
00:45:25 [CH]
Yeah, I guess right now I'm thinking, what's the delineation between train programming and fork programming? They're the same thing in BQN and Dyalog APL, but in KAP, Elias separated the fork from trains, in my head. And so he added explicit syntax for forks and then train programming is really only in 2-trains, which is just unary function composition. You can have a fork or a phi combinator or a phi-one combinator, but that's no longer quote unquote "train programming" in my head because there's explicit syntax for it.
00:46:06 [ML]
Yeah, so that's actually kind of the same thing, that only functions of one argument is pretty similar to what J restricted their modifier trains down to because it's just chaining adverbs together and being able to fill in one operand of a conjunction.
00:46:24 [CH]
Interesting. And then, yeah, in TinyAPL, you're still doing quote unquote "train programming" even in my head. This is just kind of my mental model for it. It seems like you're still doing train programming. It's now just that though, to spell a train requires explicit syntax, which is why I feel like I just said the same thing twice, but then came to two different conclusions [laughs] for TinyAPL and KAP. In KAP, they added explicit syntax for forks, which no longer makes it a train; or is it still a train? And I think it's because the documentation says there's only support for 2-trains and the fork is now explicit syntax, whereas in TinyAPL, there is support for trains. There's just explicit syntax with the crescents and then the diamonds to separate ... [sentence left incomplete]. Adám's got his hand up. We'll let him correct what I've said.
00:47:10 [AB]
No, I can't correct you because it's a judgment call. It's a nomenclature call. But I would suggest that we say that trains are just sequences of things, whether it's functions normally or it could be these adverb conjunctions; other things like that. That maybe means that even the strand is a type of train. Maybe it even means that a normal expression is a type of train, whereas forks is the flow pattern. It's the pattern of giving arguments to two functions and then passing the results into a third function. If we distinguish like that in a way that's not meaningful to distinguish in BQN and J and APL (because trains are forks; 3-trains are forks) then it makes it a bit easier to speak about it. Then we can say that KAP certainly has forks and TinyAPL certainly has forks. KAP also has trains, but they are just atops, atops, atops. And TinyAPL does not have trains. We just tried: any sequence of two adjacent functions isolated from anything else is an error immediately. Does that make sense to give us some language to speak with?
00:48:25 [ML]
Yeah. So you could say trains are a syntactic feature and forks are a semantic feature [Adám agrees] ... is the kind of one sentence condensation.
00:48:32 [AB]
Right. But I mean, there's a little bit of weakness in what I'm saying in that there's no good distinction then between a normal expression and a train. And of course we always end up with "I" (Editor's note: I is a tacit programming language that Marshall developed). In I there indeed is no distinction between a normal expression and the forky train; [they're] one and the same. But I think it's good enough for our purpose where we just assume that if it's something that immediately evaluates to a value, then we don't call it the train though we could. That gets rid of both strands and normal expressions.
00:49:11 [ML]
Yeah. In BQN, the rule is just that a train is an expression that results in a function [Adám agrees]. So many of these expressions are going to be 1-trains, but you can say clearly that "1+1" is not a train because it results in an array. Whereas "+-×" ("plus minus times") is a train because it results in a function.
00:49:16 [AB]
But "1+1" in I does result in a function.
00:49:34 [ML]
Yes, because everything in I is a function.
00:49:35 [AB]
The 2-function.
00:49:38 [BT]
And just to clarify, I is a language [chuckles] that Marshall developed.
00:49:44 [ML]
Designed by me.
00:49:45 [BT]
Designed by Marshall.
00:49:46 [ML]
That I don't mention in my introductions, I suppose.
00:49:48 [AB]
Marshall, why did you not say I designed it [laughs]?
00:49:50 [ML]
Yeah, I probably did say I in my introductions, but people thought it was referring to me [Bob laughs].
00:49:56 [BT]
I think Madeline would like to add something here [laughs].
00:49:58 [MV]
Yeah, I think another important thing that trains do is longer trains, right? As far as I know, every language with trains, you can do a 5-train or a 6-train or a 94-train. That's the thing that APL can do. I think it's train like syntax is more like trains because it can do that. You can do longer trains.
00:50:17 [AB]
I can't do 94-trains; invalid.
00:50:22 [ML]
Well, I mean, it depends. So it's always going to be split up into three trains, but it's not any different than the way APL does it really.
00:50:29 [AB]
But 94? Doesn't it have to be an odd number?
00:50:32 [ML]
Oh, yeah. No, it can only do 93 or 95. Sorry.
00:50:33 [BT]
Oh yeah. No, you're right.
00:50:35 [AB]
Yeah, so it doesn't have 94-trains.
00:50:39 [ML]
Yeah.
00:50:39 [AB]
You could extend I with even length trains. Give them some meaning, make them a hook or something.
00:50:44 [ML]
But then it's only ever the very first token you write that can be the even one and everything else [Adám agrees] is decomposed into three trains.
00:50:53 [AB]
So anyway, to go back to what Madeline was saying, that other than I, we allow any length trains in any of these languages. Although you could argue that something like KAP doesn't allow any other ... [sentence left incomplete].
00:51:09 [ML]
Well, it doesn't allow any other fork types, but its trains are definitely ... [sentence left incomplete].
00:51:14 [AB]
Yeah, trains can be any length, but they're different. I wanted to say that KAP's explicit fork replacement for trains can actually only be length three. Because it's a syntactic construct, there is no thing as a fork train in the forky sense or five in KAP. Because all you end up doing is the syntax dictates what's one tine of the outer fork. You can have a fork where some of the times are forks themselves, but there's still just three and the special syntax for three. And KAP could add a special syntax for a combination of four things, whatever the meaning of that would be. But it's explicitly only that.
00:52:01 [ML]
Well, I mean, so the syntax is more designed to bring out that feature; to make that feature clear. But it's also true in APL and BQN that there's only a 2-train and a 3-train [Adám agrees]. And it resolves the precedence. It doesn't matter which direction it builds an odd train from, but it chooses to do it from the right. So that is part of the language design. I mean, it's a lot like in an imperative language: [06] you have, if A do this, else if B do this, else if C do this. There's not actually a multiple "if" construct. It's instead chosen that this if-else thing is right associative. So it looks like one, but it's built out of fixed length components.
00:52:46 [AB]
Hold on, but the if statement goes from the left.
00:52:49 [ML]
No, I don't know what you would mean from the left, but if and else are right associative.
00:52:54 [MV]
Yeah, if you have nested ternaries, you evaluate the left one first.
00:52:58 [ML]
Oh yeah, yeah, yeah. Yes. Yeah. So the parsing is right associative. The evaluation starts from the left, but...
00:53:08 [AB]
Oh, okay. I was thinking of an else if, but in tinyapl, yeah, we're right back to, well, you can have any length train, but really they just end up being forks. So forks are forks are forks until you have an atop. The question is just, is it explicit or not? It's almost like if you took J APL, BQN, and you added some color to the paper it's written on. What tinyapl has is just background framework slots.
00:53:33 [ML]
Well, no, not only that, because the separators are explicit.
00:53:38 [AB]
So every time is parenthesized implicitly.
00:53:41 [ML]
I mean, it's explicit. It's just a different explicit.
00:53:45 [AB]
Yeah. Okay. But there's no, it's not a parenthesis, but it could have been. Madeline could have chosen a syntax where instead of using diamonds, it would be parenthesis of... Yeah. Yeah. The outermost layer of parenthesis is inside the special marker context, which is what creates the times. And then you could have a special symbol saying, whenever you have a right paren followed by a left paren, right? Where one time ends and the next one begins, you could replace that with a close paren, open parent character, a single character instead of two. And not only that, you could omit the first opening paren and the last closing paren because they're obvious. And now you're right. And if they then design your close parent, open paren character to be a diamond, then it's the same thing. So it's really just a visual difference.
00:54:30 [ML]
Well, so like, yeah, there is an explicit train syntax that would have that correspondence to tinyapls. However, APL and BQN use an extension of that syntax, which doesn't require you to have the prints. So I mean, it's a different... Then it matters that modifiers have higher syntax, higher precedence than functions. So I mean, there are different syntax considerations when you have that sort of implicit syntax, I think.
00:54:59 [AB]
Well, you need to have the separation for some syntactic constructs that I'm not even sure any of them are allowed. Like if you had, if you allowed, for example, an array conjunction array train, which I don't think is allowed, supported. It is actually, but it doesn't do anything. Okay. I was about to say that array conjunction array would be ambiguous with, say, array conjunction array, if you understand what I mean. But this just happens to be also defined to be exactly that. So there's no actual problem there. But potentially you could have some kind of construct where the outside of train syntax and inside explicit train syntax would mean two different things. I don't know if that actually ever is the case.
00:55:50 [ML]
Yeah. I mean, so I think this, not only having the explicit indication outside, but also the explicit separators inside is doing something that just the outside indication alone wouldn't. I mean, it separates the semantics of trains further from the syntax so that you really can, you don't have these syntactic considerations that are bleeding into what sort of trains you can write. It's only based on, here's my sequence of things, make a train out of them, which still has some, that's not necessarily a natural, or it's not always a perfectly well-defined thing to do. So the language still has to make choices with that, but they're no longer tied to having to disambiguate it from the normal expression syntax.
00:56:33 [BT]
So Madeline, aside from getting us into a really deep discussion into the syntax and the semantics of trains and forks and such thing, which I think if people do follow along, they'll actually learn where some of the complications are. What else do you want to do with this language as you're developing it? You're developing it very quickly. You're adding all these extra things. Where do you want to take it?
00:56:55 [MV]
Well, my main goal is to make an experimentation playground. After I'm done with all the primitives I have planned, so 1.0, I want people to suggest new primitives, want people to suggest new stuff to do with the language, and I want to add it and have more people try it out and find if it's actually a good primitive or not. I also kind of want to make it faster because it's horribly slow with doing something, but I would have to do big changes in the code base and maybe that's better left for a completely new version done from scratch, which I also have planned. But yeah, the main goal is to experiment with primitives.
00:57:37 [CH]
And I will say that there are a massive number. It's called tinyapl, but maybe that's a joke because I'm pretty sure you've got more primitives than... I was checking to the CEO, "Did you have this or that?" And I was like, "You've got on top of maximum and minimum, you've got maximal and minimal." I'm not actually sure what those do, but there's a ton of... Maybe you can tell us what the maximal and minimal do, but it looks like there's like every glyph you've ever seen in all the APLs combined, which actually, I think... There's a whole other discussion of how many glyphs is too many. I don't think that there is enough. Keep on adding them. When you learn some of these ideogram languages like Chinese, you learn thousands and thousands of these. I think if you're going to work with a language, you can actually learn them pretty quickly. Anyways, over to you on maximal and minimal. And maybe if you want, you can tell us a couple of your favorites that you've put in that are not found in some of the other array languages. Yeah.
00:58:41 [MV]
So it's called tiny, not because it's tiny for the user. It's called tiny because each primitive is implemented in not a lot of code. I have 130 primitives, I think, and the whole code base is about 4,000 lines of code. So it's really small, I think. That's why it's called tiny. The difference between the minimum and minimal, and the same goes for maximum and maximal, is that minimum is pervasive, or a scalar function, or a call to whatever. So if you do two lists and you do a minimum of them, it does the minimum of each pair of elements of the list. Minimal, instead, takes a whole list and compares them like you would do if you did a gradient on the two lists, and then returns the smaller list as a whole. So it doesn't combine these lists in any way. It just uses one or the other. The same goes for the total array ordering comparisons, which are precedes, precedes, or identical, identical, not identical, succeeds, and succeeds, or identical, which are the total array ordering correspondence to less and less or equal, etc. And identical and not identical appear in every API. They call it match and not match, or a natch, or whatever. I call them identical, not identical, because they're better names, I think. And they fit better with precedes, because otherwise the less or equal equivalent would be called precedes or match. We don't really like. So identical is better, I think. But yeah, so precedes, which is like a left pointing triangle, is the natural extension to match. You compare it to arrays as a whole, not for each element, and then you return which one is that, whether the first one is slower than the other or not. It's not pervasive. It just compares the two arrays. And I really like those printers. I use them a lot. I also like the ternary syntaxes I just introduced a few days ago. And it's kind of like ternary in JavaScript or whatever. I use a quad question mark and a quad colon to do that syntax, because question mark and colon obviously have different meanings in APL and in TinyAPL. But I really like that. I use it a lot. And other than that, I don't think I have a favorite primitive yet. I haven't used language enough, probably. But I really like the capability that you can do graphical output in the JavaScript interpreter. I added the support for plots, for images, for GIFs, for audio. They're obviously stolen from VWAV. But I really like the graphic capabilities. I use a lot. Recently, when I need to do data visualization or whatever, I do it in TinyAPL. But to figure out what the problems are, what printers are missing, etc. Because it's really fun. I think it's a fun experience.
01:01:48 [AB]
So now that you've spoken about the total array ordering, [07] I'm a bit wondering why you have the ordering that you have. You want to speak about it a bit? That's another one of the top level info pages, the ordering page.
01:02:06 [MV]
Yeah, I would have to look at it too, because I don't remember. I very rarely use mixed type ordering. I think characters come after numbers and they're... I don't remember. I never use it. Almost never. When I compare two arrays, total array ordering, they're either all the same type, usually, or they're like scalars, but they are lambdas or wraps, as I call them. So, for such functions. Because you can't do equal on them, because you can't meaningfully compare the code of two functions. And I am considering doing reference equality, and I think about that, but for now you just can't. It's always false. But what you can do is order them, because everything is ordered, right? And ordering wraps equals based on the representation. And representation of defense is not useful, because it's always like open brace, dot, dot, dot, close brace, because I don't store the code inside it as a string. I probably will eventually, but I don't right now. But for primitives, if you want to compare a primitive... Let's say, if you're writing a... In a standard library, there's a function called bitwise, and an operator, an alpha called bitwise. And what it does, it's the same as, similar to the bitwise primitive operator in KAP. So, it takes an AND and turns it into bitwise AND. And the way it's implemented in a standard library is it wraps the operand, and then it does a grade on it, I think, or a precedes, I don't remember. Anyways, it checks that they're the same using precedes and succeeds. If they're both precedes and equals, and succeeds and equals, it means they're equal. I use this for that. But yeah, the actual ordering is not thought very deeply about.
01:04:03 [AB]
For example, it means that a short name that begins with a letter later in the alphabet sorts before a longer name that begins with an earlier alphabet, like ZZ, or ZZ, if you're so inclined, sorts before ABC.
01:04:21 [MV]
Yeah, that's because the way it's implanted in ASCII, it's just the default ordering instance, I think, does that, because our A's are defined as shape and then content. So, it compares shape before content. I don't think I decided that, it just came to be. I never had any problems with that. Yeah, maybe it makes more sense to compare the contents first, and the shape, or maybe rank, then contents, then shape. Yeah, I just didn't think about that yet. Nothing is settled, because 1.0 is released already. So, I can change everything with no issues. And some things I just didn't think about yet, and ordering is one of them.
01:05:03 [ML]
How many... Was it years that Dyalog spent figuring out what you wanted to do? It was a while.
01:05:11 [AB]
You might want to look at the paper, "Tell Axioms" by Roger Huey and J Fould and myself. I mentioned I was in that order, but it was just... For the subject, it was funny that it didn't matter how we ordered, by first name or by last name.
01:05:29 [CH]
All right, the last and most important question we should ask is... And I was messing around with my keyboard, and I figured it out, and I accidentally typed it, but then I couldn't figure it out. Because of the proliferation of symbols, you have mapped more than two glyphs to a key. And at least on the web editor, it's the same as it is for Dyalog, APL, and KAP. It's the backtick. But how do you access the third, and what looks like potentially fourth, fifth, and sixth symbols on some of the keys? Because you typically only are able to get access to two. What's the trick? And I know everyone always asks, "How do you type it?" as a joke on YouTube. And now it's ironic that I'm actually asking, "How do you type it?" But the people need to know.
01:06:20 [MV]
Yeah, so there's six symbols, at most six symbols on each key in the image. You can see on the keyboard. The first column is just the normal keys you get. So the lowest left is just pressing the key, and then upper left is the shift plus the key. And then there is a middle column, which is what happens when you press the backtick once. So like I'm looking at the A key. So no, that's a bad example.
01:06:47 [AB]
Take the R key as an example.
01:06:49 [MV]
Yeah, I'm looking at the R key. So if I press backtick R, I get the row symbol. If I press backtick shift R, so the upper middle, I get the square root. And then if I press backtick twice, I get access to the last column. So backtick R, lowcase R, gets the row underbar. And then backtick backtick shift R gets the real bar symbol, which is the upper right symbol. So each column is how many times you press backtick. So zero columns, zero times. First column, one time, and second column, two times. And then the rows, so the bottom row is no shift, and the top row is yes shift.
01:07:30 [AB]
That means you can't actually do a shifting keyboard for this. You can only do a prefix keyboard because you can't-- I mean, if you press your shift key hard enough, it will break. But it's not going to be double shifted like that. Maybe if you hold caps lock and shift.
01:07:44 [MV]
Yeah, control and alt maybe.
01:07:47 [AB]
Yeah, that conflates with the right side alt on some systems, some places.
01:07:52 [CH]
Well, I implore the listener to go and if not take this for a spin, at least go and check it out because it is pretty wild how many glyphs. And it makes me curious. I want to now go-- because I haven't spent any time or at least in anger used a tiny APL. And now I'm staring at a bunch of symbols and I want to go-- like I can see that there's the symbol that I discovered thanks to Adám for width. But I'm guessing that it's not width and that you've done something different with it. But yeah, it makes me curious to go and check out what all these different symbols do. I guess we'll make sure that we put a link in the show notes for not just all the documentation links, but also the editor that they can go try it out. Is this something that people can download onto their local machines and use it? Or is the editor online the main way people should check it out?
01:08:48 [MV]
Well, the online is definitely the main way because it's much more refined. There is also a WebAssembly standalone executable. So you can download like once in a time and run it with that. But it doesn't have input. So you have to copy paste the glyphs or use an external layout for that. I plan to eventually add those. But I'm not good at terminal interface programming. So we have to learn more about that for sure. But yeah, for now, the best way is definitely the web interface.
01:09:19 [CH]
Well, go ahead, Adám.
01:09:20 [AB]
Sorry, I wanted to respond to what you're saying about you want to go exploring the glyphs. We haven't spoken about the Omnibar at all. And tinyapl on Omnibar, when? When?
01:09:33 [MV]
Probably 1.0, I think. I have a branch where it's already implemented. It's not public. But because I keep changing stuff, I don't think it's just useful to keep changing the documentation on Omnibar.
01:09:46 [AB]
Maybe you should tell the listener what Omnibar is. [08]
01:09:49 [MV]
Oh yeah. Omnibar is another project of mine that is basically a collection of all the glyphs in a lot of EPL dialects and what they mean, both primitives and syntax. So like you can see, there's a filter where you can write the name of a primitive or the symbol of a primitive, the glyph of a primitive, or you can choose what dialects you want to see. And yeah, it's like you can see all the primitives and all the syntax. So for example, the first entry is a plus, so it gives plus. And you can see that it does identity or conjugate or addition or join strings in EPL64, apparently. I didn't remember that. Yeah, but there's a lot of symbols and what they do. And at the end, there's syntax, so below all the primitives. And they're color-coded, so you can tell what type these things are. So there's yellow for monads and orange for diads, et cetera. It's useful to have a list of all the primitives that every EPL has. And that's the first reason why I made this. Originally, it was much more glyph-oriented. There was the name of the glyph. But it came to be a language bar in the sense that it's like a list of all the primitives that are available. And that's why if you use it, it's called OmniBar. It's called that because it's a list of all the primitives in every language, or a lot of languages, at least.
01:11:19 [CH]
Yeah, I had seen this before.
01:11:21 [ML]
Yeah, we've brought it up on the Raycast, maybe as an announcement.
01:11:25 [CH]
Yeah, I think at some point a long time ago. And it is a very nice website. And yeah, you can see stuff that only exists in certain APLs. Like NARS 2000 has a bunch of unique stuff. And I just discovered that KAP has a bitwise function, which I think if it does the same thing... That's somewhat new. Yeah, I was going to say, Uiua has a bits one. So if it does the same thing, then maybe KAP borrowed that from Uiua, or maybe Uiua borrowed it from KAP.
01:11:56 [MV]
No, bits in Uiua is like based to the encode. And bitwise in KAP is an operator. You can do and bitwise and does bitwise and of the numbers, or not equal bitwise and does bitwise source.
01:12:11 [CH]
I see. I see. So it's two different things. Yeah. Well, a super useful resource, clearly. And obviously a lot of work because you've gone through, it looks like every single APL under the sun, to be honest. I'm sure you missed a couple, but there's looks to be like a double digit, at least 10 or 20 of these. So yeah, we will make sure I mean, I'm not sure you said you were gonna publish something, but this already is online, correct? Or what was it that you were gonna modify?
01:12:39 [MV]
Oh, it doesn't have the tinyapl GIFs yet
01:12:42 [CH]
Oh, I see. I see. Yes, yes. I mean, that is an oversight considering it's your APL.
01:12:49 [ML]
Did she know about tinyapl? I guess not at the time of writing, she didn't.
01:12:54 [MV]
I think the latest update was after tinyapl. I think all of it actually was after tinyapl, but I didn't have a bar so yet, I think when I did OmniBar. So it wasn't really make didn't really make sense to include them because it was like an idea. So no point in having them. Now, maybe it makes sense, but I'm still changing all the stuff. So I don't know, not yet. Just in a while I will add them.
01:13:19 [AB]
Also, the table will kind of explode once it gets tinyapl.
01:13:25 [AB]
I guess J doesn't have to be ashamed of its large vocabulary anymore.
01:13:30 [BT]
Oh, we never were, Adám.
01:13:31 [AB]
We never were. Always space for another dot or colon.
01:13:34 [ML]
Primitive body acceptance.
01:13:36 [BT]
Whatever. Oh, I hear.
01:13:40 [MV]
I have to come up with a glyph for everything I want. But J can just add a colon or add the dot or whatever. They have a symbol, right? It's cheating, I think.
01:13:48 [AB]
Yeah, but there's so many characters in Unicode.
01:13:50 [MV]
Yeah, but they never have the one they want. So I have to always compromise.
01:13:55 [AB]
Yeah, it's an interesting tangent. Maybe we shouldn't go on now. But Unicode is kind of a blessing and a curse. In one way, it's a blessing because now it's obvious how you should store all these primitive symbols and so on. But it's also a curse in that it's locked down which symbols exist. It's basically impossible to get a new symbol into Unicode if it hasn't already seen extensive use. You'll just be referred to the private use area. Compared to getting the API glyphs into Unicode, it's very easy. They just did it without anybody even asking for it. And so we have, for example, the greater than dash is in Unicode, but there's no less than dashes. And probably impossible to ever get it there. And we have to do weird tricks that I see even tinyapl does of using Cherokee syllabics and things like that, that approximate the shape we want. And then the font render them as what you actually want. But the situation is already bad enough if you're not using the right font out there in the world. If you don't do that for tinyapl, it becomes very hard to read. Yeah, definitely.
01:15:09 [MV]
I haven't put much thought into what people see if they don't have the font. Surely it's not great, but most of the things are correct. I think the only ones that I cheat a lot are boxes, which is like, if you say, kind of use a separator, but I like it anyways. It's basically, it does enclose, adopt, and then the function. And that uses like a Cherokee letter, which is not actually an enclosed with two dots above. It's enclosed with like one dot above and one dot to the left. And my font makes it two dots above. But apart from that, everything is mostly correct at Unicode.
01:15:50 [BT]
Mostly correct should be the definition of Unicode.
01:15:53 [CH]
All right. Well, as we mentioned before, links for all of this stuff will be in the description. I implore you to go check this out, because it definitely is an immense amount of work, even if it only is 4,000 lines of code. It's some really cool stuff. I will throw it over to Bob, who will let us, or let you know how you can reach us.
01:16:13 [BT]
You can reach us at contact@arraycast.com. And questions and comments and all that sort of thing will be taken in and distributed and sometimes answered, but always read. And also a shout out to our transcribers who will put this into written form for those of us who ingest the podcast in that way. I will point out that podcasts are not meant to be read, but if you choose to, at least understand you're not reading it in the way that we intended it. So if there's complaints about how it reads, you might want to listen to the podcast.
01:16:51 [CH]
And yeah, thank you so much, Madeline, for coming on. This has been extremely interesting and thought-provocative. I'm going to have to go spend more time going through all the different glyphs. And I'm sure that there's a couple in here that I don't know that I want, but as soon as I see it, I will be like, "Oh my goodness, every other APL needs this." Yeah, thanks so much for coming on. And hopefully you'll get some feedback and some more folks looking at this after this airs.
01:17:17 [MV]
Yeah, maybe after this podcast, I will go from one user to two users, and that will be great.
01:17:24 [CH]
We all can hope. We all can hope. And with that, we will say, happy array programming.
01:17:28 [BT]
Happy array programming.