Live data from Hacker News

Suggestivity and Idioms in APL

sacrideo.us

21–24 of 24 posts

Re: Suggestivity and Idioms in APL

#21

I walked in, realized just how advanced this was and then proceeded to read through the same way I pan through the Abstract Art section of the museum. I freaking love APL and would love to learn it, but it is so incredibly jarring not knowing anything about the syntax.

I share your sentiments and it is incredible. To an outsider this might as well be a parody of programming blog posts. Such impenetrable syntax taken in stride: We can improve this approach by recognizing that we can handle the edge cases by using a catenation: ⍸0 1⍷0,⍵ ⍸1 0⍷⍵,0 Notice immediately how much better this feels. Ah yes, how immediate is the feeling, much better, indeed. APL is so ridiculous, you have to…

The syntax is actually quite simple, but you may be confused by the unfamiliar symbols and lack of syntax. 0 1 is simply [0,1] in JSON and ⍵ is the argument name. The other symbols cirrespond to prefix and infix operators. Compare the following JS expression which is syntactically (but not semantically) equivalent to the APL expression below it:

    - [0,1] * 0 ** w
    ⍸  0 1  ⍷ 0 , ⍵

Re: Suggestivity and Idioms in APL

#22

I walked in, realized just how advanced this was and then proceeded to read through the same way I pan through the Abstract Art section of the museum. I freaking love APL and would love to learn it, but it is so incredibly jarring not knowing anything about the syntax.

I'd be happy to teach you; just let me know.

The syntax is in fact quite simple, and the semantics of the core language is mnemonic and limited, so I believe that a reasonably intelligent person can learn the entire core lanuage in 24 hours of dedicated studies. Subtract a few if the student has a knack for mathematics, but add a few if they have a CS degree.

Re: Suggestivity and Idioms in APL

#23

I walked in, realized just how advanced this was and then proceeded to read through the same way I pan through the Abstract Art section of the museum. I freaking love APL and would love to learn it, but it is so incredibly jarring not knowing anything about the syntax.

I share your sentiments and it is incredible. To an outsider this might as well be a parody of programming blog posts. Such impenetrable syntax taken in stride: We can improve this approach by recognizing that we can handle the edge cases by using a catenation: ⍸0 1⍷0,⍵ ⍸1 0⍷⍵,0 Notice immediately how much better this feels. Ah yes, how immediate is the feeling, much better, indeed. APL is so ridiculous, you have to…

As for the semantics, here are the equivalent JS functions:

    I =    y  => [...y.keys()].filter((e,i) => y[i])            // ⍸y Indices of trues in y
    E = (x,y) => y.map((e,i) => x.every((e,j) => e == y[i+j]))  // x⍷y mask indicating indices where x Exists as a sub-array in y
    C = (x,y) => [x,y].flat()                                   // x,y Catenate x and y into a single array
    
    w = [1,1,1,1,0,1,1,1,0,1,0,1,1,0,0,0,1,1,1,1]
    I(E([0,1],C(0,w)))  // [0,5,9,11,16]
Now, if I, E, and C were prefix/infix JS operators, with x being the left argument (if any) and y being the right argument, we'd write:

    I([0,1] E (0 C w))
All APL operators have long right scope, so we don't need to parenthesise right arguments:

    I [0,1] E 0 C w  // same syntax as APL's ⍸ 0 1 ⍷ 0 , ⍵
Alternatively, you can see the infix operators as being methods of all types:

   I([0,1].E(0.C(w)))
Now we remove all the .() noise:

   I [0,1] E 0 C w  // same syntax as APL's ⍸ 0 1 ⍷ 0 , ⍵
Wasn't that hard, was it?

Re: Suggestivity and Idioms in APL

#24
post #14
post #10

Earlier quoted context omitted.

[flagged]

Now that I've had the opportunity to actually run the code and test it against the input from the article, here's a corrected version: fn find_zero_one_indices (input: I) -> Vec where I: IntoIterator , { let input: Vec = std::iter::once(0).chain(input.into_iter()).collect(); input.windows(2) .enumerate() .filter_map(|(i, window)| { if window == &[0, 1] { Some(i) } else { None } }) .collect() } There's a limit to how…

I believe the point of the article can be understandable without understanding the way the code works at all and largely without understanding APL syntax - it was not about comparing the algorithms, but about how the related solutions have code which looks related which then affects how you explore the problem space. (The problem space being: an array of bools where boolean trues are grouped together, starting with finding where those groups begin and end). Here's another attempt to restate the article: without any detail about how the code actually functions to achieve the tasks I just tell you that these two lines of code do related things:

    ⍸0 1⍷0,⍵
    ⍸1 0⍷⍵,0
You can see that from the first line to the second line, on the left the 0 1 swaps around to 1 0 and on the right the 0,⍵ swaps around to ⍵,0 but the overall shape/size/length/structure/symbols are the same between the two lines. That is, I state that they do something conceptually similar and you can see they look visually similar; they do something with the concept (start, end) swapped and visually have something swapped around. Next, looking at these lines might make you wonder whether changing the digits on the left side to 0 0 or 1 1 would do anything related and interesting with groups of bools? Or whether changing the digit that goes with ⍵ to a 1 would do anything interesting in the problem space? The visual look of the code suggests these possibilities - they were changed from one line to the next, and you could continue the changes while keeping the structure. The shortness of the code makes it very easy to mentally compare the lines and very quick and low effort to try the changes and find out.

By contrast, these two lines do the same pair of tasks as the above lines:

    ⍸(⊃⍵)@0⊣¯1⌽0 1⍷⍵
    ⍸(⊃⌽⍵)@(¯1+≢⍵)⊢1 0⍷⍵
They are visually far more distinct from each other, structurally more complex with parentheses; not comparing with the previous pair, just between these two lines there are different symbols and different lengths and new sections being inserted. It's no longer visually so clear that they do related things. Looking at them there is no strong suggestion how you might continue similar changes to find a third or fourth pattern which may do something interesting with groups of bools. And the code is more complex so you are already spending more of your finite mental effort thinking about how the code works, rather than exploring the problem domain. Now compared to the previous pair they are longer, less elegant.

From your translation to Rust you have moved away from the high level view of groups of bools as soon as you name the function "find_zero_one_indices" as if the zero and one are the significant things you care about instead of incidental implementation details, one way to find the groups; already with that name there's no tempting reason to rewrite it to find the "one_zero" indices (you might name the function "startingIndicesOfGroups" then look in the code to wonder how to find the ending ones?). And if you do, once you have written it again for the one_zero indices and you've got 32 lines of code, ~500 characters with ~158 symbols, how visually clear is it at a glance what changed to match the start and end? And are you going to bother writing a "find_zero_zero_indices" from a moment of curiosity to see whether that makes an interesting group pattern when it takes another 16 lines? Are you going to be bothered to copy half a dozen variations? Once your head is full of Vec and chain(.into_iter()).collect() have you lost sight of the group start/end altogether while you wonder if any of your code will trip the borrow checker?

The article then moves on to this pair:

    ⍸2⌿⍵⍪0
Where you can see that on the left the less-than changes to greater-than, and 0⍪⍵ swaps around to ⍵⍪0. These quickly suggest that you could change the 0 to 1 and see what happens, or change the to any other boolean comparison like ≤ ≥ or operation (AND,OR,NOT) and it's only ten characters to try them out and explore what they do. Compared to the first pair these are a similar length, but they have more things to change while keeping the overall structure and most of the symbols, which makes them more interesting and more generally useful structures.

So the claim of the article is not about which one is more modular, or which one is a nicer algorithm, it's about which pairs make the similar work look similar, where the code is short enough that it's easy to compare in your head with little effort, and where the changes between them are a simple enough pattern that the code 'suggests' how you could continue the pattern of changes. And claims or implies that APL is particularly strong at this because of the tersness of the code.

Post reply on HN