The tupled version you suggest can be found as parTuple2 in Control.Parallel.Strategies, with the type:
evalTuple2 :: Strategy a -> Strategy b -> Strategy (a, b)
As for why par was designed that way, par is 'higher level', as chapter 24 of Real World Haskell discusses, where they parallelize a quicksort:
These changes to our code are remarkable for all the things we have
not needed to say.
- How many cores to use.
- What threads do to communicate with each other.
- How to divide up work among the available cores.
- Which data are shared between threads, and which are private.
- How to determine when all the participants are finished.
In A Monad for Deterministic Parallelism, Marlow, Newton, and Peyton Jones write:
The par operator is an attractive language design because it
capitalises on the overlap between lazy evaluation and futures.
To implement lazy evaluation we must have a representation for
expressions which are not yet evaluated but whose value may
later be demanded; and similarly a future is a computation whose
value is being evaluated in parallel and which we may wait for.
Hence, par was conceived as a mechanism for annotating a lazy
computation as being potentially profitable to evaluate in parallel,
in effect turning a lazy computation into a future