-
Notifications
You must be signed in to change notification settings - Fork 571
Expand file tree
/
Copy pathTypeClasses.hs
More file actions
392 lines (353 loc) · 16.1 KB
/
TypeClasses.hs
File metadata and controls
392 lines (353 loc) · 16.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
-- |
-- This module implements the desugaring pass which creates newtypes for type class dictionaries
-- and value declarations for type class instances.
--
module Language.PureScript.Sugar.TypeClasses
( desugarTypeClasses
, typeClassMemberName
, superClassDictionaryNames
) where
import Prelude
import Control.Arrow (first, second)
import Control.Monad (unless)
import Control.Monad.Error.Class (MonadError(..))
import Control.Monad.State (MonadState(..), StateT, evalStateT, modify)
import Control.Monad.Supply.Class (MonadSupply)
import Data.Graph (SCC(..), stronglyConnComp)
import Data.List (find, partition)
import Data.List.NonEmpty (nonEmpty)
import Data.Map qualified as M
import Data.Maybe (catMaybes, mapMaybe, isJust)
import Data.List.NonEmpty qualified as NEL
import Data.Set qualified as S
import Data.Text (Text)
import Data.Traversable (for)
import Language.PureScript.Constants.Prim qualified as C
import Language.PureScript.Crash (internalError)
import Language.PureScript.Environment (DataDeclType(..), NameKind(..), TypeClassData(..), dictTypeName, function, makeTypeClassData, primClasses, primCoerceClasses, primIntClasses, primRowClasses, primRowListClasses, primSymbolClasses, primTypeErrorClasses, tyRecord)
import Language.PureScript.Errors hiding (isExported, nonEmpty)
import Language.PureScript.Externs (ExternsDeclaration(..), ExternsFile(..))
import Language.PureScript.Label (Label(..))
import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), ModuleName, Name(..), ProperName, ProperNameType(..), Qualified(..), QualifiedBy(..), coerceProperName, freshIdent, qualify, runIdent)
import Language.PureScript.PSString (mkString)
import Language.PureScript.Sugar.CaseDeclarations (desugarCases)
import Language.PureScript.TypeClassDictionaries (superclassName)
import Language.PureScript.Types
type MemberMap = M.Map (ModuleName, ProperName 'ClassName) TypeClassData
type Desugar = StateT MemberMap
-- |
-- Add type synonym declarations for type class dictionary types, and value declarations for type class
-- instance dictionary expressions.
--
desugarTypeClasses
:: (MonadSupply m, MonadError MultipleErrors m)
=> [ExternsFile]
-> Module
-> m Module
desugarTypeClasses externs = flip evalStateT initialState . desugarModule
where
initialState :: MemberMap
initialState =
mconcat
[ M.mapKeys (qualify C.M_Prim) primClasses
, M.mapKeys (qualify C.M_Prim_Coerce) primCoerceClasses
, M.mapKeys (qualify C.M_Prim_Row) primRowClasses
, M.mapKeys (qualify C.M_Prim_RowList) primRowListClasses
, M.mapKeys (qualify C.M_Prim_Symbol) primSymbolClasses
, M.mapKeys (qualify C.M_Prim_Int) primIntClasses
, M.mapKeys (qualify C.M_Prim_TypeError) primTypeErrorClasses
, M.fromList (externs >>= \ExternsFile{..} -> mapMaybe (fromExternsDecl efModuleName) efDeclarations)
]
fromExternsDecl
:: ModuleName
-> ExternsDeclaration
-> Maybe ((ModuleName, ProperName 'ClassName), TypeClassData)
fromExternsDecl mn (EDClass name args members implies deps tcIsEmpty) = Just ((mn, name), typeClass) where
typeClass = makeTypeClassData args members implies deps tcIsEmpty
fromExternsDecl _ _ = Nothing
desugarModule
:: (MonadSupply m, MonadError MultipleErrors m)
=> Module
-> Desugar m Module
desugarModule (Module ss coms name decls (Just exps)) = do
let (classDecls, restDecls) = partition isTypeClassDecl decls
classVerts = fmap (\d -> (d, classDeclName d, superClassesNames d)) classDecls
(classNewExpss, classDeclss) <- unzip <$> parU (stronglyConnComp classVerts) (desugarClassDecl name exps)
(restNewExpss, restDeclss) <- unzip <$> parU restDecls (desugarDecl name exps)
return $ Module ss coms name (concat restDeclss ++ concat classDeclss) $ Just (exps ++ catMaybes restNewExpss ++ catMaybes classNewExpss)
where
desugarClassDecl :: (MonadSupply m, MonadError MultipleErrors m)
=> ModuleName
-> [DeclarationRef]
-> SCC Declaration
-> Desugar m (Maybe DeclarationRef, [Declaration])
desugarClassDecl name' exps' (AcyclicSCC d) = desugarDecl name' exps' d
desugarClassDecl _ _ (CyclicSCC ds')
| Just ds'' <- nonEmpty ds' = throwError . errorMessage' (declSourceSpan (NEL.head ds'')) $ CycleInTypeClassDeclaration (NEL.map classDeclName ds'')
| otherwise = internalError "desugarClassDecl: empty CyclicSCC"
superClassesNames :: Declaration -> [Qualified (ProperName 'ClassName)]
superClassesNames (TypeClassDeclaration _ _ _ implies _ _) = fmap constraintName implies
superClassesNames _ = []
constraintName :: SourceConstraint -> Qualified (ProperName 'ClassName)
constraintName (Constraint _ cName _ _ _) = cName
classDeclName :: Declaration -> Qualified (ProperName 'ClassName)
classDeclName (TypeClassDeclaration _ pn _ _ _ _) = Qualified (ByModuleName name) pn
classDeclName _ = internalError "Expected TypeClassDeclaration"
desugarModule _ = internalError "Exports should have been elaborated in name desugaring"
{- Desugar type class and type class instance declarations
--
-- Type classes become newtypes for their dictionaries, and type instances become dictionary declarations.
-- Additional values are generated to access individual members of a dictionary, with the appropriate type.
--
-- E.g. the following
--
-- module Test where
--
-- class Foo a where
-- foo :: a -> a
--
-- instance fooString :: Foo String where
-- foo s = s ++ s
--
-- instance fooArray :: (Foo a) => Foo [a] where
-- foo = map foo
--
-- {- Superclasses -}
--
-- class (Foo a) <= Sub a where
-- sub :: a
--
-- instance subString :: Sub String where
-- sub = ""
--
-- becomes:
--
-- <TypeClassDeclaration Foo ...>
--
-- newtype Foo$Dict a = Foo$Dict { foo :: a -> a }
--
-- -- this following type is marked as not needing to be checked so a new Abs
-- -- is not introduced around the definition in type checking, but when
-- -- called the dictionary value is still passed in for the `dict` argument
-- foo :: forall a. (Foo$Dict a) => a -> a
-- foo (Foo$Dict dict) = dict.foo
--
-- fooString :: Foo$Dict String
-- fooString = Foo$Dict { foo: \s -> s ++ s }
--
-- fooArray :: forall a. (Foo$Dict a) => Foo$Dict [a]
-- fooArray = Foo$Dict { foo: map foo }
--
-- {- Superclasses -}
--
-- <TypeClassDeclaration Sub ...>
--
-- newtype Sub$Dict a = Sub$Dict { sub :: a
-- , "Foo0" :: {} -> Foo$Dict a
-- }
--
-- -- As with `foo` above, this type is unchecked at the declaration
-- sub :: forall a. (Sub$Dict a) => a
-- sub (Sub$Dict dict) = dict.sub
--
-- subString :: Sub$Dict String
-- subString = Sub$Dict { sub: "",
-- , "Foo0": \_ -> <DeferredDictionary Foo String>
-- }
--
-- and finally as the generated javascript:
--
-- var foo = function (dict) {
-- return dict.foo;
-- };
--
-- var fooString = {
-- foo: function (s) {
-- return s + s;
-- }
-- };
--
-- var fooArray = function (dictFoo) {
-- return {
-- foo: map(foo(dictFoo))
-- };
-- };
--
-- var sub = function (dict) {
-- return dict.sub;
-- };
--
-- var subString = {
-- sub: "",
-- Foo0: function () {
-- return fooString;
-- }
-- };
-}
desugarDecl
:: (MonadSupply m, MonadError MultipleErrors m)
=> ModuleName
-> [DeclarationRef]
-> Declaration
-> Desugar m (Maybe DeclarationRef, [Declaration])
desugarDecl mn exps = go
where
go d@(TypeClassDeclaration sa name args implies deps members) = do
modify (M.insert (mn, name) (makeTypeClassData args (map memberToNameAndType members) implies deps False))
return (Nothing, d : typeClassDictionaryDeclaration sa name args implies members : map (typeClassMemberToDictionaryAccessor mn name args) members)
go (TypeInstanceDeclaration sa na chainId idx name deps className tys body) = do
name' <- desugarInstName name
let d = TypeInstanceDeclaration sa na chainId idx (Right name') deps className tys body
let explicitOrNot = case body of
DerivedInstance -> Left $ DerivedInstancePlaceholder className KnownClassStrategy
NewtypeInstance -> Left $ DerivedInstancePlaceholder className NewtypeStrategy
ExplicitInstance members -> Right members
dictDecl <- case explicitOrNot of
Right members
| className == C.Coercible ->
throwError . errorMessage' (fst sa) $ InvalidCoercibleInstanceDeclaration tys
| otherwise -> do
desugared <- desugarCases members
typeInstanceDictionaryDeclaration sa name' mn deps className tys desugared
Left dict ->
let
dictTy = foldl srcTypeApp (srcTypeConstructor (fmap (coerceProperName . dictTypeName) className)) tys
constrainedTy = quantify (foldr srcConstrainedType dictTy deps)
in
return $ ValueDecl sa name' Private [] [MkUnguarded (TypedValue True dict constrainedTy)]
return (expRef name' className tys, [d, dictDecl])
go other = return (Nothing, [other])
-- Completes the name generation for type class instances that do not have
-- a unique name defined in source code.
desugarInstName :: MonadSupply m => Either Text Ident -> Desugar m Ident
desugarInstName = either freshIdent pure
expRef :: Ident -> Qualified (ProperName 'ClassName) -> [SourceType] -> Maybe DeclarationRef
expRef name className tys
| isExportedClass className && all (all isExportedType . getConstructors) tys =
Just $ TypeInstanceRef genSpan name UserNamed
| otherwise = Nothing
isExportedClass :: Qualified (ProperName 'ClassName) -> Bool
isExportedClass = isExported (elem . TypeClassRef genSpan)
isExportedType :: Qualified (ProperName 'TypeName) -> Bool
isExportedType = isExported $ \pn -> isJust . find (matchesTypeRef pn)
isExported
:: (ProperName a -> [DeclarationRef] -> Bool)
-> Qualified (ProperName a)
-> Bool
isExported test (Qualified (ByModuleName mn') pn) = mn /= mn' || test pn exps
isExported _ _ = internalError "Names should have been qualified in name desugaring"
matchesTypeRef :: ProperName 'TypeName -> DeclarationRef -> Bool
matchesTypeRef pn (TypeRef _ pn' _) = pn == pn'
matchesTypeRef _ _ = False
getConstructors :: SourceType -> [Qualified (ProperName 'TypeName)]
getConstructors = everythingOnTypes (++) getConstructor
where
getConstructor (TypeConstructor _ tcname) = [tcname]
getConstructor _ = []
genSpan :: SourceSpan
genSpan = internalModuleSourceSpan "<generated>"
memberToNameAndType :: Declaration -> (Ident, SourceType)
memberToNameAndType (TypeDeclaration td) = unwrapTypeDeclaration td
memberToNameAndType _ = internalError "Invalid declaration in type class definition"
typeClassDictionaryDeclaration
:: SourceAnn
-> ProperName 'ClassName
-> [(Text, Maybe SourceType)]
-> [SourceConstraint]
-> [Declaration]
-> Declaration
typeClassDictionaryDeclaration sa name args implies members =
let superclassTypes = superClassDictionaryNames implies `zip`
[ function unit (foldl srcTypeApp (srcTypeConstructor (fmap (coerceProperName . dictTypeName) superclass)) tyArgs)
| (Constraint _ superclass _ tyArgs _) <- implies
]
members' = map (first runIdent . memberToNameAndType) members
mtys = members' ++ superclassTypes
toRowListItem (l, t) = srcRowListItem (Label $ mkString l) t
ctor = DataConstructorDeclaration sa (coerceProperName $ dictTypeName name)
[(Ident "dict", srcTypeApp tyRecord $ rowFromList (map toRowListItem mtys, srcREmpty))]
in DataDeclaration sa Newtype (coerceProperName $ dictTypeName name) args [ctor]
typeClassMemberToDictionaryAccessor
:: ModuleName
-> ProperName 'ClassName
-> [(Text, Maybe SourceType)]
-> Declaration
-> Declaration
typeClassMemberToDictionaryAccessor mn name args (TypeDeclaration (TypeDeclarationData sa@(ss, _) ident ty)) =
let className = Qualified (ByModuleName mn) name
dictIdent = Ident "dict"
dictObjIdent = Ident "v"
ctor = ConstructorBinder ss (coerceProperName . dictTypeName <$> className) [VarBinder ss dictObjIdent]
acsr = Accessor (mkString $ runIdent ident) (Var ss (Qualified ByNullSourcePos dictObjIdent))
visibility = second (const TypeVarVisible) <$> args
in ValueDecl sa ident Private []
[MkUnguarded (
TypedValue False (Abs (VarBinder ss dictIdent) (Case [Var ss $ Qualified ByNullSourcePos dictIdent] [CaseAlternative [ctor] [MkUnguarded acsr]])) $
addVisibility visibility (moveQuantifiersToFront NullSourceAnn (quantify (srcConstrainedType (srcConstraint className [] (map (srcTypeVar . fst) args) Nothing) ty)))
)]
typeClassMemberToDictionaryAccessor _ _ _ _ = internalError "Invalid declaration in type class definition"
unit :: SourceType
unit = srcTypeApp tyRecord srcREmpty
typeInstanceDictionaryDeclaration
:: forall m
. MonadError MultipleErrors m
=> SourceAnn
-> Ident
-> ModuleName
-> [SourceConstraint]
-> Qualified (ProperName 'ClassName)
-> [SourceType]
-> [Declaration]
-> Desugar m Declaration
typeInstanceDictionaryDeclaration sa@(ss, _) name mn deps className tys decls =
rethrow (addHint (ErrorInInstance className tys)) $ do
m <- get
-- Lookup the type arguments and member types for the type class
TypeClassData{..} <-
maybe (throwError . errorMessage' ss . UnknownName $ fmap TyClassName className) return $
M.lookup (qualify mn className) m
-- Replace the type arguments with the appropriate types in the member types
let memberTypes = map (second (replaceAllTypeVars (zip (map fst typeClassArguments) tys)) . tuple3To2) typeClassMembers
let declaredMembers = S.fromList $ mapMaybe declIdent decls
-- Instance declarations with a Fail constraint are unreachable code, so
-- we allow them to be empty.
let unreachable = any ((C.Fail ==) . constraintClass) deps && null decls
unless unreachable $
case filter (\(ident, _) -> not $ S.member ident declaredMembers) memberTypes of
hd : tl -> throwError . errorMessage' ss $ MissingClassMember (hd NEL.:| tl)
[] -> pure ()
-- Create values for the type instance members
members <- zip (map typeClassMemberName decls) <$> traverse (memberToValue memberTypes) decls
-- Create the type of the dictionary
-- The type is a record type, but depending on type instance dependencies, may be constrained.
-- The dictionary itself is a record literal (unless unreachable, in which case it's undefined).
superclassesDicts <- for typeClassSuperclasses $ \(Constraint _ superclass _ suTyArgs _) -> do
let tyArgs = map (replaceAllTypeVars (zip (map fst typeClassArguments) tys)) suTyArgs
pure $ Abs (VarBinder ss UnusedIdent) (DeferredDictionary superclass tyArgs)
let superclasses = superClassDictionaryNames typeClassSuperclasses `zip` superclassesDicts
let props = Literal ss $ ObjectLiteral $ map (first mkString) (members ++ superclasses)
dictTy = foldl srcTypeApp (srcTypeConstructor (fmap (coerceProperName . dictTypeName) className)) tys
constrainedTy = quantify (foldr srcConstrainedType dictTy deps)
dict = App (Constructor ss (fmap (coerceProperName . dictTypeName) className)) props
mkTV = if unreachable then TypedValue False (Var nullSourceSpan C.I_undefined) else TypedValue True dict
result = ValueDecl sa name Private [] [MkUnguarded (mkTV constrainedTy)]
return result
where
memberToValue :: [(Ident, SourceType)] -> Declaration -> Desugar m Expr
memberToValue tys' (ValueDecl (ss', _) ident _ [] [MkUnguarded val]) = do
_ <- maybe (throwError . errorMessage' ss' $ ExtraneousClassMember ident className) return $ lookup ident tys'
return val
memberToValue _ _ = internalError "Invalid declaration in type instance definition"
declIdent :: Declaration -> Maybe Ident
declIdent (ValueDeclaration vd) = Just (valdeclIdent vd)
declIdent (TypeDeclaration td) = Just (tydeclIdent td)
declIdent _ = Nothing
typeClassMemberName :: Declaration -> Text
typeClassMemberName = maybe (internalError "typeClassMemberName: Invalid declaration in type class definition") runIdent . declIdent
superClassDictionaryNames :: [Constraint a] -> [Text]
superClassDictionaryNames supers =
[ superclassName pn index
| (index, Constraint _ pn _ _ _) <- zip [0..] supers
]
tuple3To2 :: (a, b, c) -> (a, b)
tuple3To2 (a, b, _) = (a, b)