hnix-0.15.0: src/Nix/Utils.hs
{-# language NoImplicitPrelude #-}
{-# language CPP #-}
{-# language GeneralizedNewtypeDeriving #-}
-- | This is a module of custom "Prelude" code.
-- It is for import for projects other then @HNix@.
-- For @HNix@ - this module gets reexported by "Prelude", so for @HNix@ please fix-up pass-through there.
module Nix.Utils
( KeyMap
, TransformF
, Transform
, Alg
, Path(..)
, isAbsolute
, (</>)
, joinPath
, splitDirectories
, takeDirectory
, takeFileName
, takeBaseName
, takeExtension
, takeExtensions
, addExtension
, dropExtensions
, replaceExtension
, Has(..)
, trace
, traceM
, stub
, pass
, whenTrue
, whenFalse
, list
, whenText
, free
, whenJust
, dup
, mapPair
, both
, readFile
, traverseM
, lifted
, loebM
, adi
, module X
)
where
import Relude hiding ( pass
, force
, readFile
, whenJust
, whenNothing
, trace
, traceM
)
import Data.Binary ( Binary )
import Data.Data ( Data )
import Codec.Serialise ( Serialise )
import Control.Monad.Fix ( MonadFix(..) )
import Control.Monad.Free ( Free(..) )
import Control.Monad.Trans.Control ( MonadTransControl(..) )
import qualified Data.Aeson as A
import Data.Fix ( Fix(..) )
import qualified Data.Text as Text
import qualified Data.Text.IO as Text
import Lens.Family2 as X
( view
, over
, LensLike'
, Lens'
)
import Lens.Family2.Stock ( _1
, _2
)
import qualified System.FilePath as FilePath
#if ENABLE_TRACING
import qualified Relude.Debug as X
#else
-- Well, since it is currently CPP intermingled with Debug.Trace, required to use String here.
trace :: String -> a -> a
trace = const id
{-# inline trace #-}
traceM :: Monad m => String -> m ()
traceM = const stub
{-# inline traceM #-}
#endif
-- | To have explicit type boundary between FilePath & String.
newtype Path = Path FilePath
deriving
( Eq, Ord, Generic
, Typeable, Data, NFData, Serialise, Binary, A.ToJSON, A.FromJSON
, Show, Read, Hashable
, Semigroup, Monoid
)
instance ToText Path where
toText = toText @String . coerce
instance IsString Path where
fromString = coerce
-- This set of @Path@ funcs is to control system filepath types & typesafety and to easy migrate from FilePath to anything suitable (like @path@ or so).
-- | @isAbsolute@ specialized to @Path@.
isAbsolute :: Path -> Bool
isAbsolute = coerce FilePath.isAbsolute
-- | @(</>)@ specialized to @Path@
(</>) :: Path -> Path -> Path
(</>) = coerce (FilePath.</>)
infixr 5 </>
-- | @joinPath@ specialized to @Path@
joinPath :: [Path] -> Path
joinPath = coerce FilePath.joinPath
-- | @splitDirectories@ specialized to @Path@
splitDirectories :: Path -> [Path]
splitDirectories = coerce FilePath.splitDirectories
-- | @takeDirectory@ specialized to @Path@
takeDirectory :: Path -> Path
takeDirectory = coerce FilePath.takeDirectory
-- | @takeFileName@ specialized to @Path@
takeFileName :: Path -> Path
takeFileName = coerce FilePath.takeFileName
-- | @takeBaseName@ specialized to @Path@
takeBaseName :: Path -> String
takeBaseName = coerce FilePath.takeBaseName
-- | @takeExtension@ specialized to @Path@
takeExtension :: Path -> String
takeExtension = coerce FilePath.takeExtensions
-- | @takeExtensions@ specialized to @Path@
takeExtensions :: Path -> String
takeExtensions = coerce FilePath.takeExtensions
addExtension :: Path -> String -> Path
addExtension = coerce FilePath.addExtension
-- | @dropExtensions@ specialized to @Path@
dropExtensions :: Path -> Path
dropExtensions = coerce FilePath.dropExtensions
-- | @replaceExtension@ specialized to @Path@
replaceExtension :: Path -> String -> Path
replaceExtension = coerce FilePath.replaceExtension
-- | > Hashmap Text -- type synonym
type KeyMap = HashMap Text
-- | F-algebra defines how to reduce the fixed-point of a functor to a value.
-- > type Alg f a = f a -> a
type Alg f a = f a -> a
-- | Do according transformation.
--
-- It is a transformation of a recursion scheme.
-- See @TransformF@.
type Transform f a = TransformF (Fix f) a
-- | Do according transformation.
--
-- It is a transformation between functors.
-- ...
-- You got me, it is a natural transformation.
type TransformF f a = (f -> a) -> f -> a
class Has a b where
hasLens :: Lens' a b
instance Has a a where
hasLens f = f
instance Has (a, b) a where
hasLens = _1
instance Has (a, b) b where
hasLens = _2
loebM :: (MonadFix m, Traversable t) => t (t a -> m a) -> m (t a)
-- Sectioning here insures optimization happening.
loebM f = mfix $ \a -> (`traverse` f) ($ a)
{-# inline loebM #-}
-- 2021-08-21: NOTE: Someone needs to put in normal words, what this does.
-- This function is pretty spefic & used only once, in "Nix.Normal".
lifted
:: (MonadTransControl u, Monad (u m), Monad m)
=> ((a -> m (StT u b)) -> m (StT u b))
-> (a -> u m b)
-> u m b
lifted f k =
do
lftd <- liftWith (\run -> f (run . k))
restoreT $ pure lftd
-- | adi is Abstracting Definitional Interpreters:
--
-- https://arxiv.org/abs/1707.04755
--
-- All ADI does is interleaves every layer of evaluation by inserting intermitten layers between them, in that way the evaluation can be extended/embelished in any way wanted. Look at its use to see great examples.
--
-- Essentially, it does for evaluation what recursion schemes do for
-- representation: allows threading layers through existing structure, only
-- in this case through behavior.
adi
:: Functor f
=> Transform f a
-> Alg f a
-> Fix f
-> a
adi g f = g $ f . (adi g f <$>) . unFix
-- | Analog for @bool@ or @maybe@, for list-like cons structures.
list
:: Foldable t
=> b -> (t a -> b) -> t a -> b
list e f l =
bool
(f l)
e
(null l)
{-# inline list #-}
whenText
:: a -> (Text -> a) -> Text -> a
whenText e f t =
bool
(f t)
e
(Text.null t)
-- | Lambda analog of @maybe@ or @either@ for Free monad.
free :: (a -> b) -> (f (Free f a) -> b) -> Free f a -> b
free fP fF fr =
case fr of
Pure a -> fP a
Free fa -> fF fa
{-# inline free #-}
whenTrue :: (Monoid a)
=> a -> Bool -> a
whenTrue =
bool
mempty
{-# inline whenTrue #-}
whenFalse :: (Monoid a)
=> a -> Bool -> a
whenFalse f =
bool
f
mempty
{-# inline whenFalse #-}
whenJust
:: Monoid b
=> (a -> b)
-> Maybe a
-> b
whenJust =
maybe
mempty
{-# inline whenJust #-}
-- | Apply a single function to both components of a pair.
--
-- > both succ (1,2) == (2,3)
--
-- Taken From package @extra@
both :: (a -> b) -> (a, a) -> (b, b)
both f (x,y) = (f x, f y)
{-# inline both #-}
-- | Duplicates object into a tuple.
dup :: a -> (a, a)
dup x = (x, x)
{-# inline dup #-}
-- | From @utility-ht@ for tuple laziness.
mapPair :: (a -> c, b -> d) -> (a,b) -> (c,d)
mapPair ~(f,g) ~(a,b) = (f a, g b)
{-# inline mapPair #-}
-- After migration from the @relude@ - @relude: pass -> stub@
-- | @pure mempty@: Short-curcuit, stub.
stub :: (Applicative f, Monoid a) => f a
stub = pure mempty
{-# inline stub #-}
-- | Alias for @stub@, since @Relude@ has more specialized @pure ()@.
pass :: (Applicative f) => f ()
pass = stub
{-# inline pass #-}
readFile :: Path -> IO Text
readFile = Text.readFile . coerce
traverseM
:: ( Applicative m
, Applicative f
, Traversable t
)
=> ( a
-> m (f b)
)
-> t a
-> m (f (t b))
traverseM f x = sequenceA <$> traverse f x