isSquare takes an integer square root. This doesn't seem to be necessary—although truncating the approximate square root ((truncate::Double -> Int) . sqrt . fromIntegral) of a number can give a result that's off by one, this does not appear to happen if the number is actually square, so the extra test doesn't accomplish anything. See maaartinus's proof that Double arithmetic is always good enough to find square roots of square Int64s for a detailed discussion.
It also probably makes sense to do some testing to see whether the possible square functions strike the right speed/precision balance. An answer maaartinus gives to this question suggests it may be better to be faster at the expense of precision, because additional or more precise tests may blow away any advantage over floating point square roots. My own answer to that question is a slight simplification that may or may not be slightly faster. Working modulo 64 allows for some absurdly fast and simple bitvector indexing (using rotateL or rotateR and a sign test), but there may well be ways to make some stronger tests fast as well.