@@ -0,0 +1,150 @@
1+-----------------------------------------------------------------------------
3+-- Module : Language.PureScript.TypeChecker.Deriving
4+-- Copyright : (c) Gershom Bazerman 2015
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
12+-- This module implements the generic deriving elaboration that takes place during typechecking.
14+-----------------------------------------------------------------------------
16+{-# LANGUAGE FlexibleContexts, MultiParamTypeClasses, OverloadedStrings, PatternSynonyms #-}
18+module Language.PureScript.TypeChecker.Deriving (
23+import Data.Maybe (fromMaybe)
24+import Data.Ord (comparing)
25+import qualified Data.Map as M
27+import Control.Applicative
28+import Control.Monad.State
29+import Control.Monad.Except
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
38+elaborateInstance :: (MonadState CheckState m, MonadError MultipleErrors m) => Expr -> m Expr
39+elaborateInstance (TypeClassInstanceMemberFunction funName className t) =
40+ fromMaybe noInstanceError $ lookup (unQualify className, funName) derivingList
41+ where noInstanceError = throwError $ MultipleErrors [ErrorInInstance className [TypeConstructor t] (SimpleErrorWrapper $ MissingClassMember funName)]
42+ derivingList = [((ProperName "Generic", Ident "toSpine"), mkSpineFunction t)
43+ ,((ProperName "Generic", Ident "fromSpine"), mkFromSpineFunction t)
44+ ,((ProperName "Generic", Ident "toSignature"), mkSignatureFunction t)]
45+elaborateInstance e = return e
47+mkSpineFunction :: MonadState CheckState m => Qualified ProperName -> m Expr
48+mkSpineFunction t = do
49+ ctors <- M.toList . dataConstructors <$> getEnv
50+ tcs <- M.toList . typeClasses <$> getEnv
51+ let prodConstructor = App (Constructor $ findName "SProd" ctors)
52+ recordConstructor = App (Constructor $ findName "SRecord" ctors)
53+ genericModule = case findName "Generic" tcs of (Qualified mn _) -> mn
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)
58+ toSpineFun :: Expr -> Type -> Expr
59+ toSpineFun i (ObjectType rec) =
60+ lam "q" . recordConstructor . ArrayLiteral .
61+ map (\(str,typ) -> ObjectLiteral [("recLabel", StringLiteral str), ("recValue", toSpineFun (Accessor str i) typ)])
63+ toSpineFun i _ = lam "q" $ App (mkVarMn genericModule "toSpine") i
65+ return . lamCase "x" . map mkCtorClause . constructorsForType t $ ctors
67+mkSignatureFunction :: MonadState CheckState m => Qualified ProperName -> m Expr
68+mkSignatureFunction t = do
69+ ctors <- M.toList . dataConstructors <$> getEnv
70+ envtyps <- M.toList . types <$> getEnv
71+ tcs <- M.toList . typeClasses <$> getEnv
72+ let mkSigProd = App (Constructor $ findName "SigProd" ctors) . ArrayLiteral
73+ mkSigRec = App (Constructor $ findName "SigRecord" ctors) . ArrayLiteral
74+ genericModule = case findName "Generic" tcs of (Qualified mn _) -> mn
76+ mkProdClause (ctorName, (_,_,typ,_)) = ObjectLiteral [("sigConstructor",StringLiteral (runProperName . unQualify $ ctorName)),("sigValues", ArrayLiteral . map mkProductSignature . argTypes $ typ)]
78+ mkProductSignature (ObjectType rec) =
80+ map (\(str,typ) -> ObjectLiteral [("recLabel", StringLiteral str), ("recValue", mkProductSignature typ)])
82+ mkProductSignature typ = lam "q" $ App (mkVarMn genericModule "toSignature")
83+ (TypedValue False (mkVarMn genericModule "anyProxy") (TypeApp (TypeConstructor $ findName "Proxy" envtyps) typ))
85+ return . lam "x" . mkSigProd . map mkProdClause . constructorsForType t $ ctors
87+mkFromSpineFunction :: MonadState CheckState m => Qualified ProperName -> m Expr
88+mkFromSpineFunction t = do
89+ ctors <- M.toList . dataConstructors <$> getEnv
90+ tcs <- M.toList . typeClasses <$> getEnv
92+ mkJust = App (Constructor $ findName "Just" ctors)
93+ mkNothing = Constructor $ findName "Nothing" ctors
94+ genericModule = case findName "Generic" tcs of (Qualified mn _) -> mn
96+ mkAlternative (ctor@(Qualified _ (ProperName ctorName)), (_,_,typ,idents)) =
97+ CaseAlternative [ConstructorBinder (findName "SProd" ctors) [StringBinder ctorName, ArrayBinder (map VarBinder idents)]]
98+ . Right $ liftApplicative (mkJust $ Constructor ctor) (zipWith fromSpineFun (map (Var . (Qualified Nothing))idents) (argTypes typ))
100+ fromSpineFun e (ObjectType rec) = App (lamCase "r" [mkRecCase (decomposeRec rec), CaseAlternative [NullBinder] (Right mkNothing)]) (App e (mkPrelVar "unit"))
102+ fromSpineFun e _ = App (mkVarMn genericModule "fromSpine") (App e (mkPrelVar "unit"))
104+ 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)
106+ mkRecFun :: [(String,Type)] -> Expr
107+ mkRecFun xs = mkJust $ foldr (\s e -> lam s e) recLiteral (map fst xs)
108+ where recLiteral = ObjectLiteral $ map (\(s,_) -> (s,mkVar s)) xs
110+ return . lamCase "x" $ map mkAlternative (constructorsForType t ctors) ++ [CaseAlternative [NullBinder] (Right mkNothing)]
114+pattern ObjectType rec = TypeApp (TypeConstructor (Qualified (Just (ModuleName [ProperName "Prim"])) (ProperName "Object"))) rec
116+lam :: String -> Expr -> Expr
117+lam s = Abs (Left (Ident s))
119+lamCase :: String -> [CaseAlternative] -> Expr
120+lamCase s = lam s . Case [mkVar s]
122+liftApplicative :: Expr -> [Expr] -> Expr
123+liftApplicative = foldl' (\x e -> App (App (mkPrelVar "ap") x) e)
125+mkVarMn :: Maybe ModuleName -> String -> Expr
126+mkVarMn mn s = Var (Qualified mn (Ident s))
128+mkVar :: String -> Expr
129+mkVar s = mkVarMn Nothing s
131+mkPrelVar :: String -> Expr
132+mkPrelVar s = mkVarMn (Just (ModuleName [ProperName "Prelude"])) s
134+decomposeRec :: Type -> [(String, Type)]
135+decomposeRec = sortBy (comparing fst) . go
136+ where go (RCons str typ typs) = (str, typ) : decomposeRec typs
139+argTypes :: Type -> [Type]
140+argTypes (TypeApp (TypeApp (TypeConstructor (Qualified (Just (ModuleName [ProperName "Prim"])) (ProperName "Function"))) a) b) = a : argTypes b
143+findName :: String -> [(Qualified ProperName, a)] -> Qualified ProperName
144+findName s lst = fromMaybe (Qualified Nothing (ProperName s)) . find ((== ProperName s) . unQualify) . map fst $ lst
146+unQualify :: Qualified a -> a
147+unQualify (Qualified _ x) = x
149+constructorsForType :: Qualified ProperName -> [(Qualified ProperName, (a, ProperName, b, c))] -> [(Qualified ProperName, (a, ProperName, b, c))]
150+constructorsForType t = filter (\(Qualified mn _ ,(_,typeName,_,_)) -> t == Qualified mn typeName)