@@ -10,14 +10,15 @@ import Data.Function (on)
10import Data.List (find, minimumBy, partition, sortBy, (\\))
10import Data.List (find, minimumBy, partition, sortBy, (\\))
11import Data.Map (Map)
11import Data.Map (Map)
12import qualified Data.Map as M
12import qualified Data.Map as M
13−import Data.Maybe (catMaybes, fromMaybe, isJust)
13+import Data.Maybe (catMaybes, fromMaybe, isJust, maybeToList)
14import Data.Monoid (Monoid (mempty, mappend))
14import Data.Monoid (Monoid (mempty, mappend))
15+import qualified Data.PriorityQueue.FingerTree as Q
15import Data.Set (Set)
16import Data.Set (Set)
16import qualified Data.Set as S
17import qualified Data.Set as S
17import qualified Data.Text as T (pack, isPrefixOf)
18import qualified Data.Text as T (pack, isPrefixOf)
19import Idris.AbsSyntax (addUsingConstraints, addImpl, getContext, getIState, putIState, implicit)
20import Idris.AbsSyntax (addUsingConstraints, addImpl, getContext, getIState, putIState, implicit)
20−import Idris.AbsSyntaxTree (class_instances, defaultSyntax, Idris,
21+import Idris.AbsSyntaxTree (class_instances, ClassInfo, defaultSyntax, Idris,
21 IState (idris_classes, idris_docstrings, tt_ctxt),
22 IState (idris_classes, idris_docstrings, tt_ctxt),
22 implicitAllowed, prettyDocumentedIst, prettyIst, PTerm, toplevel)
23 implicitAllowed, prettyDocumentedIst, prettyIst, PTerm, toplevel)
23import Idris.Core.Evaluate (Context (definitions), Def (Function, TyDecl, CaseOp), normaliseC)
24import Idris.Core.Evaluate (Context (definitions), Def (Function, TyDecl, CaseOp), normaliseC)
@@ -41,7 +42,7 @@ searchByType h pterm = do
41 ty <- elabType' False toplevel syn (fst noDocs) (snd noDocs) emptyFC [] n pterm'
42 ty <- elabType' False toplevel syn (fst noDocs) (snd noDocs) emptyFC [] n pterm'
42 putIState i -- don't actually make any changes
43 putIState i -- don't actually make any changes
43 let names = searchUsing searchPred i ty
44 let names = searchUsing searchPred i ty
44− let names' = take numLimit . takeWhile ((< scoreLimit) . getScore) $
45+ let names' = take numLimit $
45 sortBy (compare `on` getScore) names
46 sortBy (compare `on` getScore) names
46 let docs =
47 let docs =
47 [ let docInfo = (n, delabTy i n, fmap (overview . fst) (lookupCtxtExact n (idris_docstrings i))) in
48 [ let docInfo = (n, delabTy i n, fmap (overview . fst) (lookupCtxtExact n (idris_docstrings i))) in
@@ -51,7 +52,6 @@ searchByType h pterm = do
52 getScore = defaultScoreFunction . snd . snd
53 getScore = defaultScoreFunction . snd . snd
53 numLimit = 50
54 numLimit = 50
55 syn = defaultSyntax { implicitAllowed = True } -- syntax
55 syn = defaultSyntax { implicitAllowed = True } -- syntax
56 n = sMN 0 "searchType" -- name
56 n = sMN 0 "searchType" -- name
@@ -84,10 +84,9 @@ searchUsing pred istate ty =
85-- Our default search predicate.
85-- Our default search predicate.
86searchPred :: IState -> Type -> Type -> Maybe Score
86searchPred :: IState -> Type -> Type -> Maybe Score
87−searchPred istate ty1 = \ty2 -> case matcher ty2 of
87+searchPred istate ty1 = matcher where
88− xs -> guard (not (null xs)) >> return (minimumBy (compare `on` defaultScoreFunction) xs)
88+ maxScore = 100
90− matcher = unifyWithHoles istate ty1
89+ matcher = unifyWithHoles istate maxScore ty1
93typeFromDef :: (Def, b, c, d) -> Maybe Type
92typeFromDef :: (Def, b, c, d) -> Maybe Type
@@ -218,36 +217,53 @@ inArgTys :: (Type -> Type) -> ArgsDAG -> ArgsDAG
218inArgTys = map . first . second
217inArgTys = map . first . second
220+typeclassUnify :: Ctxt ClassInfo -> Context -> Type -> Type -> Maybe [(Name, Type)]
221+typeclassUnify classInfo ctxt ty tyTry = do
222+ res <- tcToMaybe $ match_unify ctxt [] ty retTy [] holes []
223+ guard $ null (holes \\ map fst res)
224+ let argTys' = map (second $ foldr (.) id [ subst n t | (n, t) <- res ]) tcArgs
228+ holes = map fst nonTcArgs
229+ retTy = getRetTy tyTry'
230+ (tcArgs, nonTcArgs) = partition (isTypeClassArg classInfo . snd) $ getArgTys tyTry'
232+isTypeClassArg :: Ctxt ClassInfo -> Type -> Bool
233+isTypeClassArg classInfo ty = not (null (getClassName clss >>= flip lookupCtxt classInfo)) where
234+ (clss, args) = unApply ty
235+ getClassName (P (TCon _ _) className _) = [className]
239+instance Ord Score where
240+ compare = compare `on` defaultScoreFunction
221--DONT run vToP first!
242--DONT run vToP first!
222-- | Try to match two types together in a unification-like procedure.
243-- | Try to match two types together in a unification-like procedure.
223-- Returns a list of possible scores representing ways in which the two
244-- Returns a list of possible scores representing ways in which the two
224-- types can be matched.
245-- types can be matched.
225−unifyWithHoles :: IState -> Type -> Type -> [Score]
246+unifyWithHoles :: IState -> Int -> Type -> Type -> Maybe Score
226−unifyWithHoles istate type1 = \type2 -> let
247+unifyWithHoles istate maxScore type1 = \type2 -> let
227 (dag2, typeClassArgs2, retTy2) = makeDag (uniqueBinders argNames1 type2)
248 (dag2, typeClassArgs2, retTy2) = makeDag (uniqueBinders argNames1 type2)
228 argNames2 = map (fst . fst) dag2
249 argNames2 = map (fst . fst) dag2
229 startingHoles = argNames1 ++ argNames2
250 startingHoles = argNames1 ++ argNames2
231 startingTypes = (retTy1, retTy2) : []
252 startingTypes = (retTy1, retTy2) : []
232− in case unifyQueue (State startingHoles dag1 dag2
254+ state <- unifyQueue (State startingHoles dag1 dag2
233 typeClassArgs1 typeClassArgs2
255 typeClassArgs1 typeClassArgs2
234 mempty startingHoles) startingTypes
256 mempty startingHoles) startingTypes
235− of Nothing -> []
257+ getResults (Q.singleton (score state) state)
236− Just state -> possibleMatches state
238 ctxt = tt_ctxt istate
259 ctxt = tt_ctxt istate
239 classInfo = idris_classes istate
260 classInfo = idris_classes istate
241− isTypeClassArg :: Type -> Bool
242− isTypeClassArg ty = not (null (getClassName clss >>= flip lookupCtxt classInfo)) where
243− (clss, args) = unApply ty
244− getClassName (P (TCon _ _) className _) = [className]
247 (dag1, typeClassArgs1, retTy1) = makeDag type1
262 (dag1, typeClassArgs1, retTy1) = makeDag type1
248 argNames1 = map (fst . fst) dag1
263 argNames1 = map (fst . fst) dag1
249 makeDag :: Type -> (ArgsDAG, [(Name, Type)], Type)
264 makeDag :: Type -> (ArgsDAG, [(Name, Type)], Type)
250− makeDag = first3 (zipWith (\i (ty, deps) -> (ty, (i, deps))) [0..] . reverseDag) . computeDagP isTypeClassArg . vToP
265+ makeDag = first3 (zipWith (\i (ty, deps) -> (ty, (i, deps))) [0..] . reverseDag) .
266+ computeDagP (isTypeClassArg classInfo) . vToP
251 first3 f (a,b,c) = (f a, b, c)
267 first3 f (a,b,c) = (f a, b, c)
253 -- update our state with the unification resolutions
269 -- update our state with the unification resolutions
@@ -278,9 +294,13 @@ unifyWithHoles istate type1 = \type2 -> let
278 (Nothing, Nothing) -> nextStep
294 (Nothing, Nothing) -> nextStep
279 _ -> error ("Shouldn't happen. Watch the alpha conversion!\n" ++ show args1 ++ "\n\n" ++ show args2)
295 _ -> error ("Shouldn't happen. Watch the alpha conversion!\n" ++ show args1 ++ "\n\n" ++ show args2)
297+ varsInTy = map fst $ M.toList (usedVars term) --[]
298+ toDelete = name : varsInTy
299+ deleteMany = foldr (.) id $ [ deleteLeft t . deleteRight t | t <- toDelete ]
281 inScore f state = state { score = f (score state) }
301 inScore f state = state { score = f (score state) }
282− state' = modifyTypes (subst name term) . deleteLeft name . deleteRight name $
302+ state' = modifyTypes (subst name term) . deleteMany $
283− state { holes = holes \\ [name]}
303+ state { holes = holes \\ toDelete }
284 nextStep = resolveUnis xs state'
304 nextStep = resolveUnis xs state'
@@ -288,12 +308,13 @@ unifyWithHoles istate type1 = \type2 -> let
288 unifyQueue :: State -> [(Type, Type)] -> Maybe State
308 unifyQueue :: State -> [(Type, Type)] -> Maybe State
289 unifyQueue state [] = return state
309 unifyQueue state [] = return state
290 unifyQueue state ((ty1, ty2) : queue) = do
310 unifyQueue state ((ty1, ty2) : queue) = do
311+ --trace ("go: \n" ++ show state) True `seq` return ()
291 res <- tcToMaybe $ match_unify ctxt [] ty1 ty2 [] (holes state) []
312 res <- tcToMaybe $ match_unify ctxt [] ty1 ty2 [] (holes state) []
292 (state', queueAdditions) <- resolveUnis res state
313 (state', queueAdditions) <- resolveUnis res state
293 guard $ scoreCriterion (score state')
314 guard $ scoreCriterion (score state')
294 unifyQueue state' (queue ++ queueAdditions)
315 unifyQueue state' (queue ++ queueAdditions)
296− possClassInstances :: [Name] -> Type -> [([(Name, Type)], Type)]
317+ possClassInstances :: [Name] -> Type -> [Type]
297 possClassInstances usedNames ty = do
318 possClassInstances usedNames ty = do
298 className <- getClassName clss
319 className <- getClassName clss
299 classDef <- lookupCtxt className classInfo
320 classDef <- lookupCtxt className classInfo
@@ -301,57 +322,70 @@ unifyWithHoles istate type1 = \type2 -> let
301 def <- lookupCtxt n (definitions ctxt)
322 def <- lookupCtxt n (definitions ctxt)
302 ty <- normaliseC ctxt [] <$> (case typeFromDef def of Just x -> [x]; Nothing -> [])
323 ty <- normaliseC ctxt [] <$> (case typeFromDef def of Just x -> [x]; Nothing -> [])
303 let ty' = vToP (uniqueBinders usedNames ty)
324 let ty' = vToP (uniqueBinders usedNames ty)
304− return (getArgTys ty', getRetTy ty')
325+ return ty'
306− (clss, args) = unApply ty
327+ (clss, _) = unApply ty
307 getClassName (P (TCon _ _) className _) = [className]
328 getClassName (P (TCon _ _) className _) = [className]
308 getClassName _ = []
329 getClassName _ = []
332+ getResults :: Q.PQueue Score State -> Maybe Score
333+ getResults queue = do
334+ ((nextScore, next), rest) <- Q.minViewWithKey queue
336+ then return nextScore
338+ guard (defaultScoreFunction nextScore <= maxScore)
339+ let additions = if scoreCriterion nextScore
340+ then Q.fromList [ (score state, state) | state <- nextSteps next ]
342+ getResults (Q.union rest additions)
344+ isFinal (State [] [] [] [] [] score _) = True
310 -- | Find all possible matches starting from a given state.
347 -- | Find all possible matches starting from a given state.
311− possibleMatches :: State -> [Score]
348+ -- We go in stages rather than concatenating all three results in hopes of narrowing
312− possibleMatches (State [] [] [] [] [] scoreAcc _) = [scoreAcc]
349+ -- the search tree. Once we advance in a phase, there should be no going back.
350+ nextSteps :: State -> [State]
352+ -- Stage 2 - match typeclasses
353+ nextSteps (State [] [] [] c1 c2 scoreAcc usedNames) =
354+ if null results2 then results3 else results2
356+ -- try to match a typeclass argument from the left with a typeclass argument from the right
358+ catMaybes [ unifyQueue (State [] [] []
359+ (deleteFromArgList n1 c1) (map (second subst2for1) (deleteFromArgList n2 c2)) scoreAcc usedNames) [(ty1, ty2)]
360+ | (n1, ty1) <- c1, (n2, ty2) <- c2, let subst2for1 = psubst n2 (P Bound n1 ty1)]
362+ -- try to hunt match a typeclass constraint by replacing it with an instance
363+ results3 = results3A ++ results3B
364+ typeClassArgs classes = [ ((n, ty), inst) | (n, ty) <- classes, inst <- possClassInstances usedNames ty ]
365+ results3A = [ State [] [] []
366+ (deleteFromArgList n c1 ++ newClassArgs) c2
367+ (scoreAcc `mappend` (mempty { leftTypeClass = 1 }))
368+ (usedNames ++ newHoles)
369+ | ((n, ty), inst) <- typeClassArgs c1
370+ , newClassArgs <- maybeToList $ typeclassUnify classInfo ctxt ty inst
371+ , let newHoles = map fst newClassArgs ]
372+ results3B = [ State [] [] []
373+ c1 (deleteFromArgList n c2 ++ newClassArgs)
374+ (scoreAcc `mappend` (mempty { rightTypeClass = 1 }))
375+ (usedNames ++ newHoles)
376+ | ((n, ty), inst) <- typeClassArgs c2
377+ , newClassArgs <- maybeToList $ typeclassUnify classInfo ctxt ty inst
378+ , let newHoles = map fst newClassArgs ]
380+ -- Stage 1 - match arguments
313− possibleMatches (State holes dag1 dag2 c1 c2 scoreAcc usedNames) =
381+ nextSteps (State holes dag1 dag2 c1 c2 scoreAcc usedNames) = results1 where
314− concat [ possibleMatches state | state <- results, scoreCriterion (score state) ] where
316 -- we only try to match arguments whose names don't appear in the types
383 -- we only try to match arguments whose names don't appear in the types
317 -- of any other arguments
384 -- of any other arguments
318 canBeFirst :: ArgsDAG -> [(Name, Type)]
385 canBeFirst :: ArgsDAG -> [(Name, Type)]
319 canBeFirst = map fst . filter (S.null . snd . snd)
386 canBeFirst = map fst . filter (S.null . snd . snd)
321− -- this is done rather than concatenating all three results in hopes of narrowing
323− results = fromMaybe [] $ find (not . null) [results1, results2, results3]
325 -- try to match an argument from the left with an argument from the right
388 -- try to match an argument from the left with an argument from the right
326 results1 = catMaybes [ unifyQueue (State (holes \\ [n1, n2]) (deleteFromDag n1 dag1)
389 results1 = catMaybes [ unifyQueue (State (holes \\ [n1, n2]) (deleteFromDag n1 dag1)
327 ((inArgTys subst2for1) $ deleteFromDag n2 dag2) c1 (map (second subst2for1) c2) scoreAcc usedNames) [(ty1, ty2)]
390 ((inArgTys subst2for1) $ deleteFromDag n2 dag2) c1 (map (second subst2for1) c2) scoreAcc usedNames) [(ty1, ty2)]
328 | (n1, ty1) <- canBeFirst dag1, (n2, ty2) <- canBeFirst dag2, let subst2for1 = psubst n2 (P Bound n1 ty1)]
391 | (n1, ty1) <- canBeFirst dag1, (n2, ty2) <- canBeFirst dag2, let subst2for1 = psubst n2 (P Bound n1 ty1)]
330− -- try to match a typeclass argument from the left with a typeclass argument from the right
331− results2 = catMaybes [ unifyQueue (State (holes \\ [n1, n2]) dag1 (inArgTys subst2for1 dag2)
332− (deleteFromArgList n1 c1) (map (second subst2for1) (deleteFromArgList n2 c2)) scoreAcc usedNames) [(ty1, ty2)]
333− | (n1, ty1) <- c1, (n2, ty2) <- c2, let subst2for1 = psubst n2 (P Bound n1 ty1)]
335− -- try to hunt match a typeclass constraint by replacing it with an instance
336− results3 = if null dag1 || null dag2 then catMaybes (results3A ++ results3B) else []
337− typeClassArgs classes = [ ((n, ty), inst) | (n, ty) <- classes, inst <- possClassInstances usedNames ty ]
338− noDeps (n, ty) = ((n, ty), (0, S.empty))
339− results3A = [ unifyQueue
340− (State ((holes \\ [n]) ++ newHoles) (dag1 ++ map noDeps newNonClassArgs) dag2
341− (deleteFromArgList n c1 ++ newClassArgs) c2
342− (scoreAcc `mappend` (mempty { leftTypeClass = 1 }))
343− (usedNames ++ newHoles)
346− | ((n, ty), (newArgs, inst)) <- typeClassArgs c1, let newHoles = map fst newArgs
347− , let (newClassArgs, newNonClassArgs) = partition (isTypeClassArg . snd) newArgs ]
348− results3B = [ unifyQueue
349− (State ((holes \\ [n]) ++ newHoles) dag1 (dag2 ++ map noDeps newNonClassArgs)
350− c1 (deleteFromArgList n c2 ++ newClassArgs)
351− (scoreAcc `mappend` (mempty { rightTypeClass = 1 }))
352− (usedNames ++ newHoles)
355− | ((n, ty), (newArgs, inst)) <- typeClassArgs c2, let newHoles = map fst newArgs
356− , let (newClassArgs, newNonClassArgs) = partition (isTypeClassArg . snd) newArgs ]