@@ -0,0 +1,224 @@
1+-----------------------------------------------------------------------------
3+-- Module : Language.PureScript.TypeChecker.Deriving
4+-- Copyright : (c) Gershom Bazerman 2015
5+-- License : MIT (http://opensource.org/licenses/MIT)
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
12+-- This module implements the generic deriving elaboration that takes place during desugaring.
14+-----------------------------------------------------------------------------
16+{-# LANGUAGE FlexibleContexts #-}
17+{-# LANGUAGE MultiParamTypeClasses #-}
18+{-# LANGUAGE OverloadedStrings #-}
19+{-# LANGUAGE PatternGuards #-}
20+{-# LANGUAGE RankNTypes #-}
21+{-# LANGUAGE ScopedTypeVariables #-}
23+module Language.PureScript.Sugar.TypeClasses.Deriving (
28+import Data.Maybe (fromMaybe)
29+import Data.Ord (comparing)
31+import Control.Applicative
32+import Control.Monad (replicateM)
33+import Control.Monad.Supply.Class (MonadSupply, freshName)
34+import Control.Monad.Error.Class (MonadError(..))
36+import Language.PureScript.AST
37+import Language.PureScript.Environment
38+import Language.PureScript.Errors
39+import Language.PureScript.Names
40+import Language.PureScript.Types
41+import qualified Language.PureScript.Constants as C
43+-- | Elaborates deriving instance declarations by code generation.
44+deriveInstances :: (Functor m, Applicative m, MonadError MultipleErrors m, MonadSupply m) => Module -> m Module
45+deriveInstances (Module coms mn ds exts) = Module coms mn <$> mapM (deriveInstance mn ds) ds <*> pure exts
47+-- | Takes a declaration, and if the declaration is a deriving TypeInstanceDeclaration,
48+-- elaborates that into an instance declaration via code generation.
49+deriveInstance :: (Functor m, MonadError MultipleErrors m, MonadSupply m) => ModuleName -> [Declaration] -> Declaration -> m Declaration
50+deriveInstance mn ds (TypeInstanceDeclaration nm deps className tys@[TypeConstructor tyName] Nothing)
51+ | className == Qualified (Just dataGeneric) (ProperName C.generic)
52+ , Qualified mn' tyCon <- tyName
53+ , mn == fromMaybe mn mn'
54+ = TypeInstanceDeclaration nm deps className tys . Just <$> deriveGeneric mn ds tyCon
55+deriveInstance _ _ (TypeInstanceDeclaration _ _ className tys Nothing)
56+ = throwError . errorMessage $ CannotDerive className tys
57+deriveInstance mn ds (PositionedDeclaration pos com d) = PositionedDeclaration pos com <$> deriveInstance mn ds d
58+deriveInstance _ _ e = return e
60+dataGeneric :: ModuleName
61+dataGeneric = ModuleName [ ProperName "Data", ProperName "Generic" ]
63+dataMaybe :: ModuleName
64+dataMaybe = ModuleName [ ProperName "Data", ProperName "Maybe" ]
66+deriveGeneric :: (Functor m, MonadError MultipleErrors m, MonadSupply m) => ModuleName -> [Declaration] -> ProperName -> m [Declaration]
67+deriveGeneric mn ds tyConNm = do
68+ tyCon <- findTypeDecl tyConNm ds
69+ toSpine <- mkSpineFunction mn tyCon
70+ fromSpine <- mkFromSpineFunction mn tyCon
71+ let toSignature = mkSignatureFunction mn tyCon
72+ return [ ValueDeclaration (Ident C.toSpine) Public [] (Right toSpine)
73+ , ValueDeclaration (Ident C.fromSpine) Public [] (Right fromSpine)
74+ , ValueDeclaration (Ident C.toSignature) Public [] (Right toSignature)
77+findTypeDecl :: (Functor m, MonadError MultipleErrors m) => ProperName -> [Declaration] -> m Declaration
78+findTypeDecl tyConNm = maybe (throwError . errorMessage $ CannotFindDerivingType tyConNm) return . find isTypeDecl
80+ isTypeDecl :: Declaration -> Bool
81+ isTypeDecl (DataDeclaration _ nm _ _) | nm == tyConNm = True
82+ isTypeDecl (PositionedDeclaration _ _ d) = isTypeDecl d
85+mkSpineFunction :: forall m. (Functor m, MonadSupply m) => ModuleName -> Declaration -> m Expr
86+mkSpineFunction mn (DataDeclaration _ _ _ args) = lamCase "$x" <$> mapM mkCtorClause args
88+ prodConstructor :: Expr -> Expr
89+ prodConstructor = App (Constructor (Qualified (Just dataGeneric) (ProperName "SProd")))
91+ recordConstructor :: Expr -> Expr
92+ recordConstructor = App (Constructor (Qualified (Just dataGeneric) (ProperName "SRecord")))
94+ mkCtorClause :: (ProperName, [Type]) -> m CaseAlternative
95+ mkCtorClause (ctorName, tys) = do
96+ idents <- replicateM (length tys) (fmap Ident freshName)
97+ return $ CaseAlternative [ConstructorBinder (Qualified (Just mn) ctorName) (map VarBinder idents)] (Right (caseResult idents))
100+ App (prodConstructor (StringLiteral . runProperName $ ctorName))
102+ $ zipWith toSpineFun (map (Var . Qualified Nothing) idents) tys
104+ toSpineFun :: Expr -> Type -> Expr
105+ toSpineFun i r | Just rec <- objectType r =
106+ lamNull . recordConstructor . ArrayLiteral .
107+ map (\(str,typ) -> ObjectLiteral [("recLabel", StringLiteral str), ("recValue", toSpineFun (Accessor str i) typ)])
109+ toSpineFun i _ = lamNull $ App (mkGenVar C.toSpine) i
110+mkSpineFunction mn (PositionedDeclaration _ _ d) = mkSpineFunction mn d
111+mkSpineFunction _ _ = error "mkSpineFunction: expected DataDeclaration"
113+mkSignatureFunction :: ModuleName -> Declaration -> Expr
114+mkSignatureFunction _ (DataDeclaration _ _ _ args) = lamNull . mkSigProd $ map mkProdClause args
116+ mkSigProd :: [Expr] -> Expr
117+ mkSigProd = App (Constructor (Qualified (Just dataGeneric) (ProperName "SigProd"))) . ArrayLiteral
119+ mkSigRec :: [Expr] -> Expr
120+ mkSigRec = App (Constructor (Qualified (Just dataGeneric) (ProperName "SigRecord"))) . ArrayLiteral
122+ proxy :: Type -> Type
123+ proxy = TypeApp (TypeConstructor (Qualified (Just dataGeneric) (ProperName "Proxy")))
125+ mkProdClause :: (ProperName, [Type]) -> Expr
126+ mkProdClause (ctorName, tys) = ObjectLiteral [ ("sigConstructor", StringLiteral (runProperName ctorName))
127+ , ("sigValues", ArrayLiteral . map mkProductSignature $ tys)
130+ mkProductSignature :: Type -> Expr
131+ mkProductSignature r | Just rec <- objectType r =
132+ lamNull . mkSigRec $ [ ObjectLiteral [ ("recLabel", StringLiteral str)
133+ , ("recValue", mkProductSignature typ)
135+ | (str, typ) <- decomposeRec rec
137+ mkProductSignature typ = lamNull $ App (mkGenVar C.toSignature)
138+ (TypedValue False (mkGenVar "anyProxy") (proxy typ))
139+mkSignatureFunction mn (PositionedDeclaration _ _ d) = mkSignatureFunction mn d
140+mkSignatureFunction _ _ = error "mkSignatureFunction: expected DataDeclaration"
142+mkFromSpineFunction :: forall m. (Functor m, MonadSupply m) => ModuleName -> Declaration -> m Expr
143+mkFromSpineFunction mn (DataDeclaration _ _ _ args) = lamCase "$x" <$> (addCatch <$> mapM mkAlternative args)
145+ mkJust :: Expr -> Expr
146+ mkJust = App (Constructor (Qualified (Just dataMaybe) (ProperName "Just")))
149+ mkNothing = Constructor (Qualified (Just dataMaybe) (ProperName "Nothing"))
151+ prodBinder :: [Binder] -> Binder
152+ prodBinder = ConstructorBinder (Qualified (Just dataGeneric) (ProperName "SProd"))
154+ recordBinder :: [Binder] -> Binder
155+ recordBinder = ConstructorBinder (Qualified (Just dataGeneric) (ProperName "SRecord"))
157+ mkAlternative :: (ProperName, [Type]) -> m CaseAlternative
158+ mkAlternative (ctorName, tys) = do
159+ idents <- replicateM (length tys) (fmap Ident freshName)
160+ return $ CaseAlternative [ prodBinder [ StringBinder (runProperName ctorName), ArrayBinder (map VarBinder idents)]]
162+ $ liftApplicative (mkJust $ Constructor (Qualified (Just mn) ctorName))
163+ (zipWith fromSpineFun (map (Var . (Qualified Nothing)) idents) tys)
165+ addCatch :: [CaseAlternative] -> [CaseAlternative]
166+ addCatch = (++ [catchAll])
168+ catchAll = CaseAlternative [NullBinder] (Right mkNothing)
171+ | Just rec <- objectType r
172+ = App (lamCase "r" [ mkRecCase (decomposeRec rec)
173+ , CaseAlternative [NullBinder] (Right mkNothing)
175+ (App e (mkPrelVar "unit"))
177+ fromSpineFun e _ = App (mkGenVar C.fromSpine) (App e (mkPrelVar "unit"))
179+ mkRecCase rs = CaseAlternative [ recordBinder [ ArrayBinder (map (VarBinder . Ident . fst) rs)
183+ $ liftApplicative (mkRecFun rs) (map (\(x, y) -> fromSpineFun (Accessor "recValue" (mkVar x)) y) rs)
185+ mkRecFun :: [(String, Type)] -> Expr
186+ mkRecFun xs = mkJust $ foldr (\s e -> lam s e) recLiteral (map fst xs)
187+ where recLiteral = ObjectLiteral $ map (\(s,_) -> (s,mkVar s)) xs
188+mkFromSpineFunction mn (PositionedDeclaration _ _ d) = mkFromSpineFunction mn d
189+mkFromSpineFunction _ _ = error "mkFromSpineFunction: expected DataDeclaration"
193+objectType :: Type -> Maybe Type
194+objectType (TypeApp (TypeConstructor (Qualified (Just (ModuleName [ProperName "Prim"])) (ProperName "Object"))) rec) = Just rec
195+objectType _ = Nothing
197+lam :: String -> Expr -> Expr
198+lam s = Abs (Left (Ident s))
200+lamNull :: Expr -> Expr
203+lamCase :: String -> [CaseAlternative] -> Expr
204+lamCase s = lam s . Case [mkVar s]
206+liftApplicative :: Expr -> [Expr] -> Expr
207+liftApplicative = foldl' (\x e -> App (App (mkPrelVar "apply") x) e)
209+mkVarMn :: Maybe ModuleName -> String -> Expr
210+mkVarMn mn s = Var (Qualified mn (Ident s))
212+mkVar :: String -> Expr
213+mkVar s = mkVarMn Nothing s
215+mkPrelVar :: String -> Expr
216+mkPrelVar s = mkVarMn (Just (ModuleName [ProperName C.prelude])) s
218+mkGenVar :: String -> Expr
219+mkGenVar s = mkVarMn (Just (ModuleName [ProperName "Data", ProperName C.generic])) s
221+decomposeRec :: Type -> [(String, Type)]
222+decomposeRec = sortBy (comparing fst) . go
223+ where go (RCons str typ typs) = (str, typ) : decomposeRec typs