Hi Hans. I assume you have more data types, such as scores. Defining semantics/models for those types would be the heart of a denotational design, along with the meanings of every element of your API in terms of these models/semantics. For any type class instances you have, the principle of type class morphisms will usually determine the meanings of those instances, and often one can derive/calculate implementations from those determined meanings.
If you haven't already, see
Denotational design with type class morphisms for principles and examples.
I don't know how to keep the calculations &
proofs in sync with changes to the specification, using Haskell. Maybe
you could use a proof assistant environment, but I'm guessing. As you suggested, QuickCheck could also help, especially since using denotational design means that you have a precise specification.