{- | The main matching function using specifications:

   The pattern specification is expected to be a parameterized specification
   containing the constants to be mapped in the actual parameter specification.

   The candidates for the matching stem from those operators which have a
   definition and a certain type satisfying the given type predicate.
   A typical such predicate is:

   '(flip elem ["typename1", "typename2", ...]) . show . pretty'

   Only operators with the same type can be matched, and all possible
   combinations of matching candidates are computed.
   With the given Number (> 0) you can constrain the number of candidates to
   try before giving up the matching (0 means all candidates).

   If one candidate gives a correct match result the following candidates are
   not tried and the 'MatchResult' is returned together with the list of non
   tried candidates.
-}
matchSpecs :: (DefStore d, DevGraphNavigator nav) =>
              d -> nav
                -> (TypeScheme -> Bool) -- ^ Filter predicate for types
                -> Int -- ^ Number of candidates to try
                -> String -- ^ Name of pattern spec
                -> String -- ^ Name of concrete spec
                -> IO (Either String MatchResult, [[(MatchOp, MatchOp)]])
matchSpecs def dgnav tFilter n patN cN =
    case getCandidates def dgnav tFilter patN cN of
      Nothing -> return (Left "matchSpecs: specs not found.", [])
      Just cl
          | null cl ->
              return (Left "matchSpecs: no candidates available.", [])
          | otherwise -> do
        let (cands, remC) = if n > 0 then splitAt n cl else (cl, [])
        (mr, l) <- matchCandidates def cands
        return (mr, l ++ remC)
matchSpecs :: (DefStore d, DevGraphNavigator nav) =>
              d -> nav
                -> (TypeScheme -> Bool) -- ^ Filter predicate for types
                -> Int -- ^ Number of candidates to try
                -> String -- ^ Name of pattern spec
                -> String -- ^ Name of concrete spec
                -> IO (Either String MatchResult, [[(MatchOp, MatchOp)]])
matchSpecs def dgnav tFilter n patN cN =
    case getCandidates def dgnav tFilter patN cN of
      Nothing -> return (Left "matchSpecs: specs not found.", [])
      Just cl
          | null cl ->
              return (Left "matchSpecs: no candidates available.", [])
          | otherwise -> do
        let (cands, remC) = if n > 0 then splitAt n cl else (cl, [])
        (mr, l) <- matchCandidates def cands
        return (mr, l ++ remC)
matchSpecs def dgnav tFilter n patN cN =
    case getCandidates def dgnav tFilter patN cN of
      Nothing -> return (Left "matchSpecs: specs not found.", [])
      Just cl
          | null cl ->
              return (Left "matchSpecs: no candidates available.", [])
          | otherwise -> do
        let (cands, remC) = if n > 0 then splitAt n cl else (cl, [])
        (mr, l) <- matchCandidates def cands
        return (mr, l ++ remC)