@voidhawk the indexing required for ↑ & ↓ is different from the one required for ⊃ - "length" depends on ⎕IO, "position" doesn't. There's so harm in handling negatives for "length", which is what ↑ & ↓ do
@voidhawk so negating an index only reverses access in ⎕IO←1. and negative indexes, as a result, are extremely weird in ⎕IO←0
so negative indexes in ⎕IO←0 have the cool property that they're cyclic if you just add/subtract from an index. So, wouldn't it make sense to make ⎕IO←0 ⋄ 3⊃'abc' give 'a'? But what would that look like in ⎕IO←1?
@dzaima Yes, but they are rare, and in all such cases what the computational under would do either makes no sense, or there's a much more obvious way to get that result. So ⍢ should prefer being structural if at all possible (as does my model).
@dzaima ⊢⍢(¯3∘↓) is a no-op with structural under (apply ⊢ to all but the last 3 major cells) while computational under would replace the last three major cells with their prototype because it drops the last three, then re-places three trailing. However, ⍷⍢(¯3∘↑) would be a much more obvious coding of that (assuming monadic ⍷ being "type", {⎕ML←0 ⋄ ∊⍵} or ⊃0⍴⊂.
@Adám A structurally invertable function that has side-effects can't exist. There's no syntax. ⊃, ⌽, etc will implement structural invertability, but nothing user-definable (yet)
a function object can be "used" by calling any of call(⍵), call(⍺,⍵), callInv(⍵), callInvW(⍺,⍵), callInvA(⍺,⍵). This would require adding callInv(⍵, originalW), probably returning null if it can't be inverted (or something like that idk)
That's interesting. The above crashes APL if ⎕← is omitted: Try it online!
@dzaima We have quite a lot of internal material written in preparation for implementing Under (scheduled for 18.0+1). Shall I ask if I'm allowed to email it to you?
yay. now to spam structural inversion definitions everywhere :|
@dzaima oh, in order to implement, say, -⍢(2⊃) i'd also need to add callInvW(⍺, ⍵, originalW) (probably not worth adding callInvA(⍺, ⍵, originalA) though :P)
@Adám output. i literally just added an inversion definition to ⊃ and nothing else :p
@Adám e.g. ⊃.callInvW(⍺, ⍵, originalW) would act (±) the same as ⍵@⍺ ⊢ originalW
for operators it'd require a bit more work due to the boundary between the operator and derived function, but the idea would be that jot should define that (using horrible pseudo-code) A∘f.callInv(⍵, originalW) would act as f.callInv(A, ⍵, originalW)
(using metadot instead of just . would make the expression more APLy :D)
so for (g h)ꞏstrInv (⍵ ⋄ origW) (switching to APL notation!; also realized i've been confusingly calling structural inverse and computational inverse the same..) i'd need to do gꞏstrInv (hꞏstrInv (⍵ ⋄ g origW) ⋄ origW), but that (potentially unsafe & unneeded) g call needs to happen before I know if it's structurally invertible. hmm
(i think that's wrong actually)
^ yeah. (g h)ꞏstrInv (⍵ ⋄ origW) → hꞏstrInv (gꞏstrInv (⍵ ⋄ h origW) ⋄ origW) should be correct, but the problem's the same - i'm not storing (nor can I) the intermediate originals so i need to regenerate them
problem with checking beforehand is that can quickly explode to O(2^N) checks with nested functions. gotta go though ¯\_(ツ)_/¯
(actually if i check before doing anything else, once, it should be fine? but that does mean implementing some structural inversion logic twice)