@@ -19,7 +19,7 @@ import Control.Monad.State
1919import Control.Monad.Supply.Class (MonadSupply(..))
2020import Control.Monad.Writer
2121
22−import Data.Foldable (for_)
22+import Data.Foldable (for_, fold, toList)
2323import Data.Function (on)
2424import Data.List (minimumBy, nub)
2525import Data.Maybe (fromMaybe, maybeToList, mapMaybe)
@@ -37,11 +37,26 @@ import Language.PureScript.TypeClassDictionaries
3737import Language.PureScript.Types
3838import qualified Language.PureScript.Constants as C
3939
40+-- | Describes what sort of dictionary to generate for type class instances
41+data Evidence
42+ = NamedInstance (Qualified Ident)
43+ -- ^ An existing named instance
44+ | IsSymbolInstance String
45+ -- ^ Computed instance of the IsSymbol type class for a given Symbol literal
46+ deriving (Eq)
47+
48+-- | Extract the identifier of a named instance
49+namedInstanceIdentifier :: Evidence -> Maybe (Qualified Ident)
50+namedInstanceIdentifier (NamedInstance i) = Just i
51+namedInstanceIdentifier _ = Nothing
52+
53+-- | Description of a type class dictionary with instance evidence
54+type TypeClassDict = TypeClassDictionaryInScope Evidence
55+
4056-- | The 'InstanceContext' tracks those constraints which can be satisfied.
4157type InstanceContext = M.Map (Maybe ModuleName)
4258 (M.Map (Qualified (ProperName 'ClassName))
43− (M.Map (Qualified Ident)
44− TypeClassDictionaryInScope))
59+ (M.Map (Qualified Ident) NamedDict))
4560
4661-- | A type substitution which makes an instance head match a list of types.
4762--
@@ -88,7 +103,7 @@ replaceTypeClassDictionaries shouldGeneralize expr = flip evalStateT M.empty $ d
88103
89104-- | Three options for how we can handle a constraint, depending on the mode we're in.
90105data EntailsResult a
91− = Solved a TypeClassDictionaryInScope
106+ = Solved a TypeClassDict
92107 -- ^ We solved this constraint
93108 | Unsolved Constraint
94109 -- ^ We couldn't solve this constraint right now, it will be generalized
@@ -120,7 +135,8 @@ entails
120135entails SolverOptions{..} constraint context hints =
121136 solve constraint
122137 where
123− forClassName :: InstanceContext -> Qualified (ProperName 'ClassName) -> [Type] -> [TypeClassDictionaryInScope]
138+ forClassName :: InstanceContext -> Qualified (ProperName 'ClassName) -> [Type] -> [TypeClassDict]
139+ forClassName _ C.IsSymbol [TypeLevelString sym] = [TypeClassDictionaryInScope (IsSymbolInstance sym) [] C.IsSymbol [TypeLevelString sym] Nothing]
124140 forClassName ctx cn@(Qualified (Just mn) _) tys = concatMap (findDicts ctx cn) (nub (Nothing : Just mn : map Just (mapMaybe ctorModules tys)))
125141 forClassName _ _ _ = internalError "forClassName: expected qualified class name"
126142
@@ -130,8 +146,8 @@ entails SolverOptions{..} constraint context hints =
130146 ctorModules (TypeApp ty _) = ctorModules ty
131147 ctorModules _ = Nothing
132148
133− findDicts :: InstanceContext -> Qualified (ProperName 'ClassName) -> Maybe ModuleName -> [TypeClassDictionaryInScope]
134− findDicts ctx cn = maybe [] M.elems . (>>= M.lookup cn) . flip M.lookup ctx
149+ findDicts :: InstanceContext -> Qualified (ProperName 'ClassName) -> Maybe ModuleName -> [TypeClassDict]
150+ findDicts ctx cn = fmap (fmap NamedInstance) . maybe [] M.elems . (>>= M.lookup cn) . flip M.lookup ctx
135151
136152 valUndefined :: Expr
137153 valUndefined = Var (Qualified (Just (ModuleName [ProperName C.prim])) (Ident C.undefined))
@@ -181,7 +197,7 @@ entails SolverOptions{..} constraint context hints =
181197 -- Solve any necessary subgoals
182198 args <- solveSubgoals subst'' (tcdDependencies tcd)
183199 let match = foldr (\(superclassName, index) dict -> subclassDictionaryValue dict superclassName index)
184− (mkDictionary (tcdName tcd) args)
200+ (mkDictionary (tcdValue tcd) args)
185201 (tcdPath tcd)
186202 return match
187203 Unsolved unsolved -> do
@@ -213,7 +229,7 @@ entails SolverOptions{..} constraint context hints =
213229 -- fresh type variables. This function extends a substitution with fresh type variables
214230 -- as necessary, based on the types in the instance head.
215231 withFreshTypes
216− :: TypeClassDictionaryInScope
232+ :: TypeClassDict
217233 -> Matching Type
218234 -> m (Matching Type)
219235 withFreshTypes TypeClassDictionaryInScope{..} subst = do
@@ -232,7 +248,7 @@ entails SolverOptions{..} constraint context hints =
232248 t <- freshType
233249 return (s, t)
234250
235− unique :: [Type] -> [(a, TypeClassDictionaryInScope)] -> m (EntailsResult a)
251+ unique :: [Type] -> [(a, TypeClassDict)] -> m (EntailsResult a)
236252 unique tyArgs []
237253 | solverDeferErrors = return Deferred
238254 -- We need a special case for nullary type classes, since we want
@@ -242,7 +258,7 @@ entails SolverOptions{..} constraint context hints =
242258 unique _ [(a, dict)] = return $ Solved a dict
243259 unique tyArgs tcds
244260 | pairwiseAny overlapping (map snd tcds) = do
245− tell . errorMessage $ OverlappingInstances className' tyArgs (map (tcdName . snd) tcds)
261+ tell . errorMessage $ OverlappingInstances className' tyArgs (tcds >>= (toList . namedInstanceIdentifier . tcdValue . snd))
246262 return $ uncurry Solved (head tcds)
247263 | otherwise = return $ uncurry Solved (minimumBy (compare `on` length . tcdPath . snd) tcds)
248264
@@ -255,12 +271,12 @@ entails SolverOptions{..} constraint context hints =
255271 --
256272 -- Dictionaries which are subclass dictionaries cannot overlap, since otherwise the overlap would have
257273 -- been caught when constructing superclass dictionaries.
258− overlapping :: TypeClassDictionaryInScope -> TypeClassDictionaryInScope -> Bool
274+ overlapping :: TypeClassDict -> TypeClassDict -> Bool
259275 overlapping TypeClassDictionaryInScope{ tcdPath = _ : _ } _ = False
260276 overlapping _ TypeClassDictionaryInScope{ tcdPath = _ : _ } = False
261277 overlapping TypeClassDictionaryInScope{ tcdDependencies = Nothing } _ = False
262278 overlapping _ TypeClassDictionaryInScope{ tcdDependencies = Nothing } = False
263− overlapping tcd1 tcd2 = tcdName tcd1 /= tcdName tcd2
279+ overlapping tcd1 tcd2 = tcdValue tcd1 /= tcdValue tcd2
264280
265281 -- Create dictionaries for subgoals which still need to be solved by calling go recursively
266282 -- E.g. the goal (Show a, Show b) => Show (Either a b) can be satisfied if the current type
@@ -271,10 +287,11 @@ entails SolverOptions{..} constraint context hints =
271287 Just <$> traverse (go (work + 1) . mapConstraintArgs (map (replaceAllTypeVars (M.toList subst)))) subgoals
272288
273289 -- Make a dictionary from subgoal dictionaries by applying the correct function
274− mkDictionary :: Qualified Ident -> Maybe [Expr] -> Expr
275− mkDictionary fnName Nothing = Var fnName
276− mkDictionary fnName (Just []) = Var fnName
277− mkDictionary fnName (Just dicts) = foldl App (Var fnName) dicts
290+ mkDictionary :: Evidence -> Maybe [Expr] -> Expr
291+ mkDictionary (NamedInstance n) args = foldl App (Var n) (fold args)
292+ mkDictionary (IsSymbolInstance sym) _ = TypeClassDictionaryConstructorApp C.IsSymbol (Literal (ObjectLiteral fields)) where
293+ fields = [ ("reflectSymbol", Abs (Left (Ident C.__unused)) (Literal (StringLiteral sym)))
294+ ]
278295
279296 -- Turn a DictionaryValue into a Expr
280297 subclassDictionaryValue :: Expr -> Qualified (ProperName a) -> Integer -> Expr
@@ -286,7 +303,7 @@ entails SolverOptions{..} constraint context hints =
286303-- Check if an instance matches our list of types, allowing for types
287304-- to be solved via functional dependencies. If the types match, we return a
288305-- substitution which makes them match. If not, we return 'Nothing'.
289−matches :: [FunctionalDependency] -> TypeClassDictionaryInScope -> [Type] -> Maybe (Matching [Type])
306+matches :: [FunctionalDependency] -> TypeClassDict -> [Type] -> Maybe (Matching [Type])
290307matches deps TypeClassDictionaryInScope{..} tys = do
291308 -- First, find those types which match exactly
292309 let matched = zipWith typeHeadsAreEqual tys tcdInstanceTypes
@@ -402,7 +419,7 @@ newDictionaries
402419 => [(Qualified (ProperName 'ClassName), Integer)]
403420 -> Qualified Ident
404421 -> Constraint
405− -> m [TypeClassDictionaryInScope]
422+ -> m [NamedDict]
406423newDictionaries path name (Constraint className instanceTy _) = do
407424 tcs <- gets (typeClasses . checkEnv)
408425 let TypeClassData{..} = fromMaybe (internalError "newDictionaries: type class lookup failed") $ M.lookup className tcs
@@ -416,9 +433,9 @@ newDictionaries path name (Constraint className instanceTy _) = do
416433 instantiateSuperclass :: [String] -> [Type] -> [Type] -> [Type]
417434 instantiateSuperclass args supArgs tys = map (replaceAllTypeVars (zip args tys)) supArgs
418435
419−mkContext :: [TypeClassDictionaryInScope] -> InstanceContext
436+mkContext :: [NamedDict] -> InstanceContext
420437mkContext = foldr combineContexts M.empty . map fromDict where
421− fromDict d = M.singleton Nothing (M.singleton (tcdClassName d) (M.singleton (tcdName d) d))
438+ fromDict d = M.singleton Nothing (M.singleton (tcdClassName d) (M.singleton (tcdValue d) d))
422439
423440-- | Check all pairs of values in a list match a predicate
424441pairwiseAll :: (a -> a -> Bool) -> [a] -> Bool