hnix-0.13.0: src/Nix/Utils.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}
{-# OPTIONS_GHC -Wno-missing-signatures #-}
module Nix.Utils (module Nix.Utils, module X) where
import Control.Monad.Fix ( MonadFix(..) )
import Control.Monad.Free ( Free(..) )
import Control.Monad.Trans.Control ( MonadTransControl(..) )
import qualified Data.Aeson as A
import qualified Data.Aeson.Encoding as A
import Data.Fix ( Fix(..) )
import qualified Data.HashMap.Lazy as M
import qualified Data.Text as Text
import qualified Data.Vector as V
import Lens.Family2 as X hiding ((&))
import Lens.Family2.Stock ( _1
, _2
)
import Lens.Family2.TH ( makeLensesBy )
#if ENABLE_TRACING
import Debug.Trace 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
traceM :: Monad m => String -> m ()
traceM = const pass
#endif
$(makeLensesBy (\n -> pure ("_" <> n)) ''Fix)
type AttrSet = 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 AlgM f m a = f a -> m a
-- | "Transform" here means a modification of a catamorphism.
type Transform f a = (Fix f -> a) -> Fix f -> a
loeb :: Functor f => f (f a -> a) -> f a
loeb x = go
where
go = ($ go) <$> x
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 #-}
para :: Functor f => (f (Fix f, a) -> a) -> Fix f -> a
para f = f . fmap (id &&& para f) . unFix
paraM :: (Traversable f, Monad m) => (f (Fix f, a) -> m a) -> Fix f -> m a
paraM f = f <=< traverse (\x -> (x, ) <$> paraM f x) . unFix
cataP :: Functor f => (Fix f -> f a -> a) -> Fix f -> a
cataP f x = f x . fmap (cataP f) . unFix $ x
cataPM :: (Traversable f, Monad m) => (Fix f -> f a -> m a) -> Fix f -> m a
cataPM f x = f x <=< traverse (cataPM f) . unFix $ x
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
-- | Replace:
-- @Pure a -> a@
-- @Free -> Fix@
freeToFix :: Functor f => (a -> Fix f) -> Free f a -> Fix f
freeToFix f = go
where
go =
free
f
$ Fix . (go <$>)
fixToFree :: Functor f => Fix f -> Free f a
fixToFree = Free . go
where
go (Fix f) = Free . go <$> f
-- | adi is Abstracting Definitional Interpreters:
--
-- https://arxiv.org/abs/1707.04755
--
-- 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 => (f a -> a) -> ((Fix f -> a) -> Fix f -> a) -> Fix f -> a
adi f g = g $ f . (adi f g <$>) . unFix
adiM
:: (Traversable t, Monad m)
=> (t a -> m a)
-> ((Fix t -> m a) -> Fix t -> m a)
-> Fix t
-> m a
adiM f g = g $ f <=< traverse (adiM f g) . unFix
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
toEncodingSorted :: A.Value -> A.Encoding
toEncodingSorted = \case
A.Object m ->
A.pairs
. mconcat
. ((\(k, v) -> A.pair k $ toEncodingSorted v) <$>)
. sortWith fst
$ M.toList m
A.Array l -> A.list toEncodingSorted $ V.toList l
v -> A.toEncoding v
data NixPathEntryType = PathEntryPath | PathEntryURI deriving (Show, Eq)
-- | @NIX_PATH@ is colon-separated, but can also contain URLs, which have a colon
-- (i.e. @https://...@)
uriAwareSplit :: Text -> [(Text, NixPathEntryType)]
uriAwareSplit txt =
case Text.break (== ':') txt of
(e1, e2)
| Text.null e2 -> [(e1, PathEntryPath)]
| "://" `Text.isPrefixOf` e2 ->
let ((suffix, _) : path) = uriAwareSplit (Text.drop 3 e2) in
(e1 <> "://" <> suffix, PathEntryURI) : path
| otherwise -> (e1, PathEntryPath) : uriAwareSplit (Text.drop 1 e2)
alterF
:: (Eq k, Hashable k, Functor f)
=> (Maybe v -> f (Maybe v))
-> k
-> HashMap k v
-> f (HashMap k v)
alterF f k m =
maybe
(M.delete k m)
(\ v -> M.insert k v m)
<$> f (M.lookup k m)
-- | 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 #-}
-- | 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 #-}
whenFree :: (Monoid b)
=> (f (Free f a) -> b) -> Free f a -> b
whenFree =
free
mempty
{-# inline whenFree #-}
whenPure :: (Monoid b)
=> (a -> b) -> Free f a -> b
whenPure f =
free
f
mempty
{-# inline whenPure #-}
-- | 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 #-}