1. 4
    1. 2

      I’ve encountered this exact challenge in implementing Uiua. It’s something that anyone implementing a language with rank-polymorphic arrays will likely have to solve.

      Interestingly, Furthark’s implementation, which involves replicating lower-rank arguments, means that these operations work on the trailing axis. This is in contrast to most of the array languages, which do these operations along the leading axis:

      J:

         10 20 30 + 3 3$i.9
      10 11 12
      23 24 25
      36 37 38
      

      BQN:

           10‿20‿30 + 3‿3⥊↕9
      ┌─          
      ╵ 10 11 12  
        23 24 25  
        36 37 38  
                 ┘
      

      Uiua:

          + 10_20_30 ↯3_3⇡9
      ╭─          
      ╷ 10 11 12  
        23 24 25  
        36 37 38  
                 ╯
      

      The Uiua implementation (and likely the others?) uses recursion to resolve rank mismatches, though it makes sense why this might not work for Futhark.

      While the post does not actually show any outputs, the way it is described suggests this same operation in Futhark would give:

      10 21 32
      13 24 35
      16 27 38
      

      I was actually surprised to learn that APL does not allow this at all:

      RANK ERROR: Mismatched left and right argument ranks
            10 20 30+3 3⍴⍳9
                    ∧
      
      1. 1

        While the post does not actually show any outputs, the way it is described suggests this same operation in Futhark would give:

        10 21 32

        13 24 35

        16 27 38

        Yeah, this is correct and is inherent in the design of the system (i.e., rank polymorphism is just inserting map operations automatically). If you wanted to have it match the other examples provided, you’d have to transpose.

        I’ve encountered this exact challenge in implementing Uiua.

        I’m curious what you do in Uiua—what’s the approach?

        1. 3

          If the arguments are the same rank, then it just does the operation element-wise of course. Otherwise, it recursively works its way down the dimensions until one of the arguments is scalar, then operates on the scalar and the rest of the elements in that row.

          All the shape checks are done at runtime. Though once the checks are done, you still usually get the performance benefits of auto loop vectorization. Nothing close to what can be achieved on a GPU though.

          Uiua also has a couple features that make it a bit more complicated, like ⬚ fill, which extends the rows along some dimensions if necessary. This example fills missing elements with 0:

              ⬚0+ [10 20] [1 2 3 4 5]
          [11 22 3 4 5]
          

          In Uiua, you can achieve the same result as Futhark with ¤ fix, which ends up being implemented similarly to how the article describes, replicating one of the arrays:

              +¤ 10_20_30 ↯3_3⇡9
          ╭─          
          ╷ 10 21 32  
            13 24 35  
            16 27 38  
                     ╯