That feeling when you spend a few hours on an optimsation that is supposed to give you a 3x performance increase across the board, and you see a 20% decrease in performance instead.
Seems like my fixes broke JVM compiler inlining, making previously inlined code into dynamic calls. The performance improvement from the excessive computations I removed was absorbed by the much slower dynamic calls.
I am very surprised by the fact that KAP is consistently faster than GNU APL, sometimes by a huge margin. Even though for simple operations GNU APL has reasonably optimised code paths.
@EliasMårtenson GNU APL is sometimes horribly slow. E.g. when they added ⎕DLX (knuth's Dancing LinX), we found that its performance was far worse than our APL implementation.
I don't understand why it's so much slower than plain +. When I played around with it I assumed that it had a special optimisation for +/.
20 seconds in KAP, and half of that time is spent allocating new stack frames when calling the closure.
I really need to do something about the slow stack frame allocations. Only 14% of the 20 seconds is actually spent in the computation part.
@Adám Yes, they kind of work for simple use cases, but the lack of lexical scope is a problem. The fact that you cannot put more than one expression in them is also annoying, forcing you to over-use ⊢ and ⊣.
@Adám Was the ⎕DLX implementation slower than the APL implementation on Dyalog, or the APL implementation running on GNU APL?
@Adám It's mostly C++. There are libraries written in APL itself, but not that much. For example, the functions in the SQL library are mostly one-line wrappers to call into the native backend, with a few functions that have a bit more non-trivial APL code. I know this because I implemented the SQL support :-)
@EliasMårtenson [citation needed]. GNU APL gives the same result as Dyalog for 2 2/¨'ab' (wiz. 'aa' 'bb') whereas APL2 gives something else ('aaaa' 'bbbb'). APLX does like APL2.
@Adám I think that warrants a post to the GNU APL mailing list.
I'm not sure I understand the APL2 result here. KAP gives the same result as GNU APL and Dyalog, and my implementation is based on the ISO spec. A lot of GNU APL is also based on that spec, so perhaps that's where it comes from.
How does APL2 evaluate that expression. I don't understand how you'd get anything but "aa" "bb"
@EliasMårtenson Well, it was (kind of) intentional. "Kind of" because, as opposed to what is stated there, the APL2 documentation does makes it abundantly clear how / etc are treated.
@EliasMårtenson Very simple. / is in APL2 always a monadic operator, equivalent to {⍺⍺/⍵}.
So 2 2/¨'ab' is the same as (2 2/'a')(2 2/'b') which is (4/'a')(4/'b')
@Adám I'm sorry. It may be simple to you, but I feel pretty stupid. How can / be interpreted as an operator when there is no function to the left of it?
@EliasMårtenson Operators take operands. An operand can be either a function or an array. For example, Rank (⍤) takes a function left operand and an array right operand.
@EliasMårtenson In Dyalog it doesn't, no, because it is a weird (but useful, and sometimes frustrating) hybrid function/operator. But in APL2, it is a regular monadic operator.
@xificurC Hi there. Interested in APL? APL evaluates from right to left, just like your user name!
@Adám My parser is a single-pass left-to-right LALR parser, so dealing with operators that take numeric arguments is... somewhat difficult. I have some ideas how to deal with it though. I'll need it in order to be able to handle ⍤ specifically.
@Adám Oh, that they can. What you cannot do is to redefine a symbol's role while the code in question is running. You have to predeclare a symbol's role before the code is parsed.
@EliasMårtenson ideally operators just wouldn't care at all whether the operands are arrays or functions, and deal with them with some common supertype. How would custom operators deal with the function/array difference, though, is a good question
@xificurC J has some rationalisations and additions to original APL (though some modern APLs are adding many things from J) with special emphasis on tacit programming. Also, J uses ASCII only, often resorting to bi-glyphs or even tri-glyphs.
If you want to dynamically refer to functions as values, you need to use the λ symbol. However, this is rarely needed (normally only used in defsyntax definitions)
and to put things into perspective - I'm a programmer for ~7 years, mostly backend, had the luck to write clojure for the past 1-2 years. I am wondering, is j/apl usable in this sector? My typical "problems" are working with the database, scheduling/spawning/managing jobs/processes, handling http (json), writing tests to already written shitty java code :)
@Adám The dual role of / in handled by a special rule in the parser: After seeing a function, the operator role take precedence. So, if you have x/y, the parser first sees x, determines it's a value (because it's not been declared as a function), anything that comes after is parsed as an operator if it can.
@Adám It can take a name of a function, but the parser must know at parse-time that the name represents a function.
So x/y is parsed as VALUE FUNCTION/ VALUE if the role of x and y are unknown. If you have previously declared x as a function (for example by a function definition instruction), then it will be parsed as FUNCTION OPERATOR/ VALUE
@xificurC Speaking about APL, as I don't know enough about J: Of those things, handling JSON over HTTP is certainly done and easy (we have a ready-made HTTP server, and a built-in for JSON-to-APL/APL-to-JSON conversions). Interfacing with the world outside sometimes easy, sometimes hard ― depends on what exactly is being done. We have an easy-to-use SQL interface too.
So yes, the parser is affected by previous definitions. It's simialr to C in that way, where previous definitions (esp. typedef) can affect how the parser parses an expression.
@Adám It's been a while so I don't recall exactly my line of thinking, but at the end of the day I decided that the limitations wasn't worth the benefit. I figured if you wanted to introduce such rules, it would be a trivial extension to the type classifier to take the name into account. I think it could be exposed to the user so they could implement their own type classifier in KAP itself.
@Adám GNU APL uses ⍶ and ⍹ for function operands. I'm going to use the same syntax. This is because of of how the tokeniser works. A character can either be single-character or multi-character. ⍵ is single-character, which means that ⍵⍵ is parsed as a single symbol. The reason is that everything is a symbol, even + and /.
@dzaima The question is "when" should they be treated the same. The dual-behaviour of / shows how the entire expression must be parsed completely different depending on context. If that context isn't known at parse-time, you have to re-parse every time you execute a function, which is why GNU APL is so slow for defined functions.
@dzaima As long as the context is known, it will work. I mean, the parser knows about lexically scoped local functions. So a symbol can be treated as a value in one place, and inside a function the symbol can be treated as a function.
@EliasMårtenson you said that handling operators with array arguments is somewhat difficult, whereas i'm surprised it's not trivial (beyond the need to change the operator interface)
Those particular problems I was referring to was due to the simpler implementation I went for before I thought about the need for numeric arguments to operators. The only one defined in the ISO spec is ⍤ so I was considering handling that one as a special case.
another option would be to be able to specify the operand types in the dfn itself, aka dfn headers. (though i don't have any syntax ideas for that either)
@dzaima I might pass the operand as a value, and if it's a function it becomes a function value. I know that may sound like it makes no sense, but the short summary is that to call it as a function you need to use the APPLY operation.
CMP: What would be a good way to describe parens and brackets that syntactically (i.e. not inside an inner dfn) have a line break or a diamond between the opening part and the closing part? E.g. [ 1 2 ⋄ 3 4 ]. "Broken"? "Interrupted"? "Segmented"? Something else?
@rak1507 An array that has two major cells. Equivalent to ↑(1 2)(3 4) (with the Dyalog meaning of ↑).
@EliasMårtenson No, that is usually pointless, unless the leading statements have side effects, but then you might as well put that outside the bracket.
@EliasMårtenson You're not the only one thinking that simply surrounding a strand with some paired symbols can work, but it is a mistake leading to a contradiction. If « 1 2 3 4 » is 1 2 3 4 then clearly a←1 2 3 4 ⋄ « a » must be 1 2 3 4 too. However, if you say that « 1 » is ,1 then « a » must be ,⊂a. However, for all but simple scalar as a ≢ ,⊂a so this notation definition contradicts itself. QED.
@EliasMårtenson having a separate character for the notation is quite useful (it's what BQN does with ⟨⟩), allowing for trivial 1-item and 0-item vectors, but takes up 2 more keys on the keyboard (if ignoring the need for another 2 chars for matrix notation)
I also want to work on making the REPL more dynamic. Being able to work with some kind of graphical representations of objects would be neat. It's a topic I'm very interested in, and my other project I'm working on is all about that: peertube.mastodon.host/videos/watch/…
@Adám I figure APL specifically can adopt the APL wiki logo. The question still remains about whether topanswers.xyz/apl should be APL or Array languages. Given the recent discussions here, I'm still undecided which way to go. The stream of conciousness here is still active and should and will continue, but I still think new people might benefit from the compartmentalisation of topanswers
@RikedyP Programming language logos generally have the name or initial(s), or a ubiquitous language phrase, or, for functional programming languages, a λ. I can't off-hand think of any logo for a language family.
@Adám Maybe r/apljk is really the place, but topanswers still has the appeal of forum posts + comment section + interpreter integration. The logo is just the last piece before we can say the design is good to go. Maybe considering the APL terp is the only one implemented, could go with just APL for now? What about the old Dyalog logo that used to be a 3D APL?
@RikedyP How about going with the perspective view of a cube (looks like a hexagon when flattened)? Each language can in turn adopt a variation thereof, while the language family uses a more generic cube (maybe made of smaller spheres, APL Wiki logo style)? NumPy, Dyalog APL, and APL2 already have such logos:
@dzaima Can you render 8 spheres in a 2-by-2-by-2 configuration with a perspective similar to the NumPy logo?
@Adám I'm thinking of something like the NumPy cube, but with a different number of segments along each axis. In a large version, the "front" face (bottom left; maybe angled more towards the viewer but maybe not) would have APL written in three segments and other segments would have J, K, etc. So it'd be 1x3x2 I guess.
@RikedyP I would post on topanswers, even though I won't use reddit or non-chat stackexchange (horrible website and company, respectively).
@dzaima FYI Dyalog doesn't always enforce floating-point rounding on integer sums: for example +/(2*23 10)/2*30 0 is larger than {⍺+⍵}/⌽(2*23 10)/2*30 0 (reversing because +/ with singleton result goes in increasing index order).
@EliasMårtenson Yes, maximum bound (number of elements) is given by the machine word size, so 2*64 elements on a 64-bit interpreter. That limit can only be reached with booleans, as otherwise you'll run out of addressable memory.
@dzaima Correct. Array indexes in Kotlin are limited to 32 bit integers. However, I could easily implement support for longer arrays by moving to long. Arrays would be backed by multiple underlying vectors in the case where data is actually stored in an array (things like ⍳ returns synthetic arrays)
Is there an easy way to list all of the APL functions/operators? ⎕AV lists some (but not all) and is surrounded by alphanumerics and punctuation. Essentially the output I'm after is a vector of all of the glyphs on the language bar in any order (without having to type them out of course ;) )
@JamesHeslip i've written code a couple times that goes through ⎕AV, getting the nameclass of each, but ⎕AV doesn't include all chars, and things like `⎕⍞∇→ need manual filtering out
Is there an easy way to list all of the APL functions/operators? ⎕AV lists some (but not all) and is surrounded by alphanumerics and punctuation. Essentially the output I'm after is a vector of all of the glyphs on the language bar in any order (without having to type them out of course ;) )
Thought some of you might enjoy it. It is currently halfway done and it is supposed to be suitable for skilled and non-skilled APLers, as well as people who are and are not familiar with Neural Networks. If you watch it, be sure to let me know your feedback and ideas for improvement.
I was trying to "curry" a function in GNU APL, however it doesn't seem to work?
For example:
(1∘+) 1
SYNTAX ERROR
(1∘+)1
^ ^
What am I doing wrong? Is ∘ the wrong glyph to use?
@Marshall Oh, right. You can fill the empty slot with an O.
@Marshall How does it look if you rotate the cube so the nearest corner is directly facing the camera, like in the NumPy and Dyalog logos? Oh, never mind, it isn't a cube.
I think they're pretty closely related. Julia is the only one not designed entirely around array programming (and should be behind NumPy now that I think about it).
@Marshall Yeah, I think you could remove I and Q, instead stretching J and K to 1×2. It'd give a nice progression that the NumPy and Julia letters are 1×1, while J/K/R are 1×2, and A/P/L are 3×1.
@Adám The current version uses the faces of a 1x2x3 array. If you merged squares along the top, it wouldn't be an array at all, just stickers on a cube.
@Marshall Hm, good point. Maybe a 3×3×3 array with plenty of empty squares. The left-facing side could say: ⎕⎕⎕/APL/⎕⎕⎕, the top would say J⎕⎕/⎕K⎕/⎕⎕R, and the right side would say NumPy/⎕⎕⎕/Julia (two long names on 1×3 stickers)?