{- I'm writing code to do some combinatorial construction via test-and-evaluate 
algorithms. And I want to write a very generic algorithm. The idea is that the item being constructed has a current state, which is represented by a data type of typeclass EvalState. And the algorithm will consider a set of new elements to add to the object being constructed, represented by a data type of typeclass ElemSet. Here are the class definitions:

-}

class ElemSet a where
  elemSetNumElements :: a -> Int


class EvalState s where
  isCompleteState :: s -> Bool  
  newElemSet :: ElemSet a => s -> a
  integrateElem :: ElemSet a => a -> Int -> s -> s
  -- given an elem set, and an index of the elem to choose , compute a score
  scoreElem :: ElemSet a => a -> Int -> s -> EvalScore

type EvalScore = Float

type RandomnessChooser = [(EvalScore,Int)] -> ErrorRand Int

{- here's my generic search algorithm written using the typeclasses 

the problem I'm having relates to the fact that I need to call 'integrateElem' which is a function in typeclass EvalState, and one of the arguments has the constraint of being of typeclass ElemSet, but I don't know how to put that constraint in.

The specific error is

"Ambigous type variable 'a' in the constraint: 'ElemSet a' arising from a use of 'integrateElem'"


-}

search :: (EvalState s) => 
          RandomnessChooser -> s -> ErrorRand s
search chooser state = do
  if isCompleteState state
    then return state
    else do
      let elemSet = newElemSet state      
          n = elemSetNumElements elemSet
          scores = map g [0..n-1]
          g i = (scoreElem elemSet i state,i)
      chosenElemIdx <- chooser scores
      let newState = integrateElem elemSet chosenElemIdx state
      search chooser newState