{-# LANGUAGE TemplateHaskell, CPP #-} -- | -- Module : Conjure.Conjurable.Derive -- Copyright : (c) 2019-2021 Rudy Matela -- License : 3-Clause BSD (see the file LICENSE) -- Maintainer : Rudy Matela -- -- Allows automatic derivation of 'Conjurable' typeclass instances. module Conjure.Conjurable.Derive ( deriveConjurable , deriveConjurableCascading , deriveConjurableIfNeeded ) where import Test.LeanCheck import Test.LeanCheck.Derive import Test.LeanCheck.Utils import Conjure.Expr hiding (mkName, Name, isInstanceOf) import Conjure.Conjurable hiding (Name) import Data.Express.Utils (primeCycle) import Data.Express.Utils.TH import Control.Monad import Data.Char import Data.List import Language.Haskell.TH.Lib #if __GLASGOW_HASKELL__ < 710 import Data.Functor ((<$>)) #endif -- | Derives an 'Conjurable' instance for the given type 'Name'. -- -- This function needs the @TemplateHaskell@ extension. -- -- If '-:', '->:', '->>:', '->>>:', ... are not in scope, -- this will derive them as well. -- -- For now, -- this function only derives -- 'conjureEquality', -- 'conjureTiers' and -- 'conjureExpress' -- and does not derive -- 'conjureSubTypes', -- 'conjureArgumentCases' and -- 'conjureSize'. -- These will be added in future versions. -- If you plan to use features that depend on these functionalities, -- please define your instances manually. deriveConjurable :: Name -> DecsQ deriveConjurable = deriveWhenNeededOrWarn ''Conjurable reallyDerive where reallyDerive = reallyDeriveConjurableWithRequisites -- | Same as 'deriveConjurable' but does not warn when instance already exists -- ('deriveConjurable' is preferable). -- -- For now, -- this function only derives -- 'conjureEquality', -- 'conjureTiers' and -- 'conjureExpress' -- and does not derive -- 'conjureSubTypes', -- 'conjureArgumentCases' and -- 'conjureSize'. -- These will be added in future versions. -- If you plan to use features that depend on these functionalities, -- please define your instances manually. deriveConjurableIfNeeded :: Name -> DecsQ deriveConjurableIfNeeded = deriveWhenNeeded ''Conjurable reallyDerive where reallyDerive = reallyDeriveConjurableWithRequisites -- | Derives a 'Conjurable' instance for a given type 'Name' -- cascading derivation of type arguments as well. -- -- For now, -- this function only derives -- 'conjureEquality', -- 'conjureTiers' and -- 'conjureExpress' -- and does not derive -- 'conjureSubTypes', -- 'conjureArgumentCases' and -- 'conjureSize'. -- These will be added in future versions. -- If you plan to use features that depend on these functionalities, -- please define your instances manually. deriveConjurableCascading :: Name -> DecsQ deriveConjurableCascading = deriveWhenNeeded ''Conjurable reallyDerive where reallyDerive t = concat <$> sequence [ deriveListableCascading t , deriveNameCascading t , deriveExpressCascading t , reallyDeriveConjurableCascading t ] reallyDeriveConjurableWithRequisites :: Name -> DecsQ reallyDeriveConjurableWithRequisites t = concat <$> sequence [ deriveListableIfNeeded t , deriveNameIfNeeded t , deriveExpressIfNeeded t , reallyDeriveConjurable t ] reallyDeriveConjurable :: Name -> DecsQ reallyDeriveConjurable t = do isEq <- t `isInstanceOf` ''Eq isOrd <- t `isInstanceOf` ''Ord (nt,vs) <- normalizeType t #if __GLASGOW_HASKELL__ >= 710 cxt <- sequence [ [t| $(conT c) $(return v) |] #else -- template-haskell <= 2.9.0.0: cxt <- sequence [ classP c [return v] #endif | c <- [''Conjurable, ''Listable, ''Express] ++ [''Eq | isEq] ++ [''Ord | isOrd] , v <- vs] cs <- typeConstructorsArgNames t asName <- newName "x" let withTheReturnTypeOfs = deriveWithTheReturnTypeOfs $ [length ns | (_,ns) <- cs] let inst = [d| instance Conjurable $(return nt) where conjureExpress = reifyExpress conjureEquality = reifyEquality conjureTiers = reifyTiers |] -- withTheReturnTypeOfs |++| (cxt |=>| inst) cxt |=>| inst `addFun` deriveSize t `mergeI` deriveSubTypes t `mergeI` deriveCases t -- TODO: derive conjureCases, e.g.: -- conjureCases mx = [ value "Nothing" (Nothing -: mx) -- , value "Just" (Just ->: mx) :$ hole x -- ] deriveCases :: Name -> DecsQ deriveCases t = do n <- newName "x" (nt,vs) <- normalizeType t cs <- typeConstructorsArgNames t let lets = [letin n c ns | (c,ns) <- cs] let rhs = foldr (\e1 e2 -> [| $e1 : $e2 |]) [|[]|] lets [d| instance Conjurable $(return nt) where conjureCases $(varP n) = $rhs |] where letin :: Name -> Name -> [Name] -> ExpQ letin x c ns = do und <- VarE <$> lookupValN "undefined" let lhs = conP c (map varP ns) let rhs = return $ foldl AppE (ConE c) [und | _ <- ns] let retTypeOf = varE $ mkName $ "-" ++ replicate (length ns) '>' ++ ":" let ins = foldl (\e1 e2 -> [| $e1 :$ $e2 |]) [| value $(stringE $ nameBase c) ($retTypeOf $(conE c) $(varE x)) |] [ [| hole $(varE n) |] | n <- ns ] [| let $lhs = $rhs `asTypeOf` $(varE x) in $ins |] deriveSubTypes :: Name -> DecsQ deriveSubTypes t = do n <- newName "x" (nt,vs) <- normalizeType t cs <- typeConstructorsArgNames t let lets = [letin n c ns | (c,ns) <- cs, not (null ns)] let rhs = foldr0 (\e1 e2 -> [| $e1 . $e2 |]) [|id|] lets [d| instance Conjurable $(return nt) where conjureSubTypes $(varP n) = $rhs |] where letin :: Name -> Name -> [Name] -> ExpQ letin x c ns = do und <- VarE <$> lookupValN "undefined" let lhs = conP c (map varP ns) let rhs = return $ foldl AppE (ConE c) [und | _ <- ns] let bot = foldl1 (\e1 e2 -> [| $e1 . $e2 |]) [ [| conjureType $(varE n) |] | n <- ns ] [| let $lhs = $rhs `asTypeOf` $(varE x) in $bot |] deriveSize :: Name -> DecsQ deriveSize t = ((:[]) . FunD (mkName "conjureSize")) <$> deriveSizeClauses t deriveSizeClauses :: Name -> Q [Clause] deriveSizeClauses t = mapM (uncurry mkClause) =<< typeConstructors t where mkClause :: Name -> [Type] -> Q Clause mkClause n as = clause pat body [] where ns = take (length as) $ map mkName (variableNamesFromTemplate "x") pat = [conP n [varP n | n <- ns]] body = normalB $ foldl (\e n -> [| $e + conjureSize $(varE n) |]) [| 1 |] ns -- Not only really derive Conjurable instances, -- but cascade through argument types. reallyDeriveConjurableCascading :: Name -> DecsQ reallyDeriveConjurableCascading = reallyDeriveCascading ''Conjurable reallyDeriveConjurable deriveWithTheReturnTypeOfs :: [Int] -> DecsQ deriveWithTheReturnTypeOfs = fmap concat . mapM deriveWithTheReturnTypeOf . nubSort deriveWithTheReturnTypeOf :: Int -> DecsQ deriveWithTheReturnTypeOf n = do mf <- lookupValueName name case mf of Nothing -> reallyDeriveWithTheReturnTypeOf n Just _ -> return [] where name = "-" ++ replicate n '>' ++ ":" reallyDeriveWithTheReturnTypeOf :: Int -> DecsQ reallyDeriveWithTheReturnTypeOf n = do td <- sigD name theT vd <- [d| $(varP name) = const |] return $ td:vd where theT = bind [t| $(theFunT) -> $(last vars) -> $(theFunT) |] theFunT = foldr1 funT vars funT t1 t2 = [t| $(t1) -> $(t2) |] vars = map (varT . mkName) . take (n+1) . primeCycle $ map (:"") ['a'..'z'] name = mkName $ "-" ++ replicate n '>' ++ ":" #if __GLASGOW_HASKELL__ >= 800 bind = id -- unbound variables are automatically bound #else bind = toBoundedQ #endif addFun :: DecsQ -> DecsQ -> DecsQ qds1 `addFun` qds2 = do ds1 <- qds1 ds2 <- qds2 return $ ds1 `m` ds2 where #if __GLASGOW_HASKELL__ < 800 [InstanceD c ts ds1] `m` ds2 = [InstanceD c ts (ds1 ++ ds2)] #else [InstanceD o c ts ds1] `m` ds2 = [InstanceD o c ts (ds1 ++ ds2)] #endif