@@ -1,36 +1,45 @@
2+Module : Idris.TypeSearch
3+Description : A Hoogle for Idris.
6+Maintainer : The Idris Community.
1{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE ScopedTypeVariables #-}
3module Idris.TypeSearch (
10module Idris.TypeSearch (
4− searchByType, searchPred, defaultScoreFunction
13+ , defaultScoreFunction
7import Control.Applicative (Applicative (..), (<$>), (<*>), (<|>))
16import Control.Applicative (Applicative (..), (<$>), (<*>), (<|>))
8−import Control.Arrow (first, second, (&&&), (***))
17+import Control.Arrow (first, second, (&&&), (***))
9−import Control.Monad (when, guard)
18+import Control.Monad (when, guard)
11−import Data.List (find, partition, (\\))
20+import Data.List (find, partition, (\\))
12−import Data.Map (Map)
21+import Data.Map (Map)
13−import qualified Data.Map as M
22+import qualified Data.Map as M
14−import Data.Maybe (catMaybes, fromMaybe, isJust, maybeToList, mapMaybe)
23+import Data.Maybe (catMaybes, fromMaybe, isJust, maybeToList, mapMaybe)
15−import Data.Monoid (Monoid (mempty, mappend))
24+import Data.Monoid (Monoid (mempty, mappend))
16−import Data.Ord (comparing)
25+import Data.Ord (comparing)
17import qualified Data.PriorityQueue.FingerTree as Q
26import qualified Data.PriorityQueue.FingerTree as Q
18−import Data.Set (Set)
27+import Data.Set (Set)
19−import qualified Data.Set as S
28+import qualified Data.Set as S
20−import qualified Data.Text as T (pack, isPrefixOf)
29+import qualified Data.Text as T (pack, isPrefixOf)
21−import Data.Traversable (traverse)
30+import Data.Traversable (traverse)
23−import Idris.AbsSyntax (addUsingConstraints, addImpl, getIState, putIState, implicit, logLvl)
32+import Idris.AbsSyntax (addUsingConstraints, addImpl, getIState, putIState, implicit, logLvl)
24−import Idris.AbsSyntaxTree (class_instances, ClassInfo, defaultSyntax, eqTy, Idris,
33+import Idris.AbsSyntaxTree (class_instances, ClassInfo, defaultSyntax, eqTy, Idris
25− IState (idris_classes, idris_docstrings, tt_ctxt, idris_outputmode),
34+ , IState (idris_classes, idris_docstrings, tt_ctxt, idris_outputmode),
26− implicitAllowed, OutputMode(..), PTerm, toplevel)
35+ implicitAllowed, OutputMode(..), PTerm, toplevel)
27import Idris.Core.Evaluate (Context (definitions), Def (Function, TyDecl, CaseOp), normaliseC)
36import Idris.Core.Evaluate (Context (definitions), Def (Function, TyDecl, CaseOp), normaliseC)
28−import Idris.Core.TT hiding (score)
37+import Idris.Core.TT hiding (score)
29−import Idris.Core.Unify (match_unify)
38+import Idris.Core.Unify (match_unify)
30−import Idris.Delaborate (delabTy)
39+import Idris.Delaborate (delabTy)
31−import Idris.Docstrings (noDocs, overview)
40+import Idris.Docstrings (noDocs, overview)
32−import Idris.Elab.Type (elabType)
41+import Idris.Elab.Type (elabType)
33−import Idris.Output (iputStrLn, iRenderOutput, iPrintResult, iRenderError, iRenderResult, prettyDocumentedIst)
42+import Idris.Output (iputStrLn, iRenderOutput, iPrintResult, iRenderError, iRenderResult, prettyDocumentedIst)
34import Idris.IBC
43import Idris.IBC
36import Prelude hiding (pred)
45import Prelude hiding (pred)
@@ -40,9 +49,9 @@ import Util.Pretty (text, char, vsep, (<>), Doc, annotate)
40searchByType :: [String] -> PTerm -> Idris ()
49searchByType :: [String] -> PTerm -> Idris ()
41searchByType pkgs pterm = do
50searchByType pkgs pterm = do
42 i <- getIState -- save original
51 i <- getIState -- save original
43− when (not (null pkgs)) $
52+ when (not (null pkgs)) $
44 iputStrLn $ "Searching packages: " ++ showSep ", " pkgs
53 iputStrLn $ "Searching packages: " ++ showSep ", " pkgs
46 mapM_ loadPkgIndex pkgs
55 mapM_ loadPkgIndex pkgs
47 pterm' <- addUsingConstraints syn emptyFC pterm
56 pterm' <- addUsingConstraints syn emptyFC pterm
48 pterm'' <- implicit toplevel syn name pterm'
57 pterm'' <- implicit toplevel syn name pterm'
@@ -67,9 +76,9 @@ searchByType pkgs pterm = do
67 name = sMN 0 "searchType" -- name
76 name = sMN 0 "searchType" -- name
69-- | Conduct a type-directed search using a given match predicate
78-- | Conduct a type-directed search using a given match predicate
70−searchUsing :: (IState -> Type -> [(Name, Type)] -> [(Name, a)])
79+searchUsing :: (IState -> Type -> [(Name, Type)] -> [(Name, a)])
71 -> IState -> Type -> [(Name, a)]
80 -> IState -> Type -> [(Name, a)]
72−searchUsing pred istate ty = pred istate nty . concat . M.elems $
81+searchUsing pred istate ty = pred istate nty . concat . M.elems $
73 M.mapWithKey (\key -> M.toAscList . M.mapMaybe (f key)) (definitions ctxt)
82 M.mapWithKey (\key -> M.toAscList . M.mapMaybe (f key)) (definitions ctxt)
75 nty = normaliseC ctxt [] ty
84 nty = normaliseC ctxt [] ty
@@ -81,7 +90,7 @@ searchUsing pred istate ty = pred istate nty . concat . M.elems $
81 special :: Name -> Bool
90 special :: Name -> Bool
82 special (NS n _) = special n
91 special (NS n _) = special n
83 special (SN _) = True
92 special (SN _) = True
84− special (UN n) = T.pack "default#" `T.isPrefixOf` n
93+ special (UN n) = T.pack "default#" `T.isPrefixOf` n
85 || n `elem` map T.pack ["believe_me", "really_believe_me"]
94 || n `elem` map T.pack ["believe_me", "really_believe_me"]
86 special _ = False
95 special _ = False
@@ -117,10 +126,10 @@ reverseDag xs = map f xs where
118-- run vToP first!
127-- run vToP first!
119-- | Compute a directed acyclic graph corresponding to the
128-- | Compute a directed acyclic graph corresponding to the
120−-- arguments of a function.
129+-- arguments of a function.
121-- returns [(the name and type of the bound variable
130-- returns [(the name and type of the bound variable
122-- the names in the type of the bound variable)]
131-- the names in the type of the bound variable)]
123−computeDagP :: Ord n
132+computeDagP :: Ord n
124 => (TT n -> Bool) -- ^ filter to remove some arguments
133 => (TT n -> Bool) -- ^ filter to remove some arguments
126 -> ([((n, TT n), Set n)], [(n, TT n)], TT n)
135 -> ([((n, TT n), Set n)], [(n, TT n)], TT n)
@@ -196,7 +205,7 @@ displayScore score = case both noMods (asymMods score) of
196 Sided True False -> annotated LT "<" -- found type is more general than searched type
205 Sided True False -> annotated LT "<" -- found type is more general than searched type
197 Sided False True -> annotated GT ">" -- searched type is more general than found type
206 Sided False True -> annotated GT ">" -- searched type is more general than found type
198 Sided False False -> text "_"
207 Sided False False -> text "_"
200 annotated ordr = annotate (AnnSearchResult ordr) . text
209 annotated ordr = annotate (AnnSearchResult ordr) . text
201 noMods (Mods app tcApp tcIntro) = app + tcApp + tcIntro == 0
210 noMods (Mods app tcApp tcIntro) = app + tcApp + tcIntro == 0
@@ -208,19 +217,19 @@ scoreCriterion (Score _ _ amods) = not
208 ( sided (&&) (both ((> 0) . argApp) amods)
217 ( sided (&&) (both ((> 0) . argApp) amods)
209 || sided (+) (both argApp amods) > 4
218 || sided (+) (both argApp amods) > 4
210 || sided (||) (both (\(Mods _ tcApp tcIntro) -> tcApp > 3 || tcIntro > 3) amods)
219 || sided (||) (both (\(Mods _ tcApp tcIntro) -> tcApp > 3 || tcIntro > 3) amods)
213-- | Convert a 'Score' to an 'Int' to provide an order for search results.
222-- | Convert a 'Score' to an 'Int' to provide an order for search results.
214-- Lower scores are better.
223-- Lower scores are better.
215defaultScoreFunction :: Score -> Int
224defaultScoreFunction :: Score -> Int
216−defaultScoreFunction (Score trans eqFlip amods) =
225+defaultScoreFunction (Score trans eqFlip amods) =
217 trans + eqFlip + linearPenalty + upAndDowncastPenalty
226 trans + eqFlip + linearPenalty + upAndDowncastPenalty
219 -- prefer finding a more general type to a less general type
228 -- prefer finding a more general type to a less general type
220− linearPenalty = (\(Sided l r) -> 3 * l + r)
229+ linearPenalty = (\(Sided l r) -> 3 * l + r)
221 (both (\(Mods app tcApp tcIntro) -> 3 * app + 4 * tcApp + 2 * tcIntro) amods)
230 (both (\(Mods app tcApp tcIntro) -> 3 * app + 4 * tcApp + 2 * tcIntro) amods)
222 -- it's very bad to have *both* upcasting and downcasting
231 -- it's very bad to have *both* upcasting and downcasting
223− upAndDowncastPenalty = 100 *
232+ upAndDowncastPenalty = 100 *
224 sided (*) (both (\(Mods app tcApp tcIntro) -> 2 * app + tcApp + tcIntro) amods)
233 sided (*) (both (\(Mods app tcApp tcIntro) -> 2 * app + tcApp + tcIntro) amods)
226instance Ord Score where
235instance Ord Score where
@@ -237,7 +246,7 @@ instance Monoid AsymMods where
238instance Monoid Score where
247instance Monoid Score where
239 mempty = Score 0 0 mempty
248 mempty = Score 0 0 mempty
240− (Score t e mods) `mappend` (Score t' e' mods') = Score (t + t') (e + e') (mods `mappend` mods')
249+ (Score t e mods) `mappend` (Score t' e' mods') = Score (t + t') (e + e') (mods `mappend` mods')
242-- | A directed acyclic graph representing the arguments to a function
251-- | A directed acyclic graph representing the arguments to a function
243-- The 'Int' represents the position of the argument (1st argument, 2nd, etc.)
252-- The 'Int' represents the position of the argument (1st argument, 2nd, etc.)
@@ -311,7 +320,7 @@ flipEqualities t = case t of
311 [(0, eq1), (1, app (app (app (app eq tyR) tyL) valR) valL)]
320 [(0, eq1), (1, app (app (app (app eq tyR) tyL) valR) valL)]
312 Bind n binder sc -> (\bind' (j, sc') -> (fst (binderTy bind') + j, Bind n (fmap snd bind') sc'))
321 Bind n binder sc -> (\bind' (j, sc') -> (fst (binderTy bind') + j, Bind n (fmap snd bind') sc'))
313 <$> traverse flipEqualities binder <*> flipEqualities sc
322 <$> traverse flipEqualities binder <*> flipEqualities sc
314− App _ f x -> (\(i, f') (j, x') -> (i + j, app f' x'))
323+ App _ f x -> (\(i, f') (j, x') -> (i + j, app f' x'))
315 <$> flipEqualities f <*> flipEqualities x
324 <$> flipEqualities f <*> flipEqualities x
316 t' -> [(0, t')]
325 t' -> [(0, t')]
317 where app = App Complete
326 where app = App Complete
@@ -329,13 +338,13 @@ matchTypesBulk istate maxScore type1 types = getAllResults startQueueOfQueues wh
330 ty2s = (\(i, dag) (j, retTy) -> (i + j, dag, retTy))
339 ty2s = (\(i, dag) (j, retTy) -> (i + j, dag, retTy))
331 <$> flipEqualitiesDag dag2 <*> flipEqualities retTy2
340 <$> flipEqualitiesDag dag2 <*> flipEqualities retTy2
332− flipEqualitiesDag dag = case dag of
341+ flipEqualitiesDag dag = case dag of
333 [] -> [(0, [])]
342 [] -> [(0, [])]
334− ((n, ty), (pos, deps)) : xs ->
343+ ((n, ty), (pos, deps)) : xs ->
335 (\(i, ty') (j, xs') -> (i + j , ((n, ty'), (pos, deps)) : xs'))
344 (\(i, ty') (j, xs') -> (i + j , ((n, ty'), (pos, deps)) : xs'))
336 <$> flipEqualities ty <*> flipEqualitiesDag xs
345 <$> flipEqualities ty <*> flipEqualitiesDag xs
337 startStates (numEqFlips, sndDag, sndRetTy) = do
346 startStates (numEqFlips, sndDag, sndRetTy) = do
338− state <- unifyQueue (State startingHoles
347+ state <- unifyQueue (State startingHoles
339 (Sided (dag1, typeClassArgs1) (sndDag, typeClassArgs2))
348 (Sided (dag1, typeClassArgs1) (sndDag, typeClassArgs2))
340 (mempty { equalityFlips = numEqFlips }) usedns) [(retTy1, sndRetTy)]
349 (mempty { equalityFlips = numEqFlips }) usedns) [(retTy1, sndRetTy)]
341 return (score state, state)
350 return (score state, state)
@@ -346,7 +355,7 @@ matchTypesBulk istate maxScore type1 types = getAllResults startQueueOfQueues wh
346 usedns = map fst startingHoles
355 usedns = map fst startingHoles
347 startingHoles = argNames1 ++ argNames2
356 startingHoles = argNames1 ++ argNames2
349− startingTypes = [(retTy1, retTy2)]
358+ startingTypes = [(retTy1, retTy2)]
352 startQueueOfQueues :: Q.PQueue Score (info, Q.PQueue Score State)
361 startQueueOfQueues :: Q.PQueue Score (info, Q.PQueue Score State)
@@ -372,14 +381,14 @@ matchTypesBulk istate maxScore type1 types = getAllResults startQueueOfQueues wh
372 (dag1, typeClassArgs1, retTy1) = makeDag type1
381 (dag1, typeClassArgs1, retTy1) = makeDag type1
373 argNames1 = map fst dag1
382 argNames1 = map fst dag1
374 makeDag :: Type -> (ArgsDAG, Classes, Type)
383 makeDag :: Type -> (ArgsDAG, Classes, Type)
375− makeDag = first3 (zipWith (\i (ty, deps) -> (ty, (i, deps))) [0..] . reverseDag) .
384+ makeDag = first3 (zipWith (\i (ty, deps) -> (ty, (i, deps))) [0..] . reverseDag) .
376 computeDagP (isTypeClassArg classInfo) . vToP . unLazy
385 computeDagP (isTypeClassArg classInfo) . vToP . unLazy
377 first3 f (a,b,c) = (f a, b, c)
386 first3 f (a,b,c) = (f a, b, c)
379 -- update our state with the unification resolutions
388 -- update our state with the unification resolutions
380 resolveUnis :: [(Name, Type)] -> State -> Maybe (State, [(Type, Type)])
389 resolveUnis :: [(Name, Type)] -> State -> Maybe (State, [(Type, Type)])
381 resolveUnis [] state = Just (state, [])
390 resolveUnis [] state = Just (state, [])
382− resolveUnis ((name, term@(P Bound name2 _)) : xs)
391+ resolveUnis ((name, term@(P Bound name2 _)) : xs)
383 state | isJust findArgs = do
392 state | isJust findArgs = do
384 ((ty1, ix1), (ty2, ix2)) <- findArgs
393 ((ty1, ix1), (ty2, ix2)) <- findArgs
@@ -408,7 +417,7 @@ matchTypesBulk istate maxScore type1 types = getAllResults startQueueOfQueues wh
408 -- due to injectivity
417 -- due to injectivity
409 matchedVarMap = usedVars term
418 matchedVarMap = usedVars term
410 bothT f (x, y) = (f x, f y)
419 bothT f (x, y) = (f x, f y)
411− (injUsedVars, notInjUsedVars) = bothT M.keys . M.partition id . M.filterWithKey (\k _-> k `elem` map fst hs) $ M.map snd matchedVarMap
420+ (injUsedVars, notInjUsedVars) = bothT M.keys . M.partition id . M.filterWithKey (\k _-> k `elem` map fst hs) $ M.map snd matchedVarMap
412 varsInTy = injUsedVars ++ notInjUsedVars
421 varsInTy = injUsedVars ++ notInjUsedVars
413 toDelete = name : varsInTy
422 toDelete = name : varsInTy
414 deleteMany = foldr (.) id (map deleteName toDelete)
423 deleteMany = foldr (.) id (map deleteName toDelete)
@@ -417,7 +426,7 @@ matchTypesBulk istate maxScore type1 types = getAllResults startQueueOfQueues wh
418 addScore additions theState = theState { score = let s = score theState in
427 addScore additions theState = theState { score = let s = score theState in
419 s { asymMods = asymMods s `mappend` additions } }
428 s { asymMods = asymMods s `mappend` additions } }
420− state' = state { holes = filter (not . (`elem` toDelete) . fst) hs
429+ state' = state { holes = filter (not . (`elem` toDelete) . fst) hs
421 , argsAndClasses = both (modifyTypes (subst name term) . deleteMany) (argsAndClasses state) }
430 , argsAndClasses = both (modifyTypes (subst name term) . deleteMany) (argsAndClasses state) }
422 nextStep = resolveUnis xs state'
431 nextStep = resolveUnis xs state'
@@ -427,8 +436,8 @@ matchTypesBulk istate maxScore type1 types = getAllResults startQueueOfQueues wh
427 unifyQueue state [] = return state
436 unifyQueue state [] = return state
428 unifyQueue state ((ty1, ty2) : queue) = do
437 unifyQueue state ((ty1, ty2) : queue) = do
429 --trace ("go: \n" ++ show state) True `seq` return ()
438 --trace ("go: \n" ++ show state) True `seq` return ()
430− res <- tcToMaybe $ match_unify ctxt [ (n, Pi Nothing ty (TType (UVar 0))) | (n, ty) <- holes state]
439+ res <- tcToMaybe $ match_unify ctxt [ (n, Pi Nothing ty (TType (UVar 0))) | (n, ty) <- holes state]
431− (ty1, Nothing)
440+ (ty1, Nothing)
432 (ty2, Nothing) [] (map fst $ holes state) []
441 (ty2, Nothing) [] (map fst $ holes state) []
433 (state', queueAdditions) <- resolveUnis res state
442 (state', queueAdditions) <- resolveUnis res state
434 guard $ scoreCriterion (score state')
443 guard $ scoreCriterion (score state')
@@ -453,7 +462,7 @@ matchTypesBulk istate maxScore type1 types = getAllResults startQueueOfQueues wh
453 nextStepsQueue :: Q.PQueue Score State -> Maybe (Either (Q.PQueue Score State) Score)
462 nextStepsQueue :: Q.PQueue Score State -> Maybe (Either (Q.PQueue Score State) Score)
454 nextStepsQueue queue = do
463 nextStepsQueue queue = do
455 ((nextScore, next), rest) <- Q.minViewWithKey queue
464 ((nextScore, next), rest) <- Q.minViewWithKey queue
456− Just $ if isFinal next
465+ Just $ if isFinal next
457 then Right nextScore
466 then Right nextScore
458 else let additions = if scoreCriterion nextScore
467 else let additions = if scoreCriterion nextScore
459 then Q.fromList [ (score state, state) | state <- nextSteps next ]
468 then Q.fromList [ (score state, state) | state <- nextSteps next ]
@@ -469,19 +478,19 @@ matchTypesBulk istate maxScore type1 types = getAllResults startQueueOfQueues wh
469 nextSteps :: State -> [State]
478 nextSteps :: State -> [State]
471 -- Stage 3 - match typeclasses
480 -- Stage 3 - match typeclasses
472− nextSteps (State [] unresolved@(Sided ([], c1) ([], c2)) scoreAcc usedns) =
481+ nextSteps (State [] unresolved@(Sided ([], c1) ([], c2)) scoreAcc usedns) =
473 if null results3 then results4 else results3
482 if null results3 then results4 else results3
475 -- try to match a typeclass argument from the left with a typeclass argument from the right
484 -- try to match a typeclass argument from the left with a typeclass argument from the right
476 results3 =
485 results3 =
477− catMaybes [ unifyQueue (State []
486+ catMaybes [ unifyQueue (State []
478 (Sided ([], deleteFromArgList n1 c1)
487 (Sided ([], deleteFromArgList n1 c1)
479 ([], map (second subst2for1) (deleteFromArgList n2 c2)))
488 ([], map (second subst2for1) (deleteFromArgList n2 c2)))
480− scoreAcc usedns) [(ty1, ty2)]
489+ scoreAcc usedns) [(ty1, ty2)]
481 | (n1, ty1) <- c1, (n2, ty2) <- c2, let subst2for1 = psubst n2 (P Bound n1 ty1)]
490 | (n1, ty1) <- c1, (n2, ty2) <- c2, let subst2for1 = psubst n2 (P Bound n1 ty1)]
483 -- try to hunt match a typeclass constraint by replacing it with an instance
492 -- try to hunt match a typeclass constraint by replacing it with an instance
484− results4 = [ State [] (both (\(cs, _, _) -> ([], cs)) sds)
493+ results4 = [ State [] (both (\(cs, _, _) -> ([], cs)) sds)
485 (scoreAcc `mappend` Score 0 0 (both (\(_, amods, _) -> amods) sds))
494 (scoreAcc `mappend` Score 0 0 (both (\(_, amods, _) -> amods) sds))
486 (usedns ++ sided (++) (both (\(_, _, hs) -> hs) sds))
495 (usedns ++ sided (++) (both (\(_, _, hs) -> hs) sds))
487 | sds <- allMods ]
496 | sds <- allMods ]
@@ -512,10 +521,10 @@ matchTypesBulk istate maxScore type1 types = getAllResults startQueueOfQueues wh
512 canBeFirst = map fst . filter (S.null . snd . snd)
521 canBeFirst = map fst . filter (S.null . snd . snd)
514 -- try to match an argument from the left with an argument from the right
523 -- try to match an argument from the left with an argument from the right
515− results1 = catMaybes [ unifyQueue (State (filter (not . (`elem` [n1,n2]) . fst) hs)
524+ results1 = catMaybes [ unifyQueue (State (filter (not . (`elem` [n1,n2]) . fst) hs)
516− (Sided (deleteFromDag n1 dagL, c1)
525+ (Sided (deleteFromDag n1 dagL, c1)
517 (inArgTys subst2for1 $ deleteFromDag n2 dagR, map (second subst2for1) c2))
526 (inArgTys subst2for1 $ deleteFromDag n2 dagR, map (second subst2for1) c2))
518− scoreAcc usedns) [(ty1, ty2)]
527+ scoreAcc usedns) [(ty1, ty2)]
519 | (n1, ty1) <- canBeFirst dagL, (n2, ty2) <- canBeFirst dagR
528 | (n1, ty1) <- canBeFirst dagL, (n2, ty2) <- canBeFirst dagR
520 , let subst2for1 = psubst n2 (P Bound n1 ty1)]
529 , let subst2for1 = psubst n2 (P Bound n1 ty1)]
@@ -523,7 +532,7 @@ matchTypesBulk istate maxScore type1 types = getAllResults startQueueOfQueues wh
524 -- Stage 2 - simply introduce a subset of the typeclasses
533 -- Stage 2 - simply introduce a subset of the typeclasses
525 -- we've finished, so take some classes
534 -- we've finished, so take some classes
526− takeSomeClasses (State [] unresolved@(Sided ([], _) ([], _)) scoreAcc usedns) =
535+ takeSomeClasses (State [] unresolved@(Sided ([], _) ([], _)) scoreAcc usedns) =
527 map statesFromMods . prod $ both (classMods . snd) unresolved
536 map statesFromMods . prod $ both (classMods . snd) unresolved
529 swap (Sided l r) = Sided r l
538 swap (Sided l r) = Sided r l
@@ -532,7 +541,7 @@ matchTypesBulk istate maxScore type1 types = getAllResults startQueueOfQueues wh
532 mods = swap (both snd sides) in
541 mods = swap (both snd sides) in
533 State [] classes (scoreAcc `mappend` (mempty { asymMods = mods })) usedns
542 State [] classes (scoreAcc `mappend` (mempty { asymMods = mods })) usedns
534 classMods :: Classes -> [(Classes, AsymMods)]
543 classMods :: Classes -> [(Classes, AsymMods)]
535− classMods cs = let lcs = length cs in
544+ classMods cs = let lcs = length cs in
536 [ (cs', mempty { typeClassIntro = lcs - length cs' }) | cs' <- subsets cs ]
545 [ (cs', mempty { typeClassIntro = lcs - length cs' }) | cs' <- subsets cs ]
537 prod :: Sided [a] -> [Sided a]
546 prod :: Sided [a] -> [Sided a]
538 prod (Sided ls rs) = [Sided l r | l <- ls, r <- rs]
547 prod (Sided ls rs) = [Sided l r | l <- ls, r <- rs]