clash-lib-0.6.19: src/CLaSH/Util.hs
{-|
Copyright : (C) 2012-2016, University of Twente
License : BSD2 (see the file LICENSE)
Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com>
Assortment of utility function used in the CLaSH library
-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE TupleSections #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module CLaSH.Util
( module CLaSH.Util
, module X
, makeLenses
)
where
import Control.Applicative as X (Applicative,(<$>),(<*>),pure)
import Control.Arrow as X ((***),first,second)
import Control.DeepSeq
import Control.Monad as X ((<=<),(>=>))
import Control.Monad.State (MonadState,State,StateT,runState)
import qualified Control.Monad.State as State
import Control.Monad.Trans.Class (MonadTrans,lift)
import Data.Function as X (on)
import Data.Hashable (Hashable)
import Data.HashMap.Lazy (HashMap)
import qualified Data.HashMap.Lazy as HashMapL
import qualified Data.HashMap.Strict as HashMapS
import Data.Maybe (fromMaybe)
import Data.Version (Version)
import Control.Lens
import Debug.Trace (trace)
import GHC.Base (Int(..),isTrue#,(==#),(+#))
import GHC.Integer.Logarithms (integerLogBase#)
import qualified Language.Haskell.TH as TH
#ifdef CABAL
import qualified Paths_clash_lib (version)
#endif
-- | A class that can generate unique numbers
class MonadUnique m where
-- | Get a new unique
getUniqueM :: m Int
instance Monad m => MonadUnique (StateT Int m) where
getUniqueM = do
supply <- State.get
State.modify (+1)
return supply
-- | Create a TH expression that returns the a formatted string containing the
-- name of the module 'curLoc' is spliced into, and the line where it was spliced.
curLoc :: TH.Q TH.Exp
curLoc = do
(TH.Loc _ _ modName (startPosL,_) _) <- TH.location
TH.litE (TH.StringL $ modName ++ "(" ++ show startPosL ++ "): ")
-- | Cache the result of a monadic action
makeCached :: (MonadState s m, Hashable k, Eq k)
=> k -- ^ The key the action is associated with
-> Lens' s (HashMap k v) -- ^ The Lens to the HashMap that is the cache
-> m v -- ^ The action to cache
-> m v
makeCached key l create = do
cache <- use l
case HashMapL.lookup key cache of
Just value -> return value
Nothing -> do
value <- create
l %= HashMapL.insert key value
return value
-- | Cache the result of a monadic action in a State 3 transformer layers down
makeCachedT3 :: ( MonadTrans t2, MonadTrans t1, MonadTrans t
, Eq k, Hashable k
, MonadState s m
, Monad (t2 m), Monad (t1 (t2 m)), Monad (t (t1 (t2 m))))
=> k -- ^ The key the action is associated with
-> Lens' s (HashMap k v) -- ^ The Lens to the HashMap that is the cache
-> (t (t1 (t2 m))) v -- ^ The action to cache
-> (t (t1 (t2 m))) v
makeCachedT3 key l create = do
cache <- (lift . lift . lift) $ use l
case HashMapL.lookup key cache of
Just value -> return value
Nothing -> do
value <- create
(lift . lift . lift) $ l %= HashMapL.insert key value
return value
-- | Spine-strict cache variant of 'mkCachedT3'
makeCachedT3S :: ( MonadTrans t2, MonadTrans t1, MonadTrans t
, Eq k, Hashable k
, MonadState s m
, Monad (t2 m), Monad (t1 (t2 m)), Monad (t (t1 (t2 m)))
, NFData v)
=> k
-> Lens' s (HashMap k v)
-> (t (t1 (t2 m))) v
-> (t (t1 (t2 m))) v
makeCachedT3S key l create = do
cache <- (lift . lift . lift) $ use l
case HashMapS.lookup key cache of
Just value -> return value
Nothing -> do
value <- create
value `deepseq` ((lift . lift . lift) $ l %= HashMapS.insert key value)
return value
-- | Run a State-action using the State that is stored in a higher-layer Monad
liftState :: (MonadState s m)
=> Lens' s s' -- ^ Lens to the State in the higher-layer monad
-> State s' a -- ^ The State-action to perform
-> m a
liftState l m = do
s <- use l
let (a,s') = runState m s
l .= s'
return a
-- | Functorial version of 'Control.Arrow.first'
firstM :: Functor f
=> (a -> f c)
-> (a, b)
-> f (c, b)
firstM f (x,y) = (,y) <$> f x
-- | Functorial version of 'Control.Arrow.second'
secondM :: Functor f
=> (b -> f c)
-> (a, b)
-> f (a, c)
secondM f (x,y) = (x,) <$> f y
combineM :: (Applicative f)
=> (a -> f b)
-> (c -> f d)
-> (a,c)
-> f (b,d)
combineM f g (x,y) = (,) <$> f x <*> g y
-- | Performs trace when first argument evaluates to 'True'
traceIf :: Bool -> String -> a -> a
traceIf True msg = trace msg
traceIf False _ = id
-- | Monadic version of 'Data.List.partition'
partitionM :: Monad m
=> (a -> m Bool)
-> [a]
-> m ([a], [a])
partitionM _ [] = return ([], [])
partitionM p (x:xs) = do
test <- p x
(ys, ys') <- partitionM p xs
return $ if test then (x:ys, ys') else (ys, x:ys')
-- | Monadic version of 'Data.List.mapAccumL'
mapAccumLM :: (Monad m)
=> (acc -> x -> m (acc,y))
-> acc
-> [x]
-> m (acc,[y])
mapAccumLM _ acc [] = return (acc,[])
mapAccumLM f acc (x:xs) = do
(acc',y) <- f acc x
(acc'',ys) <- mapAccumLM f acc' xs
return (acc'',y:ys)
-- | Composition of a unary function with a binary function
dot :: (c -> d) -> (a -> b -> c) -> a -> b -> d
dot = (.) . (.)
-- | if-then-else as a function on an argument
ifThenElse :: (a -> Bool)
-> (a -> b)
-> (a -> b)
-> a
-> b
ifThenElse t f g a = if t a then f a else g a
infixr 5 <:>
-- | Applicative version of 'GHC.Types.(:)'
(<:>) :: Applicative f
=> f a
-> f [a]
-> f [a]
x <:> xs = (:) <$> x <*> xs
-- | Safe indexing, returns a 'Nothing' if the index does not exist
indexMaybe :: [a]
-> Int
-> Maybe a
indexMaybe [] _ = Nothing
indexMaybe (x:_) 0 = Just x
indexMaybe (_:xs) n = indexMaybe xs (n-1)
-- | Unsafe indexing, return a custom error message when indexing fails
indexNote :: String
-> [a]
-> Int
-> a
indexNote note = fromMaybe (error note) `dot` indexMaybe
-- | Split the second list at the length of the first list
splitAtList :: [b] -> [a] -> ([a], [a])
splitAtList [] xs = ([], xs)
splitAtList _ xs@[] = (xs, xs)
splitAtList (_:xs) (y:ys) = (y:ys', ys'')
where
(ys', ys'') = splitAtList xs ys
clashLibVersion :: Version
#ifdef CABAL
clashLibVersion = Paths_clash_lib.version
#else
clashLibVersion = error "development version"
#endif
-- | \x y -> ceiling (logBase x y), x > 1 && y > 0
clogBase :: Integer -> Integer -> Maybe Int
clogBase x y | x > 1 && y > 0 =
let z1 = integerLogBase# x y
z2 = integerLogBase# x (y-1)
in if (isTrue# (z1 ==# z2))
then Just (I# (z1 +# 1#))
else Just (I# z1)
clogBase _ _ = Nothing