{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -Wno-missing-signatures #-}
{-# OPTIONS_GHC -Wno-missing-pattern-synonym-signatures #-}
module Nix.Value where
import Control.Monad
import Control.Monad.Catch
import Control.Monad.Trans.Class
import Control.Monad.Trans.Except
import qualified Data.Aeson as A
import Data.Align
import Data.Fix
import Data.HashMap.Lazy (HashMap)
import qualified Data.HashMap.Lazy as M
import Data.Hashable
import Data.Monoid (appEndo)
import Data.Text (Text)
import Data.These
import Data.Typeable (Typeable)
import GHC.Generics
import Lens.Family2
import Lens.Family2.Stock
import Lens.Family2.TH
import Nix.Atoms
import Nix.Expr.Types
import Nix.Expr.Types.Annotated
import Nix.Frames
import Nix.Scope
import Nix.Thunk
import Nix.Utils
data NValueF m r
= NVConstantF NAtom
| NVStrF Text (DList Text)
| NVPathF FilePath
| NVListF [r]
| NVSetF (AttrSet r) (AttrSet SourcePos)
| NVClosureF (Params ()) (m (NValue m) -> m (NValue m))
| NVBuiltinF String (m (NValue m) -> m (NValue m))
deriving (Generic, Typeable, Functor, Foldable, Traversable)
type NValueNF m = Fix (NValueF m)
type ValueSet m = AttrSet (NThunk m)
data Provenance m = Provenance
{ _lexicalScope :: Scopes m (NThunk m)
, _originExpr :: NExprLocF (Maybe (NValue m))
}
data NThunk m = NThunk
{ _thunkProvenance :: [Provenance m]
, _baseThunk :: Thunk m (NValue m)
}
data NValue m = NValue
{ _valueProvenance :: [Provenance m]
, _baseValue :: NValueF m (NThunk m)
}
addProvenance :: (NValue m -> Provenance m) -> NValue m -> NValue m
addProvenance f l@(NValue p v) = NValue (f l : p) v
pattern NVConstant x <- NValue _ (NVConstantF x)
nvConstant x = NValue [] (NVConstantF x)
nvConstantP p x = NValue [p] (NVConstantF x)
pattern NVStr s d <- NValue _ (NVStrF s d)
nvStr s d = NValue [] (NVStrF s d)
nvStrP p s d = NValue [p] (NVStrF s d)
pattern NVPath x <- NValue _ (NVPathF x)
nvPath x = NValue [] (NVPathF x)
nvPathP p x = NValue [p] (NVPathF x)
pattern NVList l <- NValue _ (NVListF l)
nvList l = NValue [] (NVListF l)
nvListP p l = NValue [p] (NVListF l)
pattern NVSet s x <- NValue _ (NVSetF s x)
nvSet s x = NValue [] (NVSetF s x)
nvSetP p s x = NValue [p] (NVSetF s x)
pattern NVClosure x f <- NValue _ (NVClosureF x f)
nvClosure x f = NValue [] (NVClosureF x f)
nvClosureP p x f = NValue [p] (NVClosureF x f)
pattern NVBuiltin name f <- NValue _ (NVBuiltinF name f)
nvBuiltin name f = NValue [] (NVBuiltinF name f)
nvBuiltinP p name f = NValue [p] (NVBuiltinF name f)
instance Show (NValueF m (Fix (NValueF m))) where
showsPrec = flip go where
go (NVConstantF atom) = showsCon1 "NVConstant" atom
go (NVStrF txt ctxt) = showsCon2 "NVStr" txt (appEndo ctxt [])
go (NVListF lst) = showsCon1 "NVList" lst
go (NVSetF attrs _) = showsCon1 "NVSet" attrs
go (NVClosureF p _) = showsCon1 "NVClosure" p
go (NVPathF p) = showsCon1 "NVPath" p
go (NVBuiltinF name _) = showsCon1 "NVBuiltin" name
showsCon1 :: Show a => String -> a -> Int -> String -> String
showsCon1 con a d =
showParen (d > 10) $ showString (con ++ " ") . showsPrec 11 a
showsCon2 :: (Show a, Show b)
=> String -> a -> b -> Int -> String -> String
showsCon2 con a b d =
showParen (d > 10)
$ showString (con ++ " ")
. showsPrec 11 a
. showString " "
. showsPrec 11 b
instance Eq (NValue m) where
NVConstant (NFloat x) == NVConstant (NInt y) = x == fromInteger y
NVConstant (NInt x) == NVConstant (NFloat y) = fromInteger x == y
NVConstant (NInt x) == NVConstant (NInt y) = x == y
NVConstant (NFloat x) == NVConstant (NFloat y) = x == y
NVStr x _ == NVStr y _ = x < y
NVPath x == NVPath y = x < y
_ == _ = False
instance Ord (NValue m) where
NVConstant (NFloat x) <= NVConstant (NInt y) = x <= fromInteger y
NVConstant (NInt x) <= NVConstant (NFloat y) = fromInteger x <= y
NVConstant (NInt x) <= NVConstant (NInt y) = x <= y
NVConstant (NFloat x) <= NVConstant (NFloat y) = x <= y
NVStr x _ <= NVStr y _ = x < y
NVPath x <= NVPath y = x < y
_ <= _ = False
checkComparable :: (Framed e m, Typeable m) => NValue m -> NValue m -> m ()
checkComparable x y = case (x, y) of
(NVConstant (NFloat _), NVConstant (NInt _)) -> pure ()
(NVConstant (NInt _), NVConstant (NFloat _)) -> pure ()
(NVConstant (NInt _), NVConstant (NInt _)) -> pure ()
(NVConstant (NFloat _), NVConstant (NFloat _)) -> pure ()
(NVStr _ _, NVStr _ _) -> pure ()
(NVPath _, NVPath _) -> pure ()
_ -> throwError $ Comparison x y
builtin :: Monad m
=> String -> (m (NValue m) -> m (NValue m)) -> m (NValue m)
builtin name f = return $ nvBuiltin name f
builtin2 :: Monad m
=> String -> (m (NValue m) -> m (NValue m) -> m (NValue m))
-> m (NValue m)
builtin2 name f = builtin name (builtin name . f)
builtin3 :: Monad m
=> String
-> (m (NValue m) -> m (NValue m) -> m (NValue m) -> m (NValue m))
-> m (NValue m)
builtin3 name f =
builtin name $ \a -> builtin name $ \b -> builtin name $ \c -> f a b c
isClosureNF :: Monad m => NValueNF m -> Bool
isClosureNF (Fix NVClosureF {}) = True
isClosureNF _ = False
thunkEq :: MonadThunk (NValue m) (NThunk m) m
=> NThunk m -> NThunk m -> m Bool
thunkEq lt rt = force lt $ \lv -> force rt $ \rv -> valueEq lv rv
alignEqM
:: (Align f, Traversable f, Monad m)
=> (a -> b -> m Bool)
-> f a
-> f b
-> m Bool
alignEqM eq fa fb = fmap (either (const False) (const True)) $ runExceptT $ do
pairs <- forM (Data.Align.align fa fb) $ \case
These a b -> return (a, b)
_ -> throwE ()
forM_ pairs $ \(a, b) -> guard =<< lift (eq a b)
isDerivation :: MonadThunk (NValue m) (NThunk m) m
=> AttrSet (NThunk m) -> m Bool
isDerivation m = case M.lookup "type" m of
Nothing -> pure False
Just t -> force t $ valueEq (nvStr "derivation" mempty)
valueEq :: MonadThunk (NValue m) (NThunk m) m
=> NValue m -> NValue m -> m Bool
valueEq l r = case (l, r) of
(NVConstant lc, NVConstant rc) -> pure $ lc == rc
(NVStr ls _, NVStr rs _) -> pure $ ls == rs
(NVStr ls _, NVConstant NNull) -> pure $ ls == ""
(NVConstant NNull, NVStr rs _) -> pure $ "" == rs
(NVList ls, NVList rs) -> alignEqM thunkEq ls rs
(NVSet lm _, NVSet rm _) -> do
let compareAttrs = alignEqM thunkEq lm rm
isDerivation lm >>= \case
True -> isDerivation rm >>= \case
True | Just lp <- M.lookup "outPath" lm
, Just rp <- M.lookup "outPath" rm
-> thunkEq lp rp
_ -> compareAttrs
_ -> compareAttrs
(NVPath lp, NVPath rp) -> pure $ lp == rp
_ -> pure False
data ValueType
= TInt
| TFloat
| TBool
| TNull
| TString
| TList
| TSet
| TClosure
| TPath
| TBuiltin
deriving Show
valueType :: NValueF m r -> ValueType
valueType = \case
NVConstantF a -> case a of
NInt _ -> TInt
NFloat _ -> TFloat
NBool _ -> TBool
NNull -> TNull
NVStrF {} -> TString
NVListF {} -> TList
NVSetF {} -> TSet
NVClosureF {} -> TClosure
NVPathF {} -> TPath
NVBuiltinF {} -> TBuiltin
describeValue :: ValueType -> String
describeValue = \case
TInt -> "an integer"
TFloat -> "a float"
TBool -> "a boolean"
TNull -> "a null"
TString -> "a string"
TList -> "a list"
TSet -> "an attr set"
TClosure -> "a function"
TPath -> "a path"
TBuiltin -> "a builtin function"
instance Show (NValueF m (NThunk m)) where
show = show . describeValue . valueType
instance Show (NValue m) where
show (NValue _ v) = show v
instance Show (NThunk m) where
show (NThunk _ (Value v)) = show v
show (NThunk _ _) = "<thunk>"
data ValueFrame m
= ForcingThunk
| ConcerningValue (NValue m)
| Comparison (NValue m) (NValue m)
| Addition (NValue m) (NValue m)
| Multiplication (NValue m) (NValue m)
| Division (NValue m) (NValue m)
| Coercion ValueType ValueType
| CoercionToJsonNF (NValueNF m)
| CoercionFromJson A.Value
| ExpectationNF ValueType (NValueNF m)
| Expectation ValueType (NValue m)
deriving (Show, Typeable)
instance Typeable m => Exception (ValueFrame m)
$(makeTraversals ''NValueF)
$(makeLenses ''Provenance)
$(makeLenses ''NThunk)
$(makeLenses ''NValue)
alterF :: (Eq k, Hashable k, Functor f)
=> (Maybe v -> f (Maybe v)) -> k -> HashMap k v -> f (HashMap k v)
alterF f k m = f (M.lookup k m) <&> \case
Nothing -> M.delete k m
Just v -> M.insert k v m
hashAt :: VarName -> Lens' (AttrSet v) (Maybe v)
hashAt = flip alterF
key :: Applicative f => VarName -> LensLike' f (NValue m) (Maybe (NThunk m))
key k = baseValue._NVSetF._1.hashAt k