@@ -7,9 +7,12 @@ import Prelude.Compat
77import Data.Aeson.TH
88import qualified Data.Aeson as A
99import qualified Data.Map as M
10+import qualified Data.Set as S
1011import Data.Maybe (fromMaybe)
1112import Data.Text (Text)
1213import qualified Data.Text as T
14+import Data.List (nub)
15+import qualified Data.Graph as G
1316
1417import Language.PureScript.Crash
1518import Language.PureScript.Kinds
@@ -48,6 +51,10 @@ data TypeClassData = TypeClassData
4851 -- are considered bound in the types appearing in these constraints.
4952 , typeClassDependencies :: [FunctionalDependency]
5053 -- ^ A list of functional dependencies for the type arguments of this class.
54+ , typeClassDeterminedArguments :: S.Set Int
55+ -- ^ A set of indexes of type argument that are fully determined by other
56+ -- arguments via functional dependencies. This can be computed from both
57+ -- typeClassArguments and typeClassDependencies.
5158 } deriving Show
5259
5360-- | A functional dependency indicates a relationship between two sets of
@@ -65,6 +72,50 @@ data FunctionalDependency = FunctionalDependency
6572initEnvironment :: Environment
6673initEnvironment = Environment M.empty primTypes M.empty M.empty M.empty primClasses
6774
75+-- |
76+-- A constructor for TypeClassData that computes which type class arguments are fully determined.
77+-- Fully determined means that this argument cannot be used when selecting a type class instance.
78+--
79+-- An example of the difference between determined and fully determined would be with the class:
80+-- ```class C a b c | a -> b, b -> a, b -> c```
81+-- In this case, `a` must differ when `b` differs, and vice versa - each is determined by the other.
82+-- Both `a` and `b` can be used in selecting a type class instance. However, `c` cannot - it is
83+-- fully determined by `a` and `b`.
84+--
85+-- Define a graph of type class arguments with edges being fundep determiners to determined.
86+-- An argument is fully determined if doesn't appear at the start of a path of strongly connected components.
87+-- An argument is not fully determined otherwise.
88+--
89+-- The way we compute this is by saying: an argument X is fully determined if there are arguments that
90+-- determine X that X does not determine. This is the same thing: everything X determines includes everything
91+-- in its SCC, and everything determining X is either before it in an SCC path, or in the same SCC.
92+makeTypeClassData
93+ :: [(Text, Maybe Kind)]
94+ -> [(Ident, Type)]
95+ -> [Constraint]
96+ -> [FunctionalDependency]
97+ -> TypeClassData
98+makeTypeClassData args m s deps = TypeClassData args m s deps determinedArgs
99+ where
100+ -- list all the edges in the graph: for each fundep an edge exists for each determiner to each determined
101+ contributingDeps = M.fromListWith (++) $ do
102+ fd <- deps
103+ src <- fdDeterminers fd
104+ (src, fdDetermined fd) : map (, []) (fdDetermined fd)
105+
106+ -- here we build a graph of which arguments determine other arguments
107+ (depGraph, _, fromKey) = G.graphFromEdges ((\(n, v) -> (n, n, nub v)) <$> M.toList contributingDeps)
108+
109+ -- do there exist any arguments that contribute to `arg` that `arg` doesn't contribute to
110+ isFunDepDetermined arg = case fromKey arg of
111+ Nothing -> False -- not mentioned in fundeps
112+ Just v -> let contributesToVar = G.reachable (G.transposeG depGraph) v
113+ varContributesTo = G.reachable depGraph v
114+ in any (\r -> not (r `elem` varContributesTo)) contributesToVar
115+
116+ -- find all the arguments that are determined
117+ determinedArgs = S.fromList $ filter isFunDepDetermined [0 .. length args - 1]
118+
68119-- |
69120-- The visibility of a name in scope
70121--
@@ -264,8 +315,8 @@ primTypes =
264315primClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData
265316primClasses =
266317 M.fromList
267− [ (primName "Partial", (TypeClassData [] [] [] []))
268− , (primName "Fail", (TypeClassData [("message", Just Symbol)] [] [] []))
318+ [ (primName "Partial", (makeTypeClassData [] [] [] []))
319+ , (primName "Fail", (makeTypeClassData [("message", Just Symbol)] [] [] []))
269320 ]
270321
271322-- |