COMMUNITY

How to pattern match an array with unknown length


(Skyler Parr) #1

I’ve been digging into pattern matching more in Haxe. I don’t see anything in the docs or cookbook about matching on arrays with unknown length. Here’s a code example of what I’m trying to achieve:

function getSpecialTail(array: Array<Int>): Array<Int> {
   return switch(array) {
      case [4, 8, 12, tail ...]:
           tail;
       case _:
           []; 
   }
}

trace(getSpecialTail([4, 8, 12, 5, 8, 7]));
>> [5, 8, 7]

I’m using the ellipsis to signify some operator to let the compiler know to match on any number of elements left in the array. Is there a way to do that?

Thanks.


(Jens Fischer) #2

According to the manual, there’s no way to do this (“arrays can be matched on fixed length”).

I imagine if Haxe ever gets an OCaml target, you would be able to use that :: operator in pattern matching and also on arrays (case [4, 8, 12] :: tail:).


(Vitaliy) #3

you can use this workaround. Not really efficient thou

function getSpecialTail(array: Array<Int>): Array<Int> {

   function slice(array:Array<Int>, length:Int):Array<Int> {
       return array.slice(0, length);
   }

   return switch(array) {
       case slice(array, 3) => [4, 8, 12]:
           return array.slice(3);
       case _:
           []; 
   }
}

try it


(Simon Krajewski) #4

I’m not generally opposed to supporting this, but there are some aspects that have to be considered. Without a length check, switching on an empty Int array on static targets would match [0, ...] and [0, 0, ...] etc. if we simply access each element of the pattern in the array. This means that we’d need a >= check for each pattern, and that in turn leads to grouping and ordering considerations.

Maybe this would make a good haxe-evolution proposal.


(Juraj Kirchheim) #5

FWIW it’s implemented here: https://haxetink.github.io/tink_lang/#/implementation-sugar/array-rest-pattern
As for allowing the ...rest notation (instead of @rest rest), it would require prefix ... to become a syntactically valid unary prefix operator, as suggested here: https://github.com/HaxeFoundation/haxe/issues/7234


(Simon Krajewski) #6

How is this transformed? Does it support multiple @:rest rest in the same pattern? That would seem to increase complexity significantly.


(Juraj Kirchheim) #7

Nope, multiple @rest is not supported. It might actually have multiple solutions, so that’s a bit of a problem.

As for the transformation, it’s pretty dumb actually:

case [a1, a2, ..., aX, @rest rest, b1, b2, ..., bY]:
//becomes
case { 
   before: _.slice(0, X), 
   after: _.slice(_.length - Y), 
   between: _.slice(X, _.length - Y) 
} => { 
   before: [a1, a2, ..., aX], 
   after: [b1, b2, ..., bY], 
   between: rest 
}

So yeah, there’s the unnecessary allocation for prefix and postfix. OTOH it doesn’t have the 0-padding problem for static platforms you mentioned.