@Marshall When you added « and », were you aware that Iverson Notation had something similar? ̥↑ and ̊↓ would shift left and right ⍺ steps, padding with 0s, in parallel to ↑ and ↓ which would cyclically rotate left and right.
@Marshall Sure, and I agree with your definitions, and ⥊⟜0⊸» is neat enough.
I kind of envy J's new Fold family. Would it make sense to make dyadic f⍀ (and f\) take a control number on the left? So 1f⍀ would be fold single forward, ¯1f⍀ would be fold single reverse, 2f⍀ would be fold multiple forward, ¯2f⍀ would be fold multiple reverse.
@Adám I'm inclined to say that if you want that sort of control you should just switch to imperative programming. Those codes would be hard to understand if you ran across them in a program.
@Marshall Well, at least the ability to iterate and collect results over a list without recalculating everything for every step, would be nice to have. f⍀ really makes no sense for non-optimised fs.
The first is what we need, the second is the current `\`, the rest can be derived from reversals (though having the last one as a built-in could help for efficiency)
This also allows not having parentheses when needing a scan or reduce atop something in a train, because there'll always be a left argument one can give ⍀ to achieve the same.
@Bubbler ⍣ has so many issues. It only looks at the previous value, and so gets stuck on things like -⍣=1 instead of checking for loops. No option of giving intermediary values (dzaima's ⍡). No way to repeat 0 times if stop condition already applies.
Something that makes me sad is that so many of the primitives added to the original APL have issues.
In fact, very few of them don't have flaws.
But I should cheer up. A lot of issues can be fixed with relatively lightweight additions.
@Bubbler Extended and Prime are where I've been collecting ideas.
@rak1507 For ⌶ I agree, but when multiple functionalities are clearly closely related, then I don't mind, especially if a neat scheme can be constructed to unify them. This, I find to be the case for ○, the 12-fold way of combinatorics, the left arguments to ⎕DT and ⎕DR, my proposal for extending ⌺, and also here.
@rak1507 Right, but can you come up with 8 new glyphs to complement /⌿\⍀? J's recently introduced F.F:F..F.:F:.F:: which are basically just using Braille numbers.
@rak1507 I'd rather a clean break language than more of that nightmare. Have you ever debugged code that jumps between various settings of ⎕ML and ⎕IO and ⎕WX and ⎕FR and ⎕CT?
@rak1507 1) Scan is hardly the only thing that needs fixing, but if we are to add a new value to ⎕ML for every incremental fix, it will become unbearable. 2) Some operands of scan are optimised, with really good performance, and many idiomatic APL expressions use those. Reversing scan would break a lot of code. 3) Proper consistent design takes time. If we start changing APL here and there, we'll have a mess, while additive changes can arrive fast, but incrementally.
But if we add a left argument such that the existing behaviour becomes as if a specific left argument was the default, then it isn't a huge mess. That's what my idea was, that existing f⍀ stays, and is equivalent to 1f⍀ but with additional values to get the exact behaviour desired. Future newcomers won't notice that anything is strange, other than maybe wondering why the default is as it is.
And while we're at it, why not make 5 6 7 be the same but with "insert" rather than "reduce"?
BQN's primitives are of course much more elegant when if comes to all this, but here we're discussing what we can do to help APL a bit in the stiff competition from BQN ;-)
J is pretty close to ascii-only APL, except for some terminology, favoring rectangular simple arrays much more than nested (boxed) ones, and a few handy built-ins (some mathematical)
@Bubbler would you say rectangular arrays are "simpler" (under any definition) than ragged?
from an implementer point of view, multidimensional rectangles require worrying about storing the variable-length shape somewhere. also, what should be a simple loop often turns into recursion.
with ragged arrays, you only have to worry about scalars and vectors. there's only one dimension to store. there's usually only one loop to process the data.
i think it's simpler from a user's point of view too. with apl i tend to switch back and forth between mixed (matrix) and split (vector of vectors) form because often it's not obvious which is more convenient for the particular task.
<moon-child> @ngn considering that apl has both rectangular and ragged arrays, surely j is simpler than apl? (Though I do agree that k is simpler still)
Of course it's not really simple to use or implement. I did feel some amount of hurdle while I was trying to grasp the function rank and auto-rectifying and all
<moon-child> @Bubbler in j, all arrays are rectangular and of uniform type; that type may be a box, which may contain objects of varying type, but the array itself is still rectangular
<moon-child> you have to explicitly unbox a box before you can use its contents, though. So you can't say 1 2 + 5 6;9, you have to say 1 2 (+&.>) 5 6;9
@Razetime Yeah, I got tired of typing the long domain again and again. Problem is, I'd like the wiki itself to present such URLs, but I'm not sure how to do that.
@Razetime There is no .time, but razeti.me is available at $2.47 for the first year right now, with $12.71/yr afterwards. Of course, median income in India is about a 20th of that in the UK, so that might still be out of your reach.
Hello folks. Is there a description of what tradfn headers can do somewhere?
Particularly what the does the `;` do (eg. in `res←echo arg;context;event`). Is it a way to use multiple arguments, via some kind of pattern-matching on a vector?
Linked from some archived conversation/lesson from this chatroom was https://help.dyalog.com/18.0/#Language/Defined%20Functions%20and%20Operators/TradFns/Model%20Syntax.htm but that doesn't answer it for me.
Looking in the docs some more, I see https://help.dyalog.com/18.0/#Language/Defined%20Functions%20and%20Operators/TradFns/Locals%20Lines.htm
So the `;` is for specifying local names (do I have to do that? will all unspecified ones be made global instead?)... so I assume I have to do the `x y z←argument` myself then if I want to pass multiple arguments (3+) to a function
@MartinJaniczek Yes, (btw, APL Wiki is good for finding out about APL) you have to localise all names you don't want global, except those otherwise mentioned in the header (result, function name, argument names). You can force the function to take a 3-element right argument as per ^.
@MartinJaniczek Instead of/complementing the locals lines (you can have additional such lines after the header, e.g. categorising your local names), you can dynamically localise names using ⎕SHADOW which takes a list of names. So while you can't give it a pattern, you can generate a list of all names you want, and apply ⎕SHADOW to that.
@Adám I'm sorry but when I looked at that APL Wiki page before it didn't help me much. But, the act of writing here and doublechecking myself on the Dyalog docs made me find it help.dyalog.com/18.0/#Language/… . Thanks @dzaima also for spelling it out explicitly
@Adám i see what you mean, but technically that's not a counter-example. if you're willing to pay, let's say $10, surely you're willing to pay $4.60 too.
@MartinJaniczek Actually, allowing patterns is a great idea. We've been thinking about how to make tradfns less awkward, and the lack of auto-localisation is probably the biggest issue for most people, and a major reason for (ab)using dfns. I'll bring up your idea.
Is there any way to assign a variable conditionally (without evaluating the alternative value)? The easiest thing I could come up with so far was to make another dfn and use guards inside that.
@user Hm, sounds like you're recursing and then at the leaf, you do something else. Can you not phrase it like that and immediately return (on the guard) instead of having to assign the result?
@user (Correction: Monadic operator.) No idea. Tradfns can return a function result too. Dfns can't. Why? No idea.
Tradfns (while maybe state-of-the-art in 1960) have multiple issues: Dynamic scoping, global assignment by default, cannot be nested, know their name. But afaik, with those things in mind, they have no gotchas.
@ngn You keep saying that, but show a realistic example of where that actually matters. And by realistic, I mean keeping best practices, including always passing arguments instead of relying on globals.
@ngn That's just plain counterfactual. John Scholes basically sneaked them into the product, and if someone had actually bothered to write down the rules for how they work, they would have found obvious spec contradictions. I can't call that "well thought through".
@Adám it still couldn't parse whether a f←1 is reading a function f or assigning to (a f). (even with requiring multi-assignment parentheses, (f a)←1 is still technically possible)
@dzaima I'd be fine with it ignoring the possibility of naked multi-assignments. And (f a)←1 with f a function isn't currently permitted (though there's a bug you can exploit if you go out of your way to do so). Maybe a f⍛← would be a good thing for selective assignment, along with a(f←) for modified assignment.
@dzaima Exactly, and that's an important point. If you are strict about never relying on semi-globals, then the exact scoping rule doesn't actually matter.
> The Lisp family splits over the use of dynamic or static (a.k.a. lexical) scope. Clojure, Common Lisp and Scheme make use of static scoping by default, while newLISP, Picolisp and the embedded languages in Emacs and AutoCAD use dynamic scoping. Since version 24.1, Emacs uses both dynamic and lexical scoping.
So Scheme appears to be the first Lisp with lexical scoping. It came out in '75.
@ngn He puts "m-expression" on the APL side. Did you know that John McCarthy saw s-expressions as an implementation step on the way to m-expressions, which were his goal. He just never got around to finish implementing Lisp. I don't know if he ever heard about K, but I imagine he would have liked it.
@Adám lexical scope is crucial for closures and any higher-order functions not breaking when used out of where they were created. This also applies to a module system that stores functions as values. maybe these problems don't apply as much to APL, but they are important for most functional programming
I don't mean to criticise FPers. Not at all. I have great respect for FP. I'm just trying to be pragmatic and salvage what can be salvaged. (ngn would of course support abandoning rather than salvaging.)
@Wezl Because they'd never do anything where it makes a difference. From my experience, most programmers do fairly simple things when it comes to the structure of code and data. Actual data processing algorithms may be complex, but inter-function calls aren't.
@Wezl I've run quite a few workshops for APL programmers of our actual paying customers, and I'm repeatedly surprised by their struggles with very basic problems. Almost everything they do are fancy applications of dyadic ⍳ and ⌷. Things like FP and scope and closures are way over their heads.
For all our wanting to create the perfect array language, we often forget to be pragmatic, including realising that the customers don't think about the "beauty" or "consistency" of the language, or marvel at the "versatility" or "cleverness" in the design of a primitive.
Can anyone help me with this answer? The challenge is to interpret lisp. To evaluate s-expressions, I'm currently passing around a context containing the names of variables and their values, but I was considering making everything a tradfn (except the function returned from LAMBDA) so that parameters to lambdas are in scope. Does that seem like a good approach, or would tradfns be too verbose (and complicated)?
Thanks for suggesting it though - I can't expect you to intuit how my answer works, and anyway, it'll be useful in the future.
While the lisp in dfns looks very promising, I think I'm going to go stick with my answer for now. It'll probably be more of a headache for me to extend it anyway.
@user Here's a golfing tip that fails under TIO's Code field due to a bug in the interpreter mixed with the hack TIO uses to enable scripting in 17.1. It works in 18.0, and in the Input field (the session): Define r←⎕R'_' and use it to replace the two instances of ⎕R'_'⊢ (notice the trailing ⊢ can be removed then).
@dzaima The fact that ⌊´⟨⟩ is defined to be ∞ and not a character is a decent reason not to allow ⌊ on characters. Still not sure about restricting it though.