clash-finite-1.0.0.0: src/Clash/Class/Finite/Internal/TH.hs
{-|
Copyright : (C) 2024-2025, Felix Klein
License : MIT (see the file LICENSE)
Maintainer : Felix Klein <felix@qbaylogic.com>
-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE NoStarIsType #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_HADDOCK hide #-}
module Clash.Class.Finite.Internal.TH where
import Control.Monad (forM, replicateM)
#if !MIN_VERSION_base(4,20,0)
import Data.List (foldl')
#endif
import GHC.TypeNats (type (*))
import Language.Haskell.TH
#ifndef MAX_TUPLE_SIZE
#ifdef LARGE_TUPLES
#if MIN_VERSION_ghc(9,0,0)
import GHC.Settings.Constants (mAX_TUPLE_SIZE)
#else
import Constants (mAX_TUPLE_SIZE)
#endif
#define MAX_TUPLE_SIZE (fromIntegral mAX_TUPLE_SIZE)
#else
#define MAX_TUPLE_SIZE 12
#endif
#endif
maxTupleSize :: Num a => a
maxTupleSize = MAX_TUPLE_SIZE
-- | Contruct all the tuple instances (starting at size 3) for
-- 'Clash.Class.Finite.Internal.Finite'.
deriveFiniteTuples ::
-- | Finite
Name ->
-- | ElementCount
Name ->
-- | elements
Name ->
-- | lowest
Name ->
-- | lowestMaybe
Name ->
-- | highest
Name ->
-- | highestMaybe
Name ->
-- | predMaybe
Name ->
-- | succMaybe
Name ->
-- | ith
Name ->
-- | index
Name ->
DecsQ
deriveFiniteTuples finiteName elementCountName elementsName lowestName
lowestMaybeName highestName highestMaybeName predMaybeName succMaybeName
ithName indexName
= do
let finite = ConT finiteName
elementCount = ConT elementCountName
times = ConT ''(*)
allNames <- replicateM maxTupleSize $ newName "a"
t2N <- newName "t2N"
tN2 <- newName "tN2"
x <- newName "x"
forM [3..maxTupleSize] $ \tupleNum -> do
let names = take tupleNum allNames
(v,vs) = case map VarT names of
(z:zs) -> (z,zs)
_ -> error "maxTupleSize < 3"
tuple xs = foldl' AppT (TupleT $ length xs) xs
withConvContext b2N bN2 binds impl = return
$ Clause binds (NormalB impl)
$ ( if b2N then
(:) $ FunD t2N $ return
$ Clause
[ TupP [ p, TupP ps ]
| let (p,ps) = case map VarP names of
(z:zs) -> (z,zs)
_ -> error "maxTupleSize < 3"
]
( NormalB $ mkTupE $ map VarE names )
[]
else id
)
$ ( if bN2 then
(:) $ FunD tN2 $ return
$ Clause
[ TupP $ map VarP names ]
( let (e,es) = case map VarE names of
(z:zs) -> (z,zs)
_ -> error "maxTupleSize < 3"
in NormalB (mkTupE [e,mkTupE es])
)
[]
else id
)
[]
-- Instance declaration
context =
[ finite `AppT` v
, finite `AppT` tuple vs
]
instTy = AppT finite $ tuple (v:vs)
elementCountType =
mkTySynInstD elementCountName [tuple (v:vs)]
$ times `AppT` (elementCount `AppT` v) `AppT`
(elementCount `AppT` foldl AppT (TupleT $ tupleNum - 1) vs)
elements = FunD elementsName
$ withConvContext True False []
$ AppE (AppE (VarE '(<$>)) (VarE t2N))
$ VarE elementsName
lowest = FunD lowestName
$ withConvContext True False []
$ AppE (VarE t2N)
$ VarE lowestName
lowestMaybe = FunD lowestMaybeName
$ withConvContext True False []
$ AppE (AppE (VarE '(<$>)) (VarE t2N))
$ VarE lowestMaybeName
highest = FunD highestName
$ withConvContext True False []
$ AppE (VarE t2N)
$ VarE highestName
highestMaybe = FunD highestMaybeName
$ withConvContext True False []
$ AppE (AppE (VarE '(<$>)) (VarE t2N))
$ VarE highestMaybeName
predMaybe = FunD predMaybeName
$ withConvContext True True [ VarP x ]
$ AppE (AppE (VarE '(<$>)) (VarE t2N))
$ AppE (VarE predMaybeName)
$ AppE (VarE tN2)
$ VarE x
succMaybe = FunD succMaybeName
$ withConvContext True True [ VarP x ]
$ AppE (AppE (VarE '(<$>)) (VarE t2N))
$ AppE (VarE succMaybeName)
$ AppE (VarE tN2)
$ VarE x
ith = FunD ithName
$ withConvContext True False [ VarP x ]
$ AppE (VarE t2N)
$ AppE (VarE ithName)
$ VarE x
index = FunD indexName
$ withConvContext False True [ VarP x ]
$ AppE (VarE indexName)
$ AppE (VarE tN2)
$ VarE x
return $ InstanceD Nothing context instTy
[ elementCountType
, elements
, lowest
, lowestMaybe
, highest
, highestMaybe
, predMaybe
, succMaybe
, ith
, index
]
where
mkTupE = TupE
#if MIN_VERSION_template_haskell(2,16,0)
. map Just
#endif
-- | Compatibility helper to create TySynInstD (stolen from Clash
-- Prelude, as it is not exported by the library)
mkTySynInstD :: Name -> [Type] -> Type -> Dec
mkTySynInstD tyConNm tyArgs rhs =
#if MIN_VERSION_template_haskell(2,15,0)
TySynInstD (TySynEqn Nothing
(foldl AppT (ConT tyConNm) tyArgs)
rhs)
#else
TySynInstD tyConNm
(TySynEqn tyArgs
rhs)
#endif