on bbf672a help sign in
Idris-dev/src/Idris/TypeSearch.hs
535 lines 23 kB history raw
1{-# LANGUAGE ScopedTypeVariables #-}
3module Idris.TypeSearch (
4 searchByType, searchPred, defaultScoreFunction
5) where
7import Control.Applicative (Applicative (..), (<$>), (<*>), (<|>))
8import Control.Arrow (first, second, (&&&), (***))
9import Control.Monad (when, guard)
11import Data.List (find, partition, (\\))
12import Data.Map (Map)
13import qualified Data.Map as M
14import Data.Maybe (catMaybes, fromMaybe, isJust, maybeToList, mapMaybe)
15import Data.Monoid (Monoid (mempty, mappend))
16import Data.Ord (comparing)
17import qualified Data.PriorityQueue.FingerTree as Q
18import Data.Set (Set)
19import qualified Data.Set as S
20import qualified Data.Text as T (pack, isPrefixOf)
21import Data.Traversable (traverse)
23import Idris.AbsSyntax (addUsingConstraints, addImpl, getIState, putIState, implicit, logLvl)
24import Idris.AbsSyntaxTree (class_instances, ClassInfo, defaultSyntax, eqTy, Idris,
25 IState (idris_classes, idris_docstrings, tt_ctxt, idris_outputmode),
26 implicitAllowed, OutputMode(..), PTerm, toplevel)
27import Idris.Core.Evaluate (Context (definitions), Def (Function, TyDecl, CaseOp), normaliseC)
28import Idris.Core.TT hiding (score)
29import Idris.Core.Unify (match_unify)
30import Idris.Delaborate (delabTy)
31import Idris.Docstrings (noDocs, overview)
32import Idris.Elab.Type (elabType)
33import Idris.Output (iputStrLn, iRenderOutput, iPrintResult, iRenderResult, prettyDocumentedIst)
34import Idris.IBC
36import Prelude hiding (pred)
38import Util.Pretty (text, char, vsep, (<>), Doc, annotate)
40searchByType :: [String] -> PTerm -> Idris ()
41searchByType pkgs pterm = do
42 i <- getIState -- save original
43 when (not (null pkgs)) $
44 iputStrLn $ "Searching packages: " ++ showSep ", " pkgs
46 mapM_ loadPkgIndex pkgs
47 pterm' <- addUsingConstraints syn emptyFC pterm
48 pterm'' <- implicit toplevel syn name pterm'
49 let pterm''' = addImpl i pterm''
50 ty <- elabType toplevel syn (fst noDocs) (snd noDocs) emptyFC [] name pterm'
51 let names = searchUsing searchPred i ty
52 let names' = take numLimit names
53 let docs =
54 [ let docInfo = (n, delabTy i n, fmap (overview . fst) (lookupCtxtExact n (idris_docstrings i))) in
55 displayScore theScore <> char ' ' <> prettyDocumentedIst i docInfo
56 | (n, theScore) <- names']
57 case idris_outputmode i of
58 RawOutput _ -> do mapM_ iRenderOutput docs
59 iPrintResult ""
60 IdeMode _ _ -> iRenderResult (vsep docs)
61 putIState i -- don't actually make any changes
62 where
63 numLimit = 50
64 syn = defaultSyntax { implicitAllowed = True } -- syntax
65 name = sMN 0 "searchType" -- name
67-- | Conduct a type-directed search using a given match predicate
68searchUsing :: (IState -> Type -> [(Name, Type)] -> [(Name, a)])
69 -> IState -> Type -> [(Name, a)]
70searchUsing pred istate ty = pred istate nty . concat . M.elems $
71 M.mapWithKey (\key -> M.toAscList . M.mapMaybe (f key)) (definitions ctxt)
72 where
73 nty = normaliseC ctxt [] ty
74 ctxt = tt_ctxt istate
75 f k x = do
76 guard $ not (special k)
77 type2 <- typeFromDef x
78 return $ normaliseC ctxt [] type2
79 special :: Name -> Bool
80 special (NS n _) = special n
81 special (SN _) = True
82 special (UN n) = T.pack "default#" `T.isPrefixOf` n
83 || n `elem` map T.pack ["believe_me", "really_believe_me"]
84 special _ = False
86-- | Our default search predicate.
87searchPred :: IState -> Type -> [(Name, Type)] -> [(Name, Score)]
88searchPred istate ty1 = matcher where
89 maxScore = 100
90 matcher = matchTypesBulk istate maxScore ty1
93typeFromDef :: (Def, b, c, d) -> Maybe Type
94typeFromDef (def, _, _, _) = get def where
95 get :: Def -> Maybe Type
96 get (Function ty _) = Just ty
97 get (TyDecl _ ty) = Just ty
98 -- get (Operator ty _ _) = Just ty
99 get (CaseOp _ ty _ _ _ _) = Just ty
100 get _ = Nothing
102-- Replace all occurences of `Lazy' s t` with `t` in a type
103unLazy :: Type -> Type
104unLazy typ = case typ of
105 App (App (P _ lazy _) _) ty | lazy == sUN "Lazy'" -> unLazy ty
106 Bind name binder ty -> Bind name (fmap unLazy binder) (unLazy ty)
107 App t1 t2 -> App (unLazy t1) (unLazy t2)
108 Proj ty i -> Proj (unLazy ty) i
109 _ -> typ
111-- | reverse the edges for a directed acyclic graph
112reverseDag :: Ord k => [((k, a), Set k)] -> [((k, a), Set k)]
113reverseDag xs = map f xs where
114 f ((k, v), _) = ((k, v), S.fromList . map (fst . fst) $ filter (S.member k . snd) xs)
116-- run vToP first!
117-- | Compute a directed acyclic graph corresponding to the
118-- arguments of a function.
119-- returns [(the name and type of the bound variable
120-- the names in the type of the bound variable)]
121computeDagP :: Ord n
122 => (TT n -> Bool) -- ^ filter to remove some arguments
123 -> TT n
124 -> ([((n, TT n), Set n)], [(n, TT n)], TT n)
125computeDagP removePred t = (reverse (map f arguments), reverse theRemovedArgs , retTy) where
126 f (n, ty) = ((n, ty), M.keysSet (usedVars ty))
128 (arguments, theRemovedArgs, retTy) = go [] [] t
130 -- NOTE : args are in reverse order
131 go args removedArgs (Bind n (Pi _ ty _) sc) = let arg = (n, ty) in
132 if removePred ty
133 then go args (arg : removedArgs) sc
134 else go (arg : args) removedArgs sc
135 go args removedArgs sc = (args, removedArgs, sc)
137-- | Collect the names and types of all the free variables
138-- The Boolean indicates those variables which are determined due to injectivity
139-- I have not given nearly enough thought to know whether this is correct
140usedVars :: Ord n => TT n -> Map n (TT n, Bool)
141usedVars = f True where
142 f b (P Bound n t) = M.singleton n (t, b) `M.union` f b t
143 f b (Bind n binder t2) = (M.delete n (f b t2) `M.union`) $ case binder of
144 Let t v -> f b t `M.union` f b v
145 Guess t v -> f b t `M.union` f b v
146 bind -> f b (binderTy bind)
147 f b (App t1 t2) = f b t1 `M.union` f (b && isInjective t1) t2
148 f b (Proj t _) = f b t
149 f _ (V _) = error "unexpected! run vToP first"
150 f _ _ = M.empty
152-- | Remove a node from a directed acyclic graph
153deleteFromDag :: Ord n => n -> [((n, TT n), (a, Set n))] -> [((n, TT n), (a, Set n))]
154deleteFromDag name [] = []
155deleteFromDag name (((name2, ty), (ix, set)) : xs) = (if name == name2
156 then id
157 else (((name2, ty) , (ix, S.delete name set)) :) ) (deleteFromDag name xs)
159deleteFromArgList :: Ord n => n -> [(n, TT n)] -> [(n, TT n)]
160deleteFromArgList n = filter ((/= n) . fst)
162-- | Asymmetric modifications to keep track of
163data AsymMods = Mods
164 { argApp :: !Int
165 , typeClassApp :: !Int
166 , typeClassIntro :: !Int
167 } deriving (Eq, Show)
170-- | Homogenous tuples
171data Sided a = Sided
172 { left :: !a
173 , right :: !a
174 } deriving (Eq, Show)
176sided :: (a -> a -> b) -> Sided a -> b
177sided f (Sided l r) = f l r
179-- | Could be a functor instance, but let's avoid name overloading
180both :: (a -> b) -> Sided a -> Sided b
181both f (Sided l r) = Sided (f l) (f r)
183-- | Keeps a record of the modifications made to match one type
184-- signature with another
185data Score = Score
186 { transposition :: !Int -- ^ transposition of arguments
187 , equalityFlips :: !Int -- ^ application of symmetry of equality
188 , asymMods :: !(Sided AsymMods) -- ^ "directional" modifications
189 } deriving (Eq, Show)
191displayScore :: Score -> Doc OutputAnnotation
192displayScore score = case both noMods (asymMods score) of
193 Sided True True -> annotated EQ "=" -- types are isomorphic
194 Sided True False -> annotated LT "<" -- found type is more general than searched type
195 Sided False True -> annotated GT ">" -- searched type is more general than found type
196 Sided False False -> text "_"
197 where
198 annotated ordr = annotate (AnnSearchResult ordr) . text
199 noMods (Mods app tcApp tcIntro) = app + tcApp + tcIntro == 0
201-- | This allows the search to stop expanding on a certain state if its
202-- score is already too high. Returns 'True' if the algorithm should keep
203-- exploring from this state, and 'False' otherwise.
204scoreCriterion :: Score -> Bool
205scoreCriterion (Score _ _ amods) = not
206 ( sided (&&) (both ((> 0) . argApp) amods)
207 || sided (+) (both argApp amods) > 4
208 || sided (||) (both (\(Mods _ tcApp tcIntro) -> tcApp > 3 || tcIntro > 3) amods)
209 )
211-- | Convert a 'Score' to an 'Int' to provide an order for search results.
212-- Lower scores are better.
213defaultScoreFunction :: Score -> Int
214defaultScoreFunction (Score trans eqFlip amods) =
215 trans + eqFlip + linearPenalty + upAndDowncastPenalty
216 where
217 -- prefer finding a more general type to a less general type
218 linearPenalty = (\(Sided l r) -> 3 * l + r)
219 (both (\(Mods app tcApp tcIntro) -> 3 * app + 4 * tcApp + 2 * tcIntro) amods)
220 -- it's very bad to have *both* upcasting and downcasting
221 upAndDowncastPenalty = 100 *
222 sided (*) (both (\(Mods app tcApp tcIntro) -> 2 * app + tcApp + tcIntro) amods)
224instance Ord Score where
225 compare = comparing defaultScoreFunction
228instance Monoid a => Monoid (Sided a) where
229 mempty = Sided mempty mempty
230 (Sided l1 r1) `mappend` (Sided l2 r2) = Sided (l1 `mappend` l2) (r1 `mappend` r2)
232instance Monoid AsymMods where
233 mempty = Mods 0 0 0
234 (Mods a b c) `mappend` (Mods a' b' c') = Mods (a + a') (b + b') (c + c')
236instance Monoid Score where
237 mempty = Score 0 0 mempty
238 (Score t e mods) `mappend` (Score t' e' mods') = Score (t + t') (e + e') (mods `mappend` mods')
240-- | A directed acyclic graph representing the arguments to a function
241-- The 'Int' represents the position of the argument (1st argument, 2nd, etc.)
242type ArgsDAG = [((Name, Type), (Int, Set Name))]
244-- | A list of typeclass constraints
245type Classes = [(Name, Type)]
247-- | The state corresponding to an attempted match of two types.
248data State = State
249 { holes :: ![(Name, Type)] -- ^ names which have yet to be resolved
250 , argsAndClasses :: !(Sided (ArgsDAG, Classes))
251 -- ^ arguments and typeclass constraints for each type which have yet to be resolved
252 , score :: !Score -- ^ the score so far
253 , usedNames :: ![Name] -- ^ all names that have been used
254 } deriving Show
256modifyTypes :: (Type -> Type) -> (ArgsDAG, Classes) -> (ArgsDAG, Classes)
257modifyTypes f = modifyDag *** modifyList
258 where
259 modifyDag = map (first (second f))
260 modifyList = map (second f)
262findNameInArgsDAG :: Name -> ArgsDAG -> Maybe (Type, Maybe Int)
263findNameInArgsDAG name = fmap ((snd . fst) &&& (Just . fst . snd)) . find ((name ==) . fst . fst)
265findName :: Name -> (ArgsDAG, Classes) -> Maybe (Type, Maybe Int)
266findName n (args, classes) = findNameInArgsDAG n args <|> ((,) <$> lookup n classes <*> Nothing)
268deleteName :: Name -> (ArgsDAG, Classes) -> (ArgsDAG, Classes)
269deleteName n (args, classes) = (deleteFromDag n args, filter ((/= n) . fst) classes)
271tcToMaybe :: TC a -> Maybe a
272tcToMaybe (OK x) = Just x
273tcToMaybe (Error _) = Nothing
275inArgTys :: (Type -> Type) -> ArgsDAG -> ArgsDAG
276inArgTys = map . first . second
279typeclassUnify :: Ctxt ClassInfo -> Context -> Type -> Type -> Maybe [(Name, Type)]
280typeclassUnify classInfo ctxt ty tyTry = do
281 res <- tcToMaybe $ match_unify ctxt [] ty retTy [] theHoles []
282 guard $ null (theHoles \\ map fst res)
283 let argTys' = map (second $ foldr (.) id [ subst n t | (n, t) <- res ]) tcArgs
284 return argTys'
285 where
286 tyTry' = vToP tyTry
287 theHoles = map fst nonTcArgs
288 retTy = getRetTy tyTry'
289 (tcArgs, nonTcArgs) = partition (isTypeClassArg classInfo . snd) $ getArgTys tyTry'
291isTypeClassArg :: Ctxt ClassInfo -> Type -> Bool
292isTypeClassArg classInfo ty = not (null (getClassName clss >>= flip lookupCtxt classInfo)) where
293 (clss, args) = unApply ty
294 getClassName (P (TCon _ _) className _) = [className]
295 getClassName _ = []
298-- | Compute the power set
299subsets :: [a] -> [[a]]
300subsets [] = [[]]
301subsets (x : xs) = let ss = subsets xs in map (x :) ss ++ ss
304-- not recursive (i.e., doesn't flip iterated identities) at the moment
305-- recalls the number of flips that have been made
306flipEqualities :: Type -> [(Int, Type)]
307flipEqualities t = case t of
308 eq1@(App (App (App (App eq@(P _ eqty _) tyL) tyR) valL) valR) | eqty == eqTy ->
309 [(0, eq1), (1, App (App (App (App eq tyR) tyL) valR) valL)]
310 Bind n binder sc -> (\bind' (j, sc') -> (fst (binderTy bind') + j, Bind n (fmap snd bind') sc'))
311 <$> traverse flipEqualities binder <*> flipEqualities sc
312 App f x -> (\(i, f') (j, x') -> (i + j, App f' x'))
313 <$> flipEqualities f <*> flipEqualities x
314 t' -> [(0, t')]
316--DONT run vToP first!
317-- | Try to match two types together in a unification-like procedure.
318-- Returns a list of types and their minimum scores, sorted in order
319-- of increasing score.
320matchTypesBulk :: forall info. IState -> Int -> Type -> [(info, Type)] -> [(info, Score)]
321matchTypesBulk istate maxScore type1 types = getAllResults startQueueOfQueues where
322 getStartQueues :: (info, Type) -> Maybe (Score, (info, Q.PQueue Score State))
323 getStartQueues nty@(info, type2) = case mapMaybe startStates ty2s of
324 [] -> Nothing
325 xs -> Just (minimum (map fst xs), (info, Q.fromList xs))
326 where
327 ty2s = (\(i, dag) (j, retTy) -> (i + j, dag, retTy))
328 <$> flipEqualitiesDag dag2 <*> flipEqualities retTy2
329 flipEqualitiesDag dag = case dag of
330 [] -> [(0, [])]
331 ((n, ty), (pos, deps)) : xs ->
332 (\(i, ty') (j, xs') -> (i + j , ((n, ty'), (pos, deps)) : xs'))
333 <$> flipEqualities ty <*> flipEqualitiesDag xs
334 startStates (numEqFlips, sndDag, sndRetTy) = do
335 state <- unifyQueue (State startingHoles
336 (Sided (dag1, typeClassArgs1) (sndDag, typeClassArgs2))
337 (mempty { equalityFlips = numEqFlips }) usedns) [(retTy1, sndRetTy)]
338 return (score state, state)
341 (dag2, typeClassArgs2, retTy2) = makeDag (uniqueBinders (map fst argNames1) type2)
342 argNames2 = map fst dag2
343 usedns = map fst startingHoles
344 startingHoles = argNames1 ++ argNames2
346 startingTypes = [(retTy1, retTy2)]
349 startQueueOfQueues :: Q.PQueue Score (info, Q.PQueue Score State)
350 startQueueOfQueues = Q.fromList $ mapMaybe getStartQueues types
352 getAllResults :: Q.PQueue Score (info, Q.PQueue Score State) -> [(info, Score)]
353 getAllResults q = case Q.minViewWithKey q of
354 Nothing -> []
355 Just ((nextScore, (info, stateQ)), q') ->
356 if defaultScoreFunction nextScore <= maxScore
357 then case nextStepsQueue stateQ of
358 Nothing -> getAllResults q'
359 Just (Left stateQ') -> case Q.minViewWithKey stateQ' of
360 Nothing -> getAllResults q'
361 Just ((newQscore,_), _) -> getAllResults (Q.add newQscore (info, stateQ') q')
362 Just (Right theScore) -> (info, theScore) : getAllResults q'
363 else []
366 ctxt = tt_ctxt istate
367 classInfo = idris_classes istate
369 (dag1, typeClassArgs1, retTy1) = makeDag type1
370 argNames1 = map fst dag1
371 makeDag :: Type -> (ArgsDAG, Classes, Type)
372 makeDag = first3 (zipWith (\i (ty, deps) -> (ty, (i, deps))) [0..] . reverseDag) .
373 computeDagP (isTypeClassArg classInfo) . vToP . unLazy
374 first3 f (a,b,c) = (f a, b, c)
376 -- update our state with the unification resolutions
377 resolveUnis :: [(Name, Type)] -> State -> Maybe (State, [(Type, Type)])
378 resolveUnis [] state = Just (state, [])
379 resolveUnis ((name, term@(P Bound name2 _)) : xs)
380 state | isJust findArgs = do
381 ((ty1, ix1), (ty2, ix2)) <- findArgs
383 (state'', queue) <- resolveUnis xs state'
384 let transScore = fromMaybe 0 (abs <$> ((-) <$> ix1 <*> ix2))
385 return (inScore (\s -> s { transposition = transposition s + transScore }) state'', (ty1, ty2) : queue)
386 where
387 unresolved = argsAndClasses state
388 inScore f stat = stat { score = f (score stat) }
389 findArgs = ((,) <$> findName name (left unresolved) <*> findName name2 (right unresolved)) <|>
390 ((,) <$> findName name2 (left unresolved) <*> findName name (right unresolved))
391 matchnames = [name, name2]
392 deleteArgs = deleteName name . deleteName name2
393 state' = state { holes = filter (not . (`elem` matchnames) . fst) (holes state)
394 , argsAndClasses = both (modifyTypes (subst name term) . deleteArgs) unresolved}
396 resolveUnis ((name, term) : xs)
397 state@(State hs unresolved _ _) = case both (findName name) unresolved of
398 Sided Nothing Nothing -> Nothing
399 Sided (Just _) (Just _) -> error "Idris internal error: TypeSearch.resolveUnis"
400 oneOfEach -> first (addScore (both scoreFor oneOfEach)) <$> nextStep
401 where
402 scoreFor (Just _) = mempty { argApp = 1 }
403 scoreFor Nothing = mempty { argApp = otherApplied }
404 -- find variables which are determined uniquely by the type
405 -- due to injectivity
406 matchedVarMap = usedVars term
407 bothT f (x, y) = (f x, f y)
408 (injUsedVars, notInjUsedVars) = bothT M.keys . M.partition id . M.filterWithKey (\k _-> k `elem` map fst hs) $ M.map snd matchedVarMap
409 varsInTy = injUsedVars ++ notInjUsedVars
410 toDelete = name : varsInTy
411 deleteMany = foldr (.) id (map deleteName toDelete)
413 otherApplied = length notInjUsedVars
415 addScore additions theState = theState { score = let s = score theState in
416 s { asymMods = asymMods s `mappend` additions } }
417 state' = state { holes = filter (not . (`elem` toDelete) . fst) hs
418 , argsAndClasses = both (modifyTypes (subst name term) . deleteMany) (argsAndClasses state) }
419 nextStep = resolveUnis xs state'
422 -- | resolve a queue of unification constraints
423 unifyQueue :: State -> [(Type, Type)] -> Maybe State
424 unifyQueue state [] = return state
425 unifyQueue state ((ty1, ty2) : queue) = do
426 --trace ("go: \n" ++ show state) True `seq` return ()
427 res <- tcToMaybe $ match_unify ctxt [ (n, Pi Nothing ty (TType (UVar 0))) | (n, ty) <- holes state] ty1 ty2 [] (map fst $ holes state) []
428 (state', queueAdditions) <- resolveUnis res state
429 guard $ scoreCriterion (score state')
430 unifyQueue state' (queue ++ queueAdditions)
432 possClassInstances :: [Name] -> Type -> [Type]
433 possClassInstances usedns ty = do
434 className <- getClassName clss
435 classDef <- lookupCtxt className classInfo
436 n <- class_instances classDef
437 def <- lookupCtxt n (definitions ctxt)
438 nty <- normaliseC ctxt [] <$> (case typeFromDef def of Just x -> [x]; Nothing -> [])
439 let ty' = vToP (uniqueBinders usedns nty)
440 return ty'
441 where
442 (clss, _) = unApply ty
443 getClassName (P (TCon _ _) className _) = [className]
444 getClassName _ = []
446 -- 'Just' if the computation hasn't totally failed yet, 'Nothing' if it has
447 -- 'Left' if we haven't found a terminal state, 'Right' if we have
448 nextStepsQueue :: Q.PQueue Score State -> Maybe (Either (Q.PQueue Score State) Score)
449 nextStepsQueue queue = do
450 ((nextScore, next), rest) <- Q.minViewWithKey queue
451 Just $ if isFinal next
452 then Right nextScore
453 else let additions = if scoreCriterion nextScore
454 then Q.fromList [ (score state, state) | state <- nextSteps next ]
455 else Q.empty in
456 Left (Q.union rest additions)
457 where
458 isFinal (State [] (Sided ([], []) ([], [])) _ _) = True
459 isFinal _ = False
461 -- | Find all possible matches starting from a given state.
462 -- We go in stages rather than concatenating all three results in hopes of narrowing
463 -- the search tree. Once we advance in a phase, there should be no going back.
464 nextSteps :: State -> [State]
466 -- Stage 3 - match typeclasses
467 nextSteps (State [] unresolved@(Sided ([], c1) ([], c2)) scoreAcc usedns) =
468 if null results3 then results4 else results3
469 where
470 -- try to match a typeclass argument from the left with a typeclass argument from the right
471 results3 =
472 catMaybes [ unifyQueue (State []
473 (Sided ([], deleteFromArgList n1 c1)
474 ([], map (second subst2for1) (deleteFromArgList n2 c2)))
475 scoreAcc usedns) [(ty1, ty2)]
476 | (n1, ty1) <- c1, (n2, ty2) <- c2, let subst2for1 = psubst n2 (P Bound n1 ty1)]
478 -- try to hunt match a typeclass constraint by replacing it with an instance
479 results4 = [ State [] (both (\(cs, _, _) -> ([], cs)) sds)
480 (scoreAcc `mappend` Score 0 0 (both (\(_, amods, _) -> amods) sds))
481 (usedns ++ sided (++) (both (\(_, _, hs) -> hs) sds))
482 | sds <- allMods ]
483 where
484 allMods = parallel defMod mods
485 mods :: Sided [( Classes, AsymMods, [Name] )]
486 mods = both (instanceMods . snd) unresolved
487 defMod :: Sided (Classes, AsymMods, [Name])
488 defMod = both (\(_, cs) -> (cs, mempty , [])) unresolved
489 parallel :: Sided a -> Sided [a] -> [Sided a]
490 parallel (Sided l r) (Sided ls rs) = map (flip Sided r) ls ++ map (Sided l) rs
491 instanceMods :: Classes -> [( Classes , AsymMods, [Name] )]
492 instanceMods classes = [ ( newClassArgs, mempty { typeClassApp = 1 }, newHoles )
493 | (_, ty) <- classes
494 , inst <- possClassInstances usedns ty
495 , newClassArgs <- maybeToList $ typeclassUnify classInfo ctxt ty inst
496 , let newHoles = map fst newClassArgs ]
499 -- Stage 1 - match arguments
500 nextSteps (State hs (Sided (dagL, c1) (dagR, c2)) scoreAcc usedns) = results where
502 results = concatMap takeSomeClasses results1
504 -- we only try to match arguments whose names don't appear in the types
505 -- of any other arguments
506 canBeFirst :: ArgsDAG -> [(Name, Type)]
507 canBeFirst = map fst . filter (S.null . snd . snd)
509 -- try to match an argument from the left with an argument from the right
510 results1 = catMaybes [ unifyQueue (State (filter (not . (`elem` [n1,n2]) . fst) hs)
511 (Sided (deleteFromDag n1 dagL, c1)
512 (inArgTys subst2for1 $ deleteFromDag n2 dagR, map (second subst2for1) c2))
513 scoreAcc usedns) [(ty1, ty2)]
514 | (n1, ty1) <- canBeFirst dagL, (n2, ty2) <- canBeFirst dagR
515 , let subst2for1 = psubst n2 (P Bound n1 ty1)]
519 -- Stage 2 - simply introduce a subset of the typeclasses
520 -- we've finished, so take some classes
521 takeSomeClasses (State [] unresolved@(Sided ([], _) ([], _)) scoreAcc usedns) =
522 map statesFromMods . prod $ both (classMods . snd) unresolved
523 where
524 swap (Sided l r) = Sided r l
525 statesFromMods :: Sided (Classes, AsymMods) -> State
526 statesFromMods sides = let classes = both (\(c, _) -> ([], c)) sides
527 mods = swap (both snd sides) in
528 State [] classes (scoreAcc `mappend` (mempty { asymMods = mods })) usedns
529 classMods :: Classes -> [(Classes, AsymMods)]
530 classMods cs = let lcs = length cs in
531 [ (cs', mempty { typeClassIntro = lcs - length cs' }) | cs' <- subsets cs ]
532 prod :: Sided [a] -> [Sided a]
533 prod (Sided ls rs) = [Sided l r | l <- ls, r <- rs]
534 -- still have arguments to match, so just be the identity
535 takeSomeClasses s = [s]