module Control.Once.TH where
import Control.Once.Internal
import Control.Once.Class
import Control.Monad
import Data.Hashable
import Language.Haskell.TH.Lib
import Language.Haskell.TH
mkUncurry :: Int -> ExpQ
mkUncurry 1 = [| id |]
mkUncurry n = do
vs <- replicateM n (newName "x")
f <- newName "f"
let pats = map varP $ f:vs
tuple :: ExpQ
tuple = tupE $ map varE vs
lamE pats $ appE (varE f) tuple
mkCurry :: Int -> ExpQ
mkCurry 1 = [| id |]
mkCurry n = do
vs <- replicateM n (newName "x")
f <- newName "f"
let pats = [varP f, tupP $ map varP vs]
lamE pats $ appsE (varE f : map varE vs)
deriveOnce :: Int -> Q [Dec]
deriveOnce n = do
r <- newName "r"
as <- replicateM n (newName "a")
let subject = foldl fn (appT (conT ''IO) (varT r)) as
fn acc n = appT (appT arrowT (varT n)) acc
constrEq <- forM as $ \a -> appT (conT ''Eq) (varT a)
constrHash <- forM as $ \a -> appT (conT ''Hashable) (varT a)
let constr = pure $ constrEq ++ constrHash
bodyE = [| fmap $(mkUncurry n) . once1 . $(mkCurry n) |]
clause_ = clause [] (normalB bodyE) []
dec = funD 'once [clause_]
inst <- instanceD constr
(appT (conT ''Once) subject)
[dec]
pure [inst]