= Solution
The score function takes a proposed secret first and a guess second:
``
mkGuesser :: Eq score
=> (guess -> guess -> score)
-> ([guess] -> guess)
-> [guess] -> [score] -> [guess]
mkGuesser score strategy dictionary = play dictionary
where
play candidates replies =
let g = strategy candidates
in g : case replies of
[] -> []
r:rs -> play [s | s <- candidates, score s g == r] rs
``
Assume that the initial dictionary contains the secret, that scores are truthful, and that `strategy` returns a candidate from its nonempty input. The candidate <invariant> is exactly the dictionary entries consistent with every score already received. Filtering by `score s g == r` establishes the same invariant after the next reply and retains the true secret.
Crucially, the next guess `g` is placed under the outer list constructor before `replies` is inspected. It depends on earlier replies only; the first guess depends on none. This guarded <lazy feedback stream> makes the mutually recursive `game` productive whenever the strategy and scoring operations terminate. Moving the `case replies` outside the constructor would create a circular demand before the first guess.
Back to article page