@nathanrogers Maybe vim's having issues picking it up then. Try the debugging thing ngn says I guess, and if that doesn't work see if actually copying them helps.
Okay, new paragraph should show up here eventually. Hard refresh. Lots of inline code always looks horrible but it's better than no explanation I'm pretty sure.
And section links are fragments with the header changed to all lowercase, spaces changed to dashes, and special characters (not including - and digits) removed. So ...#2-modifiers in this case. Github has a link button you can click on their markdown display but I don't.
@Marshall Quick review, since I'm going word for word through the tutorial. When I first encountered array programming languages, it was in discussing "obfuscated c" with a coworker who likened it to the language K, that it was just line noise, and how can anyone read a language like that? Compared K to brainfuq
@Marshall I discovered APL by proxy just learning the narrative around K, and thought it was gorgeous, I was drawn to it. The notation was super tidy, and I had to figure out what it was about.
My first impressions of BQN on the other hand... its been kind of intimidating, like sterile, blocky, rigid, inflexible, gaunt (especially with the fold primitive, and the other modifiers), its hard on the eyes because of all the really tiny notation
I've spent all day trying to get something to display that I can tolerate looking at, not because its array notation, I like that part, but because of the tiny, angled, and aliased itty bitty things that were making my eyes strain to read anything
If I had 1 suggestion to make the website and the tutorial stuff more attractive, and nice to look at, I would switch the code font on your tutorial site and the repl to BQN386 Unicode font
The modifiers in particular are super legible, and I don't have to constantly squint when using it
my eyes are pretty tired after messing with BQN today, but this font is super soft on the eyes
It makes everything all around more soft, and less of that intimidation factor, instead it looks familiar, and not because I know APL already, but it is the same feeling I had the first time I saw APL
just my 2-bits
My preference would me something more like APL2 Unicode or VectorAPL for the ascii set, but they have really bad support for your extended symbols.
@nathanrogers Thanks for writing this up. I can sort of see what you mean comparing the two fonts but I never would have thought of that as an issue on my own. It would help to have some other people here comment: obviously I've chosen the font I prefer but the website is mainly not for me.
One smaller thing I could do is just make the modifiers larger in the DejaVu mod. They're not combining characters so it shouldn't cause any issues in text, but I do have to make sure they're not referenced by other characters in the font file.
in general I like the rest, I particularly like the circle thing you've got going on, that's pretty cool and nice that you can instantly tell what 'type' of symbol something is in general
@Marshall I think it looks cool in a "cold, logical alien race" sort of way, but even as someone who appreciates the notion of notation, it was a bit austere for me
But whatever font, yes larger modifiers please
especially constant. That is super hard to distinguish on the site
@Marshall how does under know what areas of the array are... evaluated?
like in the example you showed ⌽⌾(4⊸↑)↕10 how does it know, since you're just taking 4, that 6 remain? Is there an implicit catenate happening here?
Also this example is a bit misleading to people just learning trains
('A'-'a')⊸+ ⌾ (2 ↑ 4⊸↓) "abcdefgh" the 2↑4∘↓ looks like an atop, but really you're apply ⍵ to the constant function {2}, and passing the value to ↑. I don't think a novice is going to understand that and will likely only be confused later when other trains don't work due to this false intution
@nathanrogers I don't see much of a problem. As long as the user knows that a train works by recursively grouping 3 things from the right, the two interpretations {2}↑4∘↓ and (2∘↑)⍤(4∘↓) are identical.
It is indeed a useful way to read an Agh-fork (or should it be aGH-fork for BQN?)
@Bubbler So I guess you're expecting all users of BQN to experienced APL and J users
Managing expectations then?
@Marshall this looks like a niladic function based on the example. It looks to an APLer like you're defining a function, and then invoking it and getting a printed response.
To make it more clear, You could simply remove the assignment altogether:
(although this makes me wonder, if you have an impure function, say {⎕} in APL, how does that translate to bqn? do you need to still have a dummy argument?)
@nathanrogers the only reason anyone would know what a 'niladic function' even was was if they came from an APL background
@nathanrogers it looks pretty accessible to me providing you know what like, 2-modifiers are for example, but that's covered earlier in the docs
I think the only reason anyone would think {something} could be a niladic function is if they knew about niladic functions and about dfns from APL. If they were truly learning {whatever} was a 'block' not a dfn, there would be no issue
@rak1507 "providing you know..." It is covered earlier, but to a new user, I'm telling you because this is an assumption I made early on that proved to make things confusing, that this is going to confuse a non-array programmer
its not confusing to you because you write trains all day
@rak1507 The difference is the train example is in the "TUTORIAL" which new people are going to gravitate to, and the other section EXPLICITLY REFERS to contextual knowledge
looks like a function call to APLers in a section where he speaks to APL and J knowledge
I'm saying for the first example Make it explicit that this is happening rather than "leaving it up to the reader". I think that's a great learning point for new users and a short circuit for some easily confusing "false intuition"
And the same thing for the other section which speaks to domain knowledge, if I'm coming from APL that looks like a niladic function call, in a section where he speaks about BQN in comparison to APL
@rak1507 I don't think they need more complex examples, I think a literal translation like what I said a moment ago is the right answer, explicitly show the applicaiton
@rak1507 @Bubbler my whole point is that he "leaves (this specific example) it up to the reader to figure out", but I don't think he should if he's targeting new array language users... because this is a potential pit of confusion
the docs talk to APL users, and people with some experience. The tutorial really needs to get people from square 1 to trains, or at least square 1 to reading the docs
But I was asking myself what I would have thought reading it if this was my first array language. There's a lot of assumptions of knowledge, which is understandable, we all use array languages so we forgot what we didn't know
I love the light heartedness, but for the tutorials, taking a bit more time with a few "If you've used APL you can skip to [link]." To not bore the array language users would really help the novice users, and probably appeal to a wider new user base
especially with code_report on youtube getting comments like "WE'RE HUNGRY FOR APL"/"MORE APL PLEASE!!!" people want this stuff, so making it some what amenable to that audience would help its adoption
@rak1507 Right, I think the tutorials are a great read through, gets you up to speed pretty quickly
and its much easier to make sense of the docs after
Does anyone know if there's a way to link with a text name in SE Chat? Like replace the text of the link with some other text?
But it is annoying to remember the interpreter quirks in golfing languages not documented anywhere, so I stopped using them. I just use Python/JS/Haskell/APL/J/Factor/Rust/whatever I feel like it
By going through lots of different practical languages, I learned that, for every programming paradigm, there is some whole class of code that cannot be expressed well within that paradigm
@Bubbler This is why I think rather than a paradigm a notation is required. A paradigm that allows you succinctly and with precision express transformations on data, eliminating the need for paradigms and abstraction
@Bubbler you need to learn you a lisp. its the only language I know of that has a literal notation for program constructing programs. programs that write the program you would have written, and its all at runtime
code_report goes through chapter by chapter of SICP which at the end implements a scheme interpreter in scheme. its a worthy read, and a worthy watch
After learning lisp, I can't help but be an idealist. The only thing is that computers have a much harder time with notation, and the programming community at large is against the very notion of notation, considers it unreadable, fooey to that
@Bubbler that series by code_report is cool becaues he points out how many times Lisp and Scheme legends credit APL with their ideas
Really reveals how much APL was dominant in the programming space in the early days
@Bubbler I've just sent a PR for dyalog-apl-gitpod - I updated the version-numbers. WIBNI it could automagically link to the latest releases w/o having to code specific version numbers?
@ngn oh no, you need to watch code_report then, he accredits reduce to APL, and there's a lot of credit given to iverson throughout the lectures of both MIT and the Burkley lectures which he collects in his SICP overview series
@Marshall right, so I am looking at the primitives graphs, and I for the life of me can NOT understand how under works. I get what it does, but now how it works. For example {𝔾⁼∘𝔽○𝔾} You say that you "undo" G, but actually, if I take the example of under from the tutorial
•←{(2⊸↑4⊸↑)⁼∘('A'-'a')⊸+○(2⊸↑4⊸↑)𝕩 }"abcdef"
and try to spell it explicitly, undo doesn't work
my bacon explodes with all sorts of errors
@Marshall but in the second encoding, I have no idea where W is coming from {(𝔾𝕩)↩𝕨𝔽○𝔾𝕩⋄𝕩}
@rak1507 right, so how does it actually work then? does it do an index match of the range? or are there meta properties of these functions in the interpreter, or what what what? because I don't know @+¨119‿116‿102‿98‿98‿113
I'm gonna have to go through blocks again because it made sense when I was reading it, and now I don't. Its what I get for reading it at midnight on daylight savings day.
@nathanrogers Under is a new big monster that very few people fully understand what it formally does (or at least you can safely assume so). In many cases it is sufficient to think of it in plain English analogy.
(Maybe it's worth having a dedicated article for how it works with all sorts of primitives and their compositions)
@nathanrogers I too kind of don't like APL385's a-zA-Z for long-form text, but the look of glyphs is well worth it for me. (and my BQN386 modifiers went through quite a few refactorings, keeping them the same style in the same bounding box, while keeping them somewhat legible)
@nathanrogers F⌾G x first calls F G x, and then does Magic™ to call some form of G⁼, which includes giving it knowledge of the original x so it could make decisions. If you were G in G←4⊸↑, and from ⌽⌾G↕10 were given 3‿2‿1‿0≡F G x and (↕10)≡x, you have everything you need to create 3‿2‿1‿0‿4‿5‿6‿7‿8‿9
and the Magic™ part isn't very complicated either - in dzaima/BQN, calling a function and its inverse are equally "valid" ways to use a function. Calling a functions inverse with the knowledge of the original value is another equally-as-valid way to invoke a function
@nathanrogers The thing here is 2⊸↑∘(4⊸↑) looks horrible enough to make someone just quit. I think it's better to show a nice and intuitive version, and point out that an explanation is still coming up.
@nathanrogers I guess I'm just going to brute force this by stating that an immediate block isn't a niladic function. It's moderately important to show it has a subject role, which is why the assignment's there.
@dzaima (fwiw I still have a custom userstyle for mlochbaum.github.io, which includes usage of BQN386 among other changes to style and brightening things up)
@nathanrogers yeah, that's what's meant by variable names being case-insensitive. The case only, and only, matters for the syntactic role. (and underscores too, base2←16 ⋄ ba__SE_2 is 16 too)
@nathanrogers A function with 𝕨 doesn't require a left argument. If there isn't one, then 𝕨 is set to ·, called Nothing, so that it basically disappears from expressions. 𝕨𝔽○𝔾𝕩 would be 𝔽○𝔾𝕩 for example. It's covered after the first code block here.
It also lets you use a default left argument easily with 𝕨⊣default.
@nathanrogers There are (at least) two ways to implement structural Under. One is to store recovery information in each primitive as it's defined, and use that to undo them one at a time. The other is to evaluate 𝔾 on an array of indices, possibly expanding them to arrays of sub-indices if needed. dzaima/BQN uses the first and self-hosted BQN uses the second.
The specification contains a complicated proof that structural Under is well-defined, so any method of computing it will get the same result assuming it can find it.
This is better than the Undo-based scenario, which isn't always well-defined. Undo has to be specified in common cases, like requiring that ט⁼be √ so it doesn't give a negative answer.
And yes I still need to write a documentation page on Under.
@rak1507 Also still working on the tacit programming documentation page.
@Adám I tried myself at Roger's problem once again, and i'm a bit disappointed. Now i understood the hint and used it to implement this program:
#define T1(x)for(i=0;i<x;++i)
typedef unsigned long long L;
typedef unsigned int U;
typedef int I;
typedef char C;
C f1(C*x,L n){
U i;C b[256]={0},*c,m;T1(n)b[*x++]=1;c=b+256;
T1(256)if(*--c){m=c-b;break;}return m;}
C f2(C*x,L n){U i;C m=*x++;T1(n){if(*x>m)m=*x;x++;}return m;}
I also wrote some driver code for benchmarking it:
I main() {
const U mx=1<<12;U i;C c[mx];T1(mx)c[i]=rand();
L s1=__rdtsc(),e1,e2;C m1=f1(c,mx);e1=__rdtsc();
C m2=f2(c,mx);e2=__rdtsc();printf("%llu, %llu\n",
e1-s1,e2-e1);return m1-m2;}
(x86 only)
I tried: benchmarking it as 32-bit code, benchmarking it with my march=native on i5 7400, benchmarking it with old architectures, benchmarking it with -O1, -O2, -O3, -Ofast, -Os, i benchmarked it on an ARM machine (sm8150)
and Roger's version is faster only for an old architecture and -O1, for any other combination it's much, much slower
and it's nowhere near being as fast as Roger advertised it to be; as it's around 1,05-1,1 times faster, not 1,5 times faster
@KamilaSzewczyk The simple code beats it because of autovectorization. But on a 64-bit machine you can also do faster by adding eight bytes at a time with SWAR.
@KamilaSzewczyk I was just referencing the phrase "Carbon Copy, of Myself". Someone's only going to get the question right if they think just like Roger. Loop unrolling is a dirty trick but allocating 256 bytes and adding a bunch of constant overhead is just fine.