Most list- or array-shuffle implementations use Durstenfeld's variation of the Fisher-Yates algorithm (also known as "Algorithm P" in Knuth's Art of Computer Programming, vol. 2, section 3.4.2). This algorithm has the advantage that it completes in O(n) time and requires O(1) additional memory (on top of the O(n) space that the existing list needs to begin with). This is significantly more efficient than let's say creating a new list in each step. Even in a purely functional environment, the library implementation might make an initial copy of the original list and then apply this (mutating) algorithm. This would not be purely functional under the hood, but entirely hidden from the calling code. Also, by making a one-time copy, the implementation can be easily converted from in-place modification to a more purely functional style. However, as other folks have pointed out, in line with other principles that are observed for a particular library, the designers may choose not to do so.