Skip to content

Commit 249ffef

Browse files
Add attached derive clauses to data and newtype declarations
Adds Haskell-style derive clauses directly on data/newtype declarations: data Color = Red | Green | Blue derive (Eq, Ord) newtype Name = Name String derive newtype (Eq, Show) Multiple classes per clause, multiple clauses per type, and free mixing with standalone derive instance syntax. Automatically infers the correct type application for higher-kinded classes like Functor.
1 parent c4a35b3 commit 249ffef

14 files changed

Lines changed: 280 additions & 18 deletions

File tree

src/Language/PureScript/AST/Declarations.hs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,11 @@ data Declaration
445445
-- declaration, while the second @SourceAnn@ serves as the
446446
-- annotation for the type class and its arguments.
447447
| TypeInstanceDeclaration SourceAnn SourceAnn ChainId Integer (Either Text Ident) [SourceConstraint] (Qualified (ProperName 'ClassName)) [SourceType] TypeInstanceBody
448+
-- |
449+
-- A derive clause attached to a data or newtype declaration
450+
-- (annotation, type name, type vars, class name, extra type args)
451+
--
452+
| DeriveClauseDeclaration SourceAnn (ProperName 'TypeName) [(Text, Maybe SourceType)] (Qualified (ProperName 'ClassName)) [SourceType]
448453
deriving (Show, Generic, NFData)
449454

450455
data ValueFixity = ValueFixity Fixity (Qualified (Either Ident (ProperName 'ConstructorName))) (OpName 'ValueOpName)
@@ -506,6 +511,7 @@ declSourceAnn (FixityDeclaration sa _) = sa
506511
declSourceAnn (ImportDeclaration sa _ _ _) = sa
507512
declSourceAnn (TypeClassDeclaration sa _ _ _ _ _) = sa
508513
declSourceAnn (TypeInstanceDeclaration sa _ _ _ _ _ _ _ _) = sa
514+
declSourceAnn (DeriveClauseDeclaration sa _ _ _ _) = sa
509515

510516
declSourceSpan :: Declaration -> SourceSpan
511517
declSourceSpan = fst . declSourceAnn
@@ -530,6 +536,7 @@ declName DataBindingGroupDeclaration{} = Nothing
530536
declName BoundValueDeclaration{} = Nothing
531537
declName KindDeclaration{} = Nothing
532538
declName TypeDeclaration{} = Nothing
539+
declName DeriveClauseDeclaration{} = Nothing
533540

534541
-- |
535542
-- Test if a declaration is a value declaration

src/Language/PureScript/CST/Convert.hs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ convertBinder fileName = go
445445

446446
convertDeclaration :: String -> Declaration a -> [AST.Declaration]
447447
convertDeclaration fileName decl = case decl of
448-
DeclData _ (DataHead _ a vars) bd -> do
448+
DeclData _ (DataHead _ a vars) bd drvs -> do
449449
let
450450
ctrs :: SourceToken -> DataCtor b -> [(SourceToken, DataCtor b)] -> [AST.DataConstructorDeclaration]
451451
ctrs st (DataCtor _ name fields) tl
@@ -454,15 +454,17 @@ convertDeclaration fileName decl = case decl of
454454
[] -> []
455455
(st', ctor) : tl' -> ctrs st' ctor tl'
456456
)
457-
pure $ AST.DataDeclaration ann Env.Data (nameValue a) (goTypeVar <$> vars) (maybe [] (\(st, Separated hd tl) -> ctrs st hd tl) bd)
457+
AST.DataDeclaration ann Env.Data (nameValue a) (goTypeVar <$> vars) (maybe [] (\(st, Separated hd tl) -> ctrs st hd tl) bd)
458+
: convertDeriveClauses fileName (nameValue a) (goTypeVar <$> vars) drvs
458459
DeclType _ (DataHead _ a vars) _ bd ->
459460
pure $ AST.TypeSynonymDeclaration ann
460461
(nameValue a)
461462
(goTypeVar <$> vars)
462463
(convertType fileName bd)
463-
DeclNewtype _ (DataHead _ a vars) st x ys -> do
464+
DeclNewtype _ (DataHead _ a vars) st x ys drvs -> do
464465
let ctrs = [AST.DataConstructorDeclaration (sourceAnnCommented fileName st (snd $ declRange decl)) (nameValue x) [(headDef (internalError "No constructor name") ctrFields, convertType fileName ys)]]
465-
pure $ AST.DataDeclaration ann Env.Newtype (nameValue a) (goTypeVar <$> vars) ctrs
466+
AST.DataDeclaration ann Env.Newtype (nameValue a) (goTypeVar <$> vars) ctrs
467+
: convertDeriveClauses fileName (nameValue a) (goTypeVar <$> vars) drvs
466468
DeclClass _ (ClassHead _ sup name vars fdeps) bd -> do
467469
let
468470
goTyVar (TypeVarKinded (Wrapped _ (Labeled (_, a) _ _) _)) = nameValue a
@@ -619,6 +621,29 @@ convertDeclaration fileName decl = case decl of
619621
else
620622
(fst $ qualRange cls, snd $ typeRange $ last args)
621623

624+
convertDeriveClauses
625+
:: String
626+
-> N.ProperName 'N.TypeName
627+
-> [(Text.Text, Maybe T.SourceType)]
628+
-> [DeriveClause a]
629+
-> [AST.Declaration]
630+
convertDeriveClauses fileName tyName tyVars = concatMap convertClause
631+
where
632+
convertClause (DeriveClause _ _ (Wrapped _ classes _)) =
633+
map convertDeriveClass (toList classes)
634+
635+
convertDeriveClass :: DeriveClass a -> AST.Declaration
636+
convertDeriveClass (DeriveClass _ cls extraArgs) = do
637+
let
638+
clsStart = fst $ qualRange cls
639+
clsEnd = case extraArgs of
640+
[] -> snd $ qualRange cls
641+
_ -> snd $ typeRange $ last extraArgs
642+
clsAnn = sourceAnnCommented fileName clsStart clsEnd
643+
AST.DeriveClauseDeclaration clsAnn tyName tyVars
644+
(qualified cls)
645+
(convertType fileName <$> extraArgs)
646+
622647
convertSignature :: String -> Labeled (Name Ident) (Type a) -> AST.Declaration
623648
convertSignature fileName (Labeled a _ b) = do
624649
let

src/Language/PureScript/CST/Flatten.hs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,13 +203,23 @@ flattenForeign = \case
203203
flattenRole :: Role -> DList SourceToken
204204
flattenRole = pure . roleTok
205205

206+
flattenDeriveClass :: DeriveClass a -> DList SourceToken
207+
flattenDeriveClass (DeriveClass _ cls tys) =
208+
flattenQualifiedName cls <> foldMap flattenType tys
209+
210+
flattenDeriveClause :: DeriveClause a -> DList SourceToken
211+
flattenDeriveClause (DeriveClause _ kw classes) =
212+
pure kw <>
213+
flattenWrapped (flattenSeparated flattenDeriveClass) classes
214+
206215
flattenDeclaration :: Declaration a -> DList SourceToken
207216
flattenDeclaration = \case
208-
DeclData _ a b ->
217+
DeclData _ a b drvs ->
209218
flattenDataHead a <>
210-
foldMap (\(t, cs) -> pure t <> flattenSeparated flattenDataCtor cs) b
211-
DeclType _ a b c ->flattenDataHead a <> pure b <> flattenType c
212-
DeclNewtype _ a b c d -> flattenDataHead a <> pure b <> flattenName c <> flattenType d
219+
foldMap (\(t, ctrs) -> pure t <> flattenSeparated flattenDataCtor ctrs) b <>
220+
foldMap flattenDeriveClause drvs
221+
DeclType _ a b c -> flattenDataHead a <> pure b <> flattenType c
222+
DeclNewtype _ a b c d drvs -> flattenDataHead a <> pure b <> flattenName c <> flattenType d <> foldMap flattenDeriveClause drvs
213223
DeclClass _ a b ->
214224
flattenClassHead a <>
215225
foldMap (\(c, d) -> pure c <> foldMap (flattenLabeled flattenName flattenType) d) b

src/Language/PureScript/CST/Parser.y

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -661,10 +661,10 @@ import :: { Import () }
661661
| 'class' properName { ImportClass () $1 (getProperName $2) }
662662

663663
decl :: { Declaration () }
664-
: dataHead { DeclData () $1 Nothing }
665-
| dataHead '=' sep(dataCtor, '|') { DeclData () $1 (Just ($2, $3)) }
664+
: dataHead manyOrEmpty(deriveClause) { DeclData () $1 Nothing $2 }
665+
| dataHead '=' sep(dataCtor, '|') manyOrEmpty(deriveClause) { DeclData () $1 (Just ($2, $3)) $4 }
666666
| typeHead '=' type {% checkNoWildcards $3 *> pure (DeclType () $1 $2 $3) }
667-
| newtypeHead '=' properName typeAtom {% checkNoWildcards $4 *> pure (DeclNewtype () $1 $2 (getProperName $3) $4) }
667+
| newtypeHead '=' properName typeAtom manyOrEmpty(deriveClause) {% checkNoWildcards $4 *> pure (DeclNewtype () $1 $2 (getProperName $3) $4 $5) }
668668
| classHead { either id (\h -> DeclClass () h Nothing) $1 }
669669
| classHead 'where' '\{' manySep(classMember, '\;') '\}' {% either (const (parseError $2)) (\h -> pure $ DeclClass () h (Just ($2, $4))) $1 }
670670
| instHead { DeclInstanceChain () (Separated (Instance $1 Nothing) []) }
@@ -681,6 +681,12 @@ decl :: { Declaration () }
681681
| 'foreign' 'import' 'data' properName '::' type { DeclForeign () $1 $2 (ForeignData $3 (Labeled (getProperName $4) $5 $6)) }
682682
| 'type' 'role' properName many(role) { DeclRole () $1 $2 (getProperName $3) $4 }
683683

684+
deriveClause :: { DeriveClause () }
685+
: 'derive' '(' sep(deriveClass, ',') ')' { DeriveClause () $1 (Wrapped $2 $3 $4) }
686+
687+
deriveClass :: { DeriveClass () }
688+
: qualProperName manyOrEmpty(typeAtom) { DeriveClass () (getQualifiedProperName $1) $2 }
689+
684690
dataHead :: { DataHead () }
685691
: 'data' properName manyOrEmpty(typeVarBindingPlain) { DataHead $1 (getProperName $2) $3 }
686692

src/Language/PureScript/CST/Positions.hs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,21 @@ dataMembersRange = \case
157157
DataAll _ a -> (a, a)
158158
DataEnumerated _ (Wrapped a _ b) -> (a, b)
159159

160+
deriveClauseRange :: DeriveClause a -> TokenRange
161+
deriveClauseRange (DeriveClause _ kw classes) = (kw, wrpClose classes)
162+
160163
declRange :: Declaration a -> TokenRange
161164
declRange = \case
162-
DeclData _ hd ctors
165+
DeclData _ hd ctors drvs
166+
| _:_ <- drvs -> (fst start, snd . deriveClauseRange $ last drvs)
163167
| Just (_, cs) <- ctors -> (fst start, snd . dataCtorRange $ sepLast cs)
164168
| otherwise -> start
165169
where start = dataHeadRange hd
166170
DeclType _ a _ b -> (fst $ dataHeadRange a, snd $ typeRange b)
167-
DeclNewtype _ a _ _ b -> (fst $ dataHeadRange a, snd $ typeRange b)
171+
DeclNewtype _ a _ _ b drvs
172+
| _:_ <- drvs -> (fst start, snd . deriveClauseRange $ last drvs)
173+
| otherwise -> start
174+
where start = (fst $ dataHeadRange a, snd $ typeRange b)
168175
DeclClass _ hd body
169176
| Just (_, ts) <- body -> (fst start, snd . typeRange . lblValue $ NE.last ts)
170177
| otherwise -> start

src/Language/PureScript/CST/Types.hs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,22 @@ data DataMembers a
194194
| DataEnumerated a (Delimited (Name (N.ProperName 'N.ConstructorName)))
195195
deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic)
196196

197+
data DeriveClass a = DeriveClass
198+
{ dcAnn :: a
199+
, dcClass :: QualifiedName (N.ProperName 'N.ClassName)
200+
, dcTypes :: [Type a]
201+
} deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic)
202+
203+
data DeriveClause a = DeriveClause
204+
{ dclAnn :: a
205+
, dclKeyword :: SourceToken
206+
, dclClasses :: Wrapped (Separated (DeriveClass a))
207+
} deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Generic)
208+
197209
data Declaration a
198-
= DeclData a (DataHead a) (Maybe (SourceToken, Separated (DataCtor a)))
210+
= DeclData a (DataHead a) (Maybe (SourceToken, Separated (DataCtor a))) [DeriveClause a]
199211
| DeclType a (DataHead a) SourceToken (Type a)
200-
| DeclNewtype a (DataHead a) SourceToken (Name (N.ProperName 'N.ConstructorName)) (Type a)
212+
| DeclNewtype a (DataHead a) SourceToken (Name (N.ProperName 'N.ConstructorName)) (Type a) [DeriveClause a]
201213
| DeclClass a (ClassHead a) (Maybe (SourceToken, NonEmpty (Labeled (Name Ident) (Type a))))
202214
| DeclInstanceChain a (Separated (Instance a))
203215
| DeclDerive a SourceToken (Maybe SourceToken) (InstanceHead a)

src/Language/PureScript/Sugar/Names.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,11 @@ renameInModule imports (Module modSS coms mn decls exps) =
242242
ValueFixityDeclaration sa fixity . fmap Right
243243
<$> updateDataConstructorName (Qualified mn' alias) ss
244244
<*> pure op
245+
updateDecl bound (DeriveClauseDeclaration sa@(ss, _) tyName tyVars className extraArgs) =
246+
fmap (bound,) $
247+
DeriveClauseDeclaration sa tyName tyVars
248+
<$> updateClassName className ss
249+
<*> traverse updateTypesEverywhere extraArgs
245250
updateDecl b d =
246251
return (b, d)
247252

src/Language/PureScript/Sugar/Operators.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,10 @@ updateTypes goType = (goDecl, goExpr, goBinder)
394394
KindDeclaration sa sigFor name <$> goType' ss ty
395395
goDecl (ExternDataDeclaration sa@(ss, _) name ty) =
396396
ExternDataDeclaration sa name <$> goType' ss ty
397+
goDecl (DeriveClauseDeclaration sa@(ss, _) tyName tyVars className extraArgs) = do
398+
tyVars' <- traverse (traverse (traverse (goType' ss))) tyVars
399+
extraArgs' <- traverse (goType' ss) extraArgs
400+
return $ DeriveClauseDeclaration sa tyName tyVars' className extraArgs'
397401
goDecl other =
398402
return other
399403

src/Language/PureScript/Sugar/TypeClasses.hs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,18 @@ import Data.Map qualified as M
2222
import Data.Maybe (catMaybes, mapMaybe, isJust)
2323
import Data.List.NonEmpty qualified as NEL
2424
import Data.Set qualified as S
25+
import Data.Char qualified
2526
import Data.Text (Text)
27+
import Data.Text qualified
2628
import Data.Traversable (for)
2729
import Language.PureScript.Constants.Prim qualified as C
30+
import Language.PureScript.AST.Declarations.ChainId (mkChainId)
2831
import Language.PureScript.Crash (internalError)
2932
import Language.PureScript.Environment (DataDeclType(..), NameKind(..), TypeClassData(..), dictTypeName, function, makeTypeClassData, primClasses, primCoerceClasses, primIntClasses, primRowClasses, primRowListClasses, primSymbolClasses, primTypeErrorClasses, tyRecord)
3033
import Language.PureScript.Errors hiding (isExported, nonEmpty)
3134
import Language.PureScript.Externs (ExternsDeclaration(..), ExternsFile(..))
3235
import Language.PureScript.Label (Label(..))
33-
import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), ModuleName, Name(..), ProperName, ProperNameType(..), Qualified(..), QualifiedBy(..), coerceProperName, freshIdent, qualify, runIdent)
36+
import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), ModuleName, Name(..), ProperName, ProperNameType(..), Qualified(..), QualifiedBy(..), coerceProperName, freshIdent, qualify, runIdent, runProperName)
3437
import Language.PureScript.PSString (mkString)
3538
import Language.PureScript.Sugar.CaseDeclarations (desugarCases)
3639
import Language.PureScript.TypeClassDictionaries (superclassName)
@@ -228,6 +231,18 @@ desugarDecl mn exps = go
228231
in
229232
return $ ValueDecl sa name' Private [] [MkUnguarded (TypedValue True dict constrainedTy)]
230233
return (expRef name' className tys, [d, dictDecl])
234+
go (DeriveClauseDeclaration sa tyName tyVars className extraArgs) = do
235+
memberMap <- get
236+
let
237+
classKey = case className of
238+
Qualified (ByModuleName clsMn) cn -> (clsMn, cn)
239+
_ -> (mn, let Qualified _ cn = className in cn)
240+
mClassData = M.lookup classKey memberMap
241+
ss = fst sa
242+
chainId = mkChainId (spanName ss) (spanStart ss)
243+
genName = mkDeriveClauseName className
244+
tyConArgs <- computeInstArgs ss mn tyName tyVars mClassData className extraArgs
245+
go $ TypeInstanceDeclaration sa sa chainId 0 (Left genName) [] className tyConArgs DerivedInstance
231246
go other = return (Nothing, [other])
232247

233248
-- Completes the name generation for type class instances that do not have
@@ -267,6 +282,58 @@ desugarDecl mn exps = go
267282
genSpan :: SourceSpan
268283
genSpan = internalModuleSourceSpan "<generated>"
269284

285+
-- | Compute the full instance type arguments for a derive clause.
286+
--
287+
-- With explicit args containing a wildcard (_), the wildcard is replaced
288+
-- with the bare type constructor:
289+
-- derive (Functor _) => Functor MyType
290+
--
291+
-- Without explicit args, the kind of the class's first parameter
292+
-- determines how many type variables to apply. Generic and Newtype
293+
-- additionally get a wildcard for their computed representation type.
294+
computeInstArgs
295+
:: MonadError MultipleErrors m
296+
=> SourceSpan
297+
-> ModuleName
298+
-> ProperName 'TypeName
299+
-> [(Text, Maybe SourceType)]
300+
-> Maybe TypeClassData
301+
-> Qualified (ProperName 'ClassName)
302+
-> [SourceType]
303+
-> m [SourceType]
304+
computeInstArgs ss mn tyName tyVars mClassData className extraArgs
305+
| _:_ <- extraArgs = pure $ map substWildcard extraArgs
306+
| otherwise = do
307+
let firstParamKind = do
308+
tcd <- mClassData
309+
(_, k) : _ <- pure (typeClassArguments tcd)
310+
k
311+
kindArity <- case firstParamKind of
312+
Just k -> pure $ kindArrowCount k
313+
Nothing -> throwError . errorMessage' ss $ CannotDerive className []
314+
let numVarsToApply = max 0 (length tyVars - kindArity)
315+
pure [foldl srcTypeApp tyCon (take numVarsToApply tyVarTypes)]
316+
where
317+
tyCon = srcTypeConstructor (Qualified (ByModuleName mn) tyName)
318+
tyVarTypes = map (\(v, _) -> srcTypeVar v) tyVars
319+
substWildcard (TypeWildcard _ _) = tyCon
320+
substWildcard t = t
321+
322+
-- | Count the number of arrows in a kind.
323+
-- Type returns 0, Type -> Type returns 1, etc.
324+
kindArrowCount :: SourceType -> Int
325+
kindArrowCount (TypeApp _ (TypeApp _ (TypeConstructor _ f) _) rest)
326+
| f == C.Function = 1 + kindArrowCount rest
327+
kindArrowCount _ = 0
328+
329+
-- | Generate a name for a derive clause instance
330+
mkDeriveClauseName :: Qualified (ProperName 'ClassName) -> Text
331+
mkDeriveClauseName (Qualified _ cn) = Data.Char.toLower c `Data.Text.cons` cs
332+
where
333+
(c, cs) = case Data.Text.uncons (runProperName cn) of
334+
Just pair -> pair
335+
Nothing -> internalError "Empty class name"
336+
270337
memberToNameAndType :: Declaration -> (Ident, SourceType)
271338
memberToNameAndType (TypeDeclaration td) = unwrapTypeDeclaration td
272339
memberToNameAndType _ = internalError "Invalid declaration in type class definition"

src/Language/PureScript/Sugar/TypeClasses/Deriving.hs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ import Control.Monad.Error.Class (MonadError(..))
88
import Control.Monad.Supply.Class (MonadSupply)
99
import Data.List (foldl', find, unzip5)
1010
import Language.PureScript.AST (Binder(..), CaseAlternative(..), DataConstructorDeclaration(..), Declaration(..), Expr(..), pattern MkUnguarded, Module(..), SourceSpan(..), TypeInstanceBody(..), pattern ValueDecl)
11+
import Language.PureScript.AST.Declarations.ChainId (mkChainId)
1112
import Language.PureScript.AST.Utils (UnwrappedTypeConstructor(..), lamCase, unguarded, unwrapTypeConstructor)
1213
import Language.PureScript.Constants.Libs qualified as Libs
1314
import Language.PureScript.Crash (internalError)
1415
import Language.PureScript.Environment (DataDeclType(..), NameKind(..))
1516
import Language.PureScript.Errors (MultipleErrors, SimpleErrorMessage(..), errorMessage')
16-
import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), ModuleName, ProperName(..), ProperNameType(..), Qualified(..), QualifiedBy(..), freshIdent)
17+
import Language.PureScript.Names (pattern ByNullSourcePos, Ident(..), ModuleName, ProperName(..), ProperNameType(..), Qualified(..), QualifiedBy(..), freshIdent, runIdent)
1718
import Language.PureScript.PSString (mkString)
18-
import Language.PureScript.Types (SourceType, Type(..), WildcardData(..), replaceAllTypeVars, srcTypeApp, srcTypeConstructor, srcTypeLevelString)
19+
import Language.PureScript.Types (SourceType, Type(..), WildcardData(..), replaceAllTypeVars, srcTypeApp, srcTypeConstructor, srcTypeLevelString, srcTypeVar)
1920
import Language.PureScript.TypeChecker (checkNewtype)
2021

2122
-- | Elaborates deriving instance declarations by code generation.
@@ -61,6 +62,15 @@ deriveInstance mn ds decl =
6162
Libs.Generic -> binaryWildcardClass (deriveGenericRep ss mn)
6263
Libs.Newtype -> binaryWildcardClass deriveNewtype
6364
_ -> pure decl
65+
DeriveClauseDeclaration sa@(ss, _) tyName tyVars className _
66+
| className == Libs.Generic || className == Libs.Newtype -> do
67+
let tyCon = srcTypeConstructor (Qualified (ByModuleName mn) tyName)
68+
tyVarTypes = map (\(v, _) -> srcTypeVar v) tyVars
69+
fullyApplied = foldl srcTypeApp tyCon tyVarTypes
70+
tys = [fullyApplied, TypeWildcard sa UnnamedWildcard]
71+
chainId = mkChainId (spanName ss) (spanStart ss)
72+
nm <- Left . runIdent <$> freshIdent "deriveClause"
73+
deriveInstance mn ds $ TypeInstanceDeclaration sa sa chainId 0 nm [] className tys DerivedInstance
6474
_ -> pure decl
6575

6676
deriveGenericRep

0 commit comments

Comments
 (0)