Skip to content

Commit 11ed79b

Browse files
committed
reintroduce Nix.Utils, pass ith through Prelude
After making this flexible prelude setup, we are moving the custom code back into Nix.Utils, so the downstream projects do not have trouble importing Prelude module, but just use regular Nix.Utils module.
1 parent 2b39c5f commit 11ed79b

File tree

3 files changed

+310
-286
lines changed

3 files changed

+310
-286
lines changed

hnix.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ library
343343
exposed-modules:
344344
Prelude
345345
Nix
346+
Nix.Utils
346347
Nix.Atoms
347348
Nix.Builtins
348349
Nix.Cache

src/Nix/Utils.hs

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
{-# language NoImplicitPrelude #-}
2+
{-# language CPP #-}
3+
{-# language FunctionalDependencies #-}
4+
{-# language TemplateHaskell #-}
5+
{-# language GeneralizedNewtypeDeriving #-}
6+
7+
{-# options_ghc -Wno-missing-signatures #-}
8+
9+
-- | This is a module of custom "Prelude" code.
10+
-- It is for import for projects other then @HNix@.
11+
-- For @HNix@ - this module gets reexported by "Prelude", so for @HNix@ please fix-up pass-through there.
12+
module Nix.Utils
13+
( module Nix.Utils
14+
, module X
15+
)
16+
where
17+
18+
import Relude hiding ( force
19+
, readFile
20+
, whenJust
21+
, whenNothing
22+
, trace
23+
, traceM
24+
)
25+
26+
import Data.Binary ( Binary )
27+
import Data.Data ( Data )
28+
import Codec.Serialise ( Serialise )
29+
import Control.Monad.Fix ( MonadFix(..) )
30+
import Control.Monad.Free ( Free(..) )
31+
import Control.Monad.Trans.Control ( MonadTransControl(..) )
32+
import qualified Data.Aeson as A
33+
import qualified Data.Aeson.Encoding as A
34+
import Data.Fix ( Fix(..) )
35+
import qualified Data.HashMap.Lazy as M
36+
import qualified Data.Text as Text
37+
import qualified Data.Text.IO as Text
38+
import qualified Data.Vector as V
39+
import Lens.Family2 as X
40+
( view
41+
, over
42+
, LensLike'
43+
, Lens'
44+
)
45+
import Lens.Family2.Stock ( _1
46+
, _2
47+
)
48+
import Lens.Family2.TH ( makeLensesBy )
49+
50+
#if ENABLE_TRACING
51+
import qualified Relude.Debug as X
52+
#else
53+
-- Well, since it is currently CPP intermingled with Debug.Trace, required to use String here.
54+
trace :: String -> a -> a
55+
trace = const id
56+
{-# inline trace #-}
57+
traceM :: Monad m => String -> m ()
58+
traceM = const pass
59+
{-# inline traceM #-}
60+
#endif
61+
62+
$(makeLensesBy (\n -> pure $ "_" <> n) ''Fix)
63+
64+
-- | To have explicit type boundary between FilePath & String.
65+
newtype Path = Path FilePath
66+
deriving
67+
( Eq, Ord, Generic
68+
, Typeable, Data, NFData, Serialise, Binary, A.ToJSON, A.FromJSON
69+
, Show, Read, Hashable
70+
, Semigroup, Monoid
71+
)
72+
73+
instance ToText Path where
74+
toText = toText @String . coerce
75+
76+
instance IsString Path where
77+
fromString = coerce
78+
79+
80+
-- | > Hashmap Text -- type synonym
81+
type KeyMap = HashMap Text
82+
83+
-- | F-algebra defines how to reduce the fixed-point of a functor to a value.
84+
-- > type Alg f a = f a -> a
85+
type Alg f a = f a -> a
86+
87+
-- | > type AlgM f m a = f a -> m a
88+
type AlgM f m a = f a -> m a
89+
90+
-- | Do according transformation.
91+
--
92+
-- It is a transformation of a recursion scheme.
93+
-- See @TransformF@.
94+
type Transform f a = TransformF (Fix f) a
95+
-- | Do according transformation.
96+
--
97+
-- It is a transformation between functors.
98+
-- ...
99+
-- You got me, it is a natural transformation.
100+
type TransformF f a = (f -> a) -> f -> a
101+
102+
loeb :: Functor f => f (f a -> a) -> f a
103+
loeb x = go
104+
where
105+
go = ($ go) <$> x
106+
107+
loebM :: (MonadFix m, Traversable t) => t (t a -> m a) -> m (t a)
108+
-- Sectioning here insures optimization happening.
109+
loebM f = mfix $ \a -> (`traverse` f) ($ a)
110+
{-# inline loebM #-}
111+
112+
para :: Functor f => (f (Fix f, a) -> a) -> Fix f -> a
113+
para f = f . fmap (id &&& para f) . unFix
114+
115+
paraM :: (Traversable f, Monad m) => (f (Fix f, a) -> m a) -> Fix f -> m a
116+
paraM f = f <=< traverse (\x -> (x, ) <$> paraM f x) . unFix
117+
118+
cataP :: Functor f => (Fix f -> f a -> a) -> Fix f -> a
119+
cataP f x = f x . fmap (cataP f) . unFix $ x
120+
121+
cataPM :: (Traversable f, Monad m) => (Fix f -> f a -> m a) -> Fix f -> m a
122+
cataPM f x = f x <=< traverse (cataPM f) . unFix $ x
123+
124+
lifted
125+
:: (MonadTransControl u, Monad (u m), Monad m)
126+
=> ((a -> m (StT u b)) -> m (StT u b))
127+
-> (a -> u m b)
128+
-> u m b
129+
lifted f k =
130+
do
131+
lftd <- liftWith (\run -> f (run . k))
132+
restoreT $ pure lftd
133+
134+
-- | Replace:
135+
-- @Pure a -> a@
136+
-- @Free -> Fix@
137+
freeToFix :: Functor f => (a -> Fix f) -> Free f a -> Fix f
138+
freeToFix f = go
139+
where
140+
go =
141+
free
142+
f
143+
$ Fix . (go <$>)
144+
145+
-- | Replace:
146+
-- @a -> Pure a@
147+
-- @Fix -> Free@
148+
fixToFree :: Functor f => Fix f -> Free f a
149+
fixToFree = Free . go
150+
where
151+
go (Fix f) = Free . go <$> f
152+
153+
-- | adi is Abstracting Definitional Interpreters:
154+
--
155+
-- https://arxiv.org/abs/1707.04755
156+
--
157+
-- Essentially, it does for evaluation what recursion schemes do for
158+
-- representation: allows threading layers through existing structure, only
159+
-- in this case through behavior.
160+
adi
161+
:: Functor f
162+
=> Transform f a
163+
-> Alg f a
164+
-> Fix f
165+
-> a
166+
adi g f = g $ f . (adi g f <$>) . unFix
167+
168+
adiM
169+
:: ( Traversable t
170+
, Monad m
171+
)
172+
=> Transform t (m a)
173+
-> AlgM t m a
174+
-> Fix t
175+
-> m a
176+
adiM g f = g $ f <=< traverse (adiM g f) . unFix
177+
178+
179+
class Has a b where
180+
hasLens :: Lens' a b
181+
182+
instance Has a a where
183+
hasLens f = f
184+
185+
instance Has (a, b) a where
186+
hasLens = _1
187+
188+
instance Has (a, b) b where
189+
hasLens = _2
190+
191+
toEncodingSorted :: A.Value -> A.Encoding
192+
toEncodingSorted = \case
193+
A.Object m ->
194+
A.pairs
195+
. mconcat
196+
. ((\(k, v) -> A.pair k $ toEncodingSorted v) <$>)
197+
. sortWith fst
198+
$ M.toList m
199+
A.Array l -> A.list toEncodingSorted $ V.toList l
200+
v -> A.toEncoding v
201+
202+
data NixPathEntryType = PathEntryPath | PathEntryURI deriving (Show, Eq)
203+
204+
-- | @NIX_PATH@ is colon-separated, but can also contain URLs, which have a colon
205+
-- (i.e. @https://...@)
206+
uriAwareSplit :: Text -> [(Text, NixPathEntryType)]
207+
uriAwareSplit txt =
208+
case Text.break (== ':') txt of
209+
(e1, e2)
210+
| Text.null e2 -> [(e1, PathEntryPath)]
211+
| "://" `Text.isPrefixOf` e2 ->
212+
let ((suffix, _) : path) = uriAwareSplit (Text.drop 3 e2) in
213+
(e1 <> "://" <> suffix, PathEntryURI) : path
214+
| otherwise -> (e1, PathEntryPath) : uriAwareSplit (Text.drop 1 e2)
215+
216+
-- | Analog for @bool@ or @maybe@, for list-like cons structures.
217+
list
218+
:: Foldable t
219+
=> b -> (t a -> b) -> t a -> b
220+
list e f l =
221+
bool
222+
(f l)
223+
e
224+
(null l)
225+
{-# inline list #-}
226+
227+
whenText
228+
:: a -> (Text -> a) -> Text -> a
229+
whenText e f t =
230+
bool
231+
(f t)
232+
e
233+
(Text.null t)
234+
235+
-- | Lambda analog of @maybe@ or @either@ for Free monad.
236+
free :: (a -> b) -> (f (Free f a) -> b) -> Free f a -> b
237+
free fP fF fr =
238+
case fr of
239+
Pure a -> fP a
240+
Free fa -> fF fa
241+
{-# inline free #-}
242+
243+
244+
whenTrue :: (Monoid a)
245+
=> a -> Bool -> a
246+
whenTrue =
247+
bool
248+
mempty
249+
{-# inline whenTrue #-}
250+
251+
whenFalse :: (Monoid a)
252+
=> a -> Bool -> a
253+
whenFalse f =
254+
bool
255+
f
256+
mempty
257+
{-# inline whenFalse #-}
258+
259+
whenFree :: (Monoid b)
260+
=> (f (Free f a) -> b) -> Free f a -> b
261+
whenFree =
262+
free
263+
mempty
264+
{-# inline whenFree #-}
265+
266+
whenPure :: (Monoid b)
267+
=> (a -> b) -> Free f a -> b
268+
whenPure f =
269+
free
270+
f
271+
mempty
272+
{-# inline whenPure #-}
273+
274+
275+
-- | Apply a single function to both components of a pair.
276+
--
277+
-- > both succ (1,2) == (2,3)
278+
--
279+
-- Taken From package @extra@
280+
both :: (a -> b) -> (a, a) -> (b, b)
281+
both f (x,y) = (f x, f y)
282+
{-# inline both #-}
283+
284+
285+
-- | Duplicates object into a tuple.
286+
dup :: a -> (a, a)
287+
dup x = (x, x)
288+
{-# inline dup #-}
289+
290+
-- | From @utility-ht@ for tuple laziness.
291+
mapPair :: (a -> c, b -> d) -> (a,b) -> (c,d)
292+
mapPair ~(f,g) ~(a,b) = (f a, g b)
293+
{-# inline mapPair #-}
294+
295+
-- After migration from the @relude@ - @relude: pass -> stub@
296+
-- | @pure mempty@: Short-curcuit, stub.
297+
stub :: (Applicative f, Monoid a) => f a
298+
stub = pure mempty
299+
{-# inline stub #-}
300+
301+
readFile :: Path -> IO Text
302+
readFile = Text.readFile . coerce

0 commit comments

Comments
 (0)