git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
Merge pull request #1138 from gbaz/deriving-generic-patch

Generic Deriving

commit396aa3b1935965796b52cf2f34f3e5f0173ccf4b authorPhil Freeman <paf31@cantab.net> dateTue 11 Aug 2015, 19:45 (11 years ago) parente698b6004616c7
6 files changed+201−1
purescript.cabal+1 src/Language/PureScript/AST/Declarations.hs+3 src/Language/PureScript/Constants.hs+14 src/Language/PureScript/Sugar/TypeClasses.hs+24−1 src/Language/PureScript/TypeChecker/Deriving.hs+154 src/Language/PureScript/TypeChecker/Types.hs+5
purescript.cabal+1
@@ -125,6 +125,7 @@ library
125125 Language.PureScript.Sugar.TypeDeclarations
126126 Language.PureScript.Traversals
127127 Language.PureScript.TypeChecker
128+ Language.PureScript.TypeChecker.Deriving
128129 Language.PureScript.TypeChecker.Entailment
129130 Language.PureScript.TypeChecker.Kinds
130131 Language.PureScript.TypeChecker.Monad
src/Language/PureScript/AST/Declarations.hs+3
@@ -376,6 +376,9 @@ data Expr
376376 --
377377 | SuperClassDictionary (Qualified ProperName) [Type]
378378 -- |
379+ -- A placeholder for a member function of a typeclass instance. To be elaborated during typechecking. (function name, class name, name of the type gaining the instance).
380+ | TypeClassInstanceMemberFunction Ident (Qualified ProperName) (Qualified ProperName)
381+ -- |
379382 -- A value with source position information
380383 --
381384 | PositionedValue SourceSpan [Comment] Expr deriving (Show, D.Data, D.Typeable)
src/Language/PureScript/Constants.hs+14
@@ -238,6 +238,20 @@ semigroupString = "semigroupString"
238238semigroupoidArr :: String
239239semigroupoidArr = "semigroupoidArr"
240240
241+-- Generic Deriving
242+
243+generic :: String
244+generic = "Generic"
245+
246+toSpine :: String
247+toSpine = "toSpine"
248+
249+fromSpine :: String
250+fromSpine = "fromSpine"
251+
252+toSignature :: String
253+toSignature = "toSignature"
254+
241255-- Main module
242256
243257main :: String
src/Language/PureScript/Sugar/TypeClasses.hs+24−1
@@ -229,8 +229,29 @@ typeClassMemberToDictionaryAccessor _ _ _ _ = error "Invalid declaration in type
229229unit :: Type
230230unit = TypeApp tyObject REmpty
231231
232+fillDerivingDecls :: (MonadError MultipleErrors m, Applicative m) => Qualified ProperName -> [Type] -> [Declaration] -> [Declaration] -> Desugar m [Declaration]
233+fillDerivingDecls className tys tyDecls decls = do
234+ let missingDecls = mapMaybe declName tyDecls \\ mapMaybe declName decls
235+ let mkMissingDecl cls fun =
236+ case tys of
237+ [TypeConstructor t] | unQualify className == ProperName cls && Ident fun `elem` missingDecls -> return [ValueDeclaration (Ident fun) Public [] (Right $ TypeClassInstanceMemberFunction (Ident fun) className t)]
238+ _ -> return []
239+ extraDecls <- concat <$> mapM (uncurry mkMissingDecl)
240+ [(C.generic,C.toSpine)
241+ ,(C.generic,C.fromSpine)
242+ ,(C.generic,C.toSignature)]
243+ return $ extraDecls ++ decls
244+ where
245+ declName :: Declaration -> Maybe Ident
246+ declName (PositionedDeclaration _ _ d) = declName d
247+ declName (ValueDeclaration ident _ _ _) = Just ident
248+ declName (TypeDeclaration ident _) = Just ident
249+ declName _ = Nothing
250+
251+ unQualify (Qualified _ a) = a
252+
232253typeInstanceDictionaryDeclaration :: (Functor m, Applicative m, MonadSupply m, MonadError MultipleErrors m) => Ident -> ModuleName -> [Constraint] -> Qualified ProperName -> [Type] -> [Declaration] -> Desugar m Declaration
233−typeInstanceDictionaryDeclaration name mn deps className tys decls =
254+typeInstanceDictionaryDeclaration name mn deps className tys decls1 =
234255 rethrow (onErrorMessages (ErrorInInstance className tys)) $ do
235256 m <- get
236257
@@ -239,6 +260,8 @@ typeInstanceDictionaryDeclaration name mn deps className tys decls =
239260 maybe (throwError . errorMessage $ UnknownTypeClass className) return $
240261 M.lookup (qualify mn className) m
241262
263+ decls <- fillDerivingDecls className tys tyDecls decls1
264+
242265 case mapMaybe declName tyDecls \\ mapMaybe declName decls of
243266 member : _ -> throwError . errorMessage $ MissingClassMember member
244267 [] -> do
src/Language/PureScript/TypeChecker/Deriving.hsnew file+154
@@ -0,0 +1,154 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Language.PureScript.TypeChecker.Deriving
4+-- Copyright : (c) Gershom Bazerman 2015
5+-- License : MIT (http://opensource.org/licenses/MIT)
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+-- This module implements the generic deriving elaboration that takes place during typechecking.
13+--
14+-----------------------------------------------------------------------------
15+
16+{-# LANGUAGE FlexibleContexts, MultiParamTypeClasses, OverloadedStrings, PatternGuards #-}
17+
18+module Language.PureScript.TypeChecker.Deriving (
19+ elaborateInstance
20+) where
21+
22+import Data.List
23+import Data.Maybe (fromMaybe)
24+import Data.Ord (comparing)
25+import qualified Data.Map as M
26+
27+import Control.Applicative
28+import Control.Monad.State
29+import Control.Monad.Error.Class (MonadError(..))
30+
31+import Language.PureScript.AST
32+import Language.PureScript.Environment
33+import Language.PureScript.Errors
34+import Language.PureScript.Names
35+import Language.PureScript.TypeChecker.Monad
36+import Language.PureScript.Types
37+import qualified Language.PureScript.Constants as C
38+
39+-- | Takes an expression, and if the head of the expression is a TypeClassInstanceMemberFunction, elaborates that into an instantiation of the actual member function via code generation.
40+elaborateInstance :: (Functor m, MonadState CheckState m, MonadError MultipleErrors m) => Expr -> m Expr
41+elaborateInstance (TypeClassInstanceMemberFunction funName className t) =
42+ fromMaybe noInstanceError $ lookup (unQualify className, funName) derivingList
43+ where noInstanceError = throwError $ MultipleErrors [ErrorInInstance className [TypeConstructor t] (SimpleErrorWrapper $ MissingClassMember funName)]
44+ derivingList = [((ProperName C.generic, Ident C.toSpine), mkSpineFunction t)
45+ ,((ProperName C.generic, Ident C.fromSpine), mkFromSpineFunction t)
46+ ,((ProperName C.generic, Ident C.toSignature), mkSignatureFunction t)]
47+elaborateInstance e = return e
48+
49+mkSpineFunction :: (Functor m, MonadState CheckState m) => Qualified ProperName -> m Expr
50+mkSpineFunction t = do
51+ ctors <- M.toList . dataConstructors <$> getEnv
52+ let prodConstructor = App (Constructor $ findName "SProd" ctors)
53+ recordConstructor = App (Constructor $ findName "SRecord" ctors)
54+
55+ mkCtorClause (ctorName, (_,_,typ,idents)) = CaseAlternative [ConstructorBinder ctorName (map VarBinder idents)] (Right caseResult)
56+ where caseResult = App (prodConstructor (StringLiteral . runProperName . unQualify $ ctorName)) . ArrayLiteral $ zipWith toSpineFun (map (Var . Qualified Nothing) idents) (argTypes typ)
57+
58+ toSpineFun :: Expr -> Type -> Expr
59+ toSpineFun i r | Just rec <- objectType r =
60+ lamNull . recordConstructor . ArrayLiteral .
61+ map (\(str,typ) -> ObjectLiteral [("recLabel", StringLiteral str), ("recValue", toSpineFun (Accessor str i) typ)])
62+ $ decomposeRec rec
63+ toSpineFun i _ = lamNull $ App (mkGenVar C.toSpine) i
64+
65+ return . lamCase "$x" . map mkCtorClause . constructorsForType t $ ctors
66+
67+mkSignatureFunction :: (Functor m, MonadState CheckState m) => Qualified ProperName -> m Expr
68+mkSignatureFunction t = do
69+ ctors <- M.toList . dataConstructors <$> getEnv
70+ envtyps <- M.toList . types <$> getEnv
71+ let mkSigProd = App (Constructor $ findName "SigProd" ctors) . ArrayLiteral
72+ mkSigRec = App (Constructor $ findName "SigRecord" ctors) . ArrayLiteral
73+
74+ mkProdClause (ctorName, (_,_,typ,_)) = ObjectLiteral [("sigConstructor",StringLiteral (runProperName . unQualify $ ctorName)),("sigValues", ArrayLiteral . map mkProductSignature . argTypes $ typ)]
75+
76+ mkProductSignature r | Just rec <- objectType r =
77+ lamNull . mkSigRec .
78+ map (\(str,typ) -> ObjectLiteral [("recLabel", StringLiteral str), ("recValue", mkProductSignature typ)])
79+ $ decomposeRec rec
80+ mkProductSignature typ = lamNull $ App (mkGenVar C.toSignature)
81+ (TypedValue False (mkGenVar "anyProxy") (TypeApp (TypeConstructor $ findName "Proxy" envtyps) typ))
82+
83+ return . lamNull . mkSigProd . map mkProdClause . constructorsForType t $ ctors
84+
85+mkFromSpineFunction :: (Functor m, MonadState CheckState m) => Qualified ProperName -> m Expr
86+mkFromSpineFunction t = do
87+ ctors <- M.toList . dataConstructors <$> getEnv
88+ let
89+ mkJust = App (Constructor $ findName "Just" ctors)
90+ mkNothing = Constructor $ findName "Nothing" ctors
91+
92+ mkAlternative (ctor@(Qualified _ (ProperName ctorName)), (_,_,typ,idents)) =
93+ CaseAlternative [ConstructorBinder (findName "SProd" ctors) [StringBinder ctorName, ArrayBinder (map VarBinder idents)]]
94+ . Right $ liftApplicative (mkJust $ Constructor ctor) (zipWith fromSpineFun (map (Var . (Qualified Nothing))idents) (argTypes typ))
95+
96+ fromSpineFun e r | Just rec <- objectType r = App (lamCase "r" [mkRecCase (decomposeRec rec), CaseAlternative [NullBinder] (Right mkNothing)]) (App e (mkPrelVar "unit"))
97+
98+ fromSpineFun e _ = App (mkGenVar C.fromSpine) (App e (mkPrelVar "unit"))
99+
100+ mkRecCase rs = CaseAlternative [ConstructorBinder (findName "SRecord" ctors) [ArrayBinder (map (VarBinder . Ident . fst) rs)]] . Right $ liftApplicative (mkRecFun rs) (map (\(x,y) -> fromSpineFun (Accessor "recValue" (mkVar x)) y) rs)
101+
102+ mkRecFun :: [(String,Type)] -> Expr
103+ mkRecFun xs = mkJust $ foldr (\s e -> lam s e) recLiteral (map fst xs)
104+ where recLiteral = ObjectLiteral $ map (\(s,_) -> (s,mkVar s)) xs
105+
106+ return . lamCase "$x" $ map mkAlternative (constructorsForType t ctors) ++ [CaseAlternative [NullBinder] (Right mkNothing)]
107+
108+-- Helpers
109+
110+objectType :: Type -> Maybe Type
111+objectType (TypeApp (TypeConstructor (Qualified (Just (ModuleName [ProperName "Prim"])) (ProperName "Object"))) rec) = Just rec
112+objectType _ = Nothing
113+
114+lam :: String -> Expr -> Expr
115+lam s = Abs (Left (Ident s))
116+
117+lamNull :: Expr -> Expr
118+lamNull = lam "$q"
119+
120+lamCase :: String -> [CaseAlternative] -> Expr
121+lamCase s = lam s . Case [mkVar s]
122+
123+liftApplicative :: Expr -> [Expr] -> Expr
124+liftApplicative = foldl' (\x e -> App (App (mkPrelVar "apply") x) e)
125+
126+mkVarMn :: Maybe ModuleName -> String -> Expr
127+mkVarMn mn s = Var (Qualified mn (Ident s))
128+
129+mkVar :: String -> Expr
130+mkVar s = mkVarMn Nothing s
131+
132+mkPrelVar :: String -> Expr
133+mkPrelVar s = mkVarMn (Just (ModuleName [ProperName C.prelude])) s
134+
135+mkGenVar :: String -> Expr
136+mkGenVar s = mkVarMn (Just (ModuleName [ProperName "Data", ProperName C.generic])) s
137+
138+decomposeRec :: Type -> [(String, Type)]
139+decomposeRec = sortBy (comparing fst) . go
140+ where go (RCons str typ typs) = (str, typ) : decomposeRec typs
141+ go _ = []
142+
143+argTypes :: Type -> [Type]
144+argTypes (TypeApp (TypeApp (TypeConstructor (Qualified (Just (ModuleName [ProperName "Prim"])) (ProperName "Function"))) a) b) = a : argTypes b
145+argTypes _ = []
146+
147+findName :: String -> [(Qualified ProperName, a)] -> Qualified ProperName
148+findName s lst = fromMaybe (Qualified Nothing (ProperName s)) . find ((== ProperName s) . unQualify) . map fst $ lst
149+
150+unQualify :: Qualified a -> a
151+unQualify (Qualified _ x) = x
152+
153+constructorsForType :: Qualified ProperName -> [(Qualified ProperName, (a, ProperName, b, c))] -> [(Qualified ProperName, (a, ProperName, b, c))]
154+constructorsForType t = filter (\(Qualified mn _ ,(_,typeName,_,_)) -> t == Qualified mn typeName)
src/Language/PureScript/TypeChecker/Types.hs+5
@@ -52,6 +52,7 @@ import Language.PureScript.Errors
5252import Language.PureScript.Kinds
5353import Language.PureScript.Names
5454import Language.PureScript.Traversals
55+import Language.PureScript.TypeChecker.Deriving
5556import Language.PureScript.TypeChecker.Entailment
5657import Language.PureScript.TypeChecker.Kinds
5758import Language.PureScript.TypeChecker.Monad
@@ -559,6 +560,9 @@ check' (Constructor c) ty = do
559560 repl <- introduceSkolemScope <=< replaceAllTypeSynonyms $ ty1
560561 _ <- subsumes Nothing repl ty
561562 return $ TypedValue True (Constructor c) ty
563+
564+check' e@TypeClassInstanceMemberFunction{} ty = flip check' ty =<< elaborateInstance e
565+
562566check' (Let ds val) ty = do
563567 (ds', val') <- inferLetBinding [] ds val (`check` ty)
564568 return $ TypedValue True (Let ds' val') ty
@@ -578,6 +582,7 @@ containsTypeSynonyms = everythingOnTypes (||) go where
578582 go (SaturatedTypeSynonym _ _) = True
579583 go _ = False
580584
585+
581586-- |
582587-- Check the type of a collection of named record fields
583588--