EmptyArraysAndPrototypes
The following is an email conversation between me (KaiJaeger) and JohnScholes. It did not occur to me that my problem was caused by the prototype:
[Note: For a simple introduction to array prototypes, see LearnMoreApl/ArrayTypeAndPrototype,
Pupil to Master: the Question
Hello John
I am struggling with what looks like a very simple problem:
a1←⍬,¨⊂⍬
concatenates an enclosed empty vector to an empty vector. Some checks:
⍴a1 0 ⍴¨a1 ≡a1 2 ≡¨a1
I came across this when I tried to match a1 with what I thought is the equivalent of the expression. To my surprise I was not able to do this:
(0/⊂⍬)≡a1 0
Although:
≡a1 2 ≡(0/⊂⍬) 2 ≡¨a1 ≡¨(0/⊂⍬) ⍴a1 0 ⍴(0/⊂⍬) 0 ⍴¨a1 ⍴¨(0/⊂⍬)
That's a bit confusing... Certainly you are in a position to make me feel like a moron?
Regards Kai
Master to Pupil: the Answer
Hi Kai,
I think the display function will help us here:
a1 ← ⍬,¨⊂⍬ display a1 ┌⊖────┐ │ ┌→┐ │ │ │0│ │ │ └~┘ │ └∊────┘
So a1 is null vector, whose "prototypical item" is the 1-item vector ,0.
For myself, I find it much less confusing to use the term "prototypical item" than to say "prototype". For example, the prototypical item of the vector 1 2 3 is 0 and the prototypical item of the vector 'abc' is ' '.
So using the above we can see that:
a1 ≡ 0/⊂,0 1 a1≡0/⊂⍬ ⍝ your example (commuted) from below. 0
In other words a1's prototypical item is a 1-item, rather than a 0-item, vector.
How did this come about? Well let's look at the expression who's evaluation we named a1:
a1 ← ⍬,¨⊂⍬
In Dyalog APL the shape of the result of applying a function derived from each is determined by the normal scalar conformability rules. In this case we have a scalar ⊂⍬ on the right and a vector ⍬ on the left, so the result will be a vector of the same shape as the left argument. In other words a null vector.
In Dyalog APL when a function derived from each returns a null array, the prototypical item of this array is determined by applying the operand function, catenate in this case, (to or) between the prototypical items of the argument array(s).
So we have a1 is a null vector, who's prototypical item is 0,⍬ which is just ,0. So a1 is the same as: 0/⊂,0
Now let's comment each of your confusing expressions:
≡a1 ⍝ depth 2, see display output above. 2 ≡(0/⊂⍬) ⍝ also depth 2, see display output below. 2 ≡¨a1 ⍝ a1 is a null vector so ≡¨ is a null vector. ≡¨(0/⊂⍬) ⍝ ditto ⍴a1 ⍝ a1 is a null vector 0 ⍴(0/⊂⍬) ⍝ this is also a null vector 0 ⍴¨a1 ⍝ a1 is a null vector, so ⍴¨ is a null vector. ⍴¨(0/⊂⍬) ⍝ ditto.
Can you spot the subtle difference between these two displays:
display a1 ← ⍬,¨⊂⍬ ⍝ prototypical item: ,0 ┌⊖────┐ │ ┌→┐ │ │ │0│ │ │ └~┘ │ └∊────┘ display a2 ← 0/⊂⍬ ⍝ prototypical item: ⍳0 ┌⊖────┐ │ ┌⊖┐ │ │ │0│ │ │ └~┘ │ └∊────┘
Dyalog is, I think, unique in its treatment of ¨ (each) applied to null arrays. The idea is that only the function (not the left or right argument) can know what the prototypical item of the result should be.
display ⊃¨ '' ⍝ prototypical item is ' ' ┌⊖┐ │ │ └─┘ display ≡¨ '' ⍝ prototypical item is 0 ┌⊖┐ │0│ └~┘
And for non-trivial functions, the only way to decide this is to apply the function to (or between) the prototypical item(s) of its argument(s).
display {⍵ ⍵}¨ ⍬ ┌⊖──────┐ │ ┌→──┐ │ │ │0 0│ │ │ └~──┘ │ └∊──────┘
This is fine for "is-y" functions, which don't have side-effects, but bad news for "do-y" functions, such as ⍬ ⎕FAPPEND¨ tie, which do.
Certainly you are in a position to make me feel like a moron
Not at all. These are tricky issues. It always takes me a while to figure these things out.
Best regards, John.