packages feed

dhall 1.16.1 → 1.17.0

raw patch · 38 files changed

+2301/−446 lines, 38 filesdep +QuickCheckdep +cborgdep +hashabledep ~bytestringdep ~containersdep ~directoryPVP ok

version bump matches the API change (PVP)

Dependencies added: QuickCheck, cborg, hashable, quickcheck-instances, serialise, tasty-quickcheck

Dependency ranges changed: bytestring, containers, directory, insert-ordered-containers, transformers

API changes (from Hackage documentation)

- Dhall.Import: Status :: NonEmpty Import -> Map Import (Expr Src X) -> Maybe Dynamic -> Status
- Dhall.Import: [_cache] :: Status -> Map Import (Expr Src X)
- Dhall.Import: [_manager] :: Status -> Maybe Dynamic
- Dhall.Import: [_stack] :: Status -> NonEmpty Import
- Dhall.Import: loadDirWith :: MonadCatch m => FilePath -> (Import -> StateT Status m (Expr Src Import)) -> Context (Expr Src X) -> Normalizer X -> Expr Src Import -> m (Expr Src X)
- Dhall.Import: loadWithContext :: Context (Expr Src X) -> Normalizer X -> Expr Src Import -> IO (Expr Src X)
+ Dhall: (>$<) :: Contravariant f => a -> b -> f b -> f a
+ Dhall: (>*<) :: Divisible f => f a -> f b -> f (a, b)
+ Dhall: RecordInputType :: (InsOrdHashMap Text (InputType a)) -> RecordInputType a
+ Dhall: infixl 4 >$<
+ Dhall: infixr 5 >*<
+ Dhall: inputField :: Inject a => Text -> RecordInputType a
+ Dhall: inputFieldWith :: Text -> InputType a -> RecordInputType a
+ Dhall: inputRecord :: RecordInputType a -> InputType a
+ Dhall: instance Data.Functor.Contravariant.Contravariant Dhall.RecordInputType
+ Dhall: instance Data.Functor.Contravariant.Divisible.Divisible Dhall.RecordInputType
+ Dhall: newtype RecordInputType a
+ Dhall: protocolVersion :: (Functor f, HasEvaluateSettings s) => LensLike' f s ProtocolVersion
+ Dhall.Binary: CBORIsNotDhall :: Term -> DecodingFailure
+ Dhall.Binary: CannotDecodeProtocolVersionString :: Term -> DecodingFailure
+ Dhall.Binary: UnsupportedProtocolVersionString :: Text -> DecodingFailure
+ Dhall.Binary: V_1_0 :: ProtocolVersion
+ Dhall.Binary: data DecodingFailure
+ Dhall.Binary: data ProtocolVersion
+ Dhall.Binary: decode :: Term -> Either DecodingFailure (Expr s Import)
+ Dhall.Binary: defaultProtocolVersion :: ProtocolVersion
+ Dhall.Binary: encode :: ProtocolVersion -> Expr s Import -> Term
+ Dhall.Binary: instance GHC.Classes.Eq Dhall.Binary.DecodingFailure
+ Dhall.Binary: instance GHC.Exception.Exception Dhall.Binary.DecodingFailure
+ Dhall.Binary: instance GHC.Show.Show Dhall.Binary.DecodingFailure
+ Dhall.Binary: parseProtocolVersion :: Parser ProtocolVersion
+ Dhall.Core: HTTP :: Scheme
+ Dhall.Core: HTTPS :: Scheme
+ Dhall.Core: Remote :: URL -> ImportType
+ Dhall.Core: [authority] :: URL -> Text
+ Dhall.Core: [fragment] :: URL -> Maybe Text
+ Dhall.Core: [headers] :: URL -> Maybe ImportHashed
+ Dhall.Core: [path] :: URL -> File
+ Dhall.Core: [query] :: URL -> Maybe Text
+ Dhall.Core: [scheme] :: URL -> Scheme
+ Dhall.Core: data Scheme
+ Dhall.Core: data URL
+ Dhall.Core: instance GHC.Classes.Eq Dhall.Core.Scheme
+ Dhall.Core: instance GHC.Classes.Eq Dhall.Core.URL
+ Dhall.Core: instance GHC.Classes.Ord Dhall.Core.Scheme
+ Dhall.Core: instance GHC.Classes.Ord Dhall.Core.URL
+ Dhall.Core: instance GHC.Generics.Generic (Dhall.Core.Chunks s a)
+ Dhall.Core: instance GHC.Generics.Generic (Dhall.Core.Expr s a)
+ Dhall.Core: instance GHC.Generics.Generic Dhall.Core.Const
+ Dhall.Core: instance GHC.Generics.Generic Dhall.Core.Directory
+ Dhall.Core: instance GHC.Generics.Generic Dhall.Core.File
+ Dhall.Core: instance GHC.Generics.Generic Dhall.Core.FilePrefix
+ Dhall.Core: instance GHC.Generics.Generic Dhall.Core.Import
+ Dhall.Core: instance GHC.Generics.Generic Dhall.Core.ImportHashed
+ Dhall.Core: instance GHC.Generics.Generic Dhall.Core.ImportMode
+ Dhall.Core: instance GHC.Generics.Generic Dhall.Core.ImportType
+ Dhall.Core: instance GHC.Generics.Generic Dhall.Core.Scheme
+ Dhall.Core: instance GHC.Generics.Generic Dhall.Core.URL
+ Dhall.Core: instance GHC.Generics.Generic Dhall.Core.Var
+ Dhall.Core: instance GHC.Show.Show Dhall.Core.Scheme
+ Dhall.Core: instance GHC.Show.Show Dhall.Core.URL
+ Dhall.Import: cache :: Functor f => LensLike' f (Status m) (Map Import (Expr Src X))
+ Dhall.Import: manager :: Functor f => LensLike' f (Status m) (Maybe Dynamic)
+ Dhall.Import: normalizer :: Functor f => LensLike' f (Status m) (ReifiedNormalizer X)
+ Dhall.Import: protocolVersion :: Functor f => LensLike' f (Status m) ProtocolVersion
+ Dhall.Import: resolver :: Functor f => LensLike' f (Status m) (Import -> StateT (Status m) m (Expr Src Import))
+ Dhall.Import: stack :: Functor f => LensLike' f (Status m) (NonEmpty Import)
+ Dhall.Import: startingContext :: Functor f => LensLike' f (Status m) (Context (Expr Src X))
+ Dhall.Main: Default :: Bool -> Mode
+ Dhall.Main: Diff :: Text -> Text -> Mode
+ Dhall.Main: Format :: (Maybe FilePath) -> Mode
+ Dhall.Main: Freeze :: (Maybe FilePath) -> Mode
+ Dhall.Main: Hash :: Mode
+ Dhall.Main: Lint :: (Maybe FilePath) -> Mode
+ Dhall.Main: Normalize :: Mode
+ Dhall.Main: Options :: Mode -> Bool -> Bool -> ProtocolVersion -> Options
+ Dhall.Main: Repl :: Mode
+ Dhall.Main: Resolve :: Mode
+ Dhall.Main: Type :: Mode
+ Dhall.Main: Version :: Mode
+ Dhall.Main: [annotate] :: Mode -> Bool
+ Dhall.Main: [explain] :: Options -> Bool
+ Dhall.Main: [mode] :: Options -> Mode
+ Dhall.Main: [plain] :: Options -> Bool
+ Dhall.Main: [protocolVersion] :: Options -> ProtocolVersion
+ Dhall.Main: data Mode
+ Dhall.Main: data Options
+ Dhall.TypeCheck: RecordMismatch :: Char -> (Expr s a) -> (Expr s a) -> Const -> Const -> TypeMessage s a
- Dhall.Core: URL :: Text -> File -> Text -> (Maybe ImportHashed) -> ImportType
+ Dhall.Core: URL :: Scheme -> Text -> File -> Maybe Text -> Maybe Text -> Maybe ImportHashed -> URL
- Dhall.Core: isNormalized :: Expr s a -> Bool
+ Dhall.Core: isNormalized :: Eq a => Expr s a -> Bool
- Dhall.Freeze: freeze :: Maybe FilePath -> IO ()
+ Dhall.Freeze: freeze :: Maybe FilePath -> ProtocolVersion -> IO ()
- Dhall.Freeze: hashImport :: Import -> IO Import
+ Dhall.Freeze: hashImport :: ProtocolVersion -> Import -> IO Import
- Dhall.Hash: hash :: IO ()
+ Dhall.Hash: hash :: ProtocolVersion -> IO ()
- Dhall.Import: data Status
+ Dhall.Import: data Status m
- Dhall.Import: emptyStatus :: FilePath -> Status
+ Dhall.Import: emptyStatus :: FilePath -> Status IO
- Dhall.Import: exprFromImport :: Import -> StateT Status IO (Expr Src Import)
+ Dhall.Import: exprFromImport :: Import -> StateT (Status IO) IO (Expr Src Import)
- Dhall.Import: hashExpression :: Expr s X -> (Digest SHA256)
+ Dhall.Import: hashExpression :: ProtocolVersion -> Expr s X -> (Digest SHA256)
- Dhall.Import: hashExpressionToCode :: Expr s X -> Text
+ Dhall.Import: hashExpressionToCode :: ProtocolVersion -> Expr s X -> Text
- Dhall.Import: loadWith :: MonadCatch m => (Import -> StateT Status m (Expr Src Import)) -> Context (Expr Src X) -> Normalizer X -> Expr Src Import -> m (Expr Src X)
+ Dhall.Import: loadWith :: MonadCatch m => Expr Src Import -> StateT (Status m) m (Expr Src X)
- Dhall.Repl: repl :: Bool -> IO ()
+ Dhall.Repl: repl :: Bool -> ProtocolVersion -> IO ()

Files

CHANGELOG.md view
@@ -1,3 +1,46 @@+1.17.0++* This release corresponds to version 2.0.0 of the language standard+* BREAKING CHANGE TO THE LANGUAGE AND API: Binary serialization support+    * This is a breaking change to the hash for all semantic integrity checks+    * The hash used by the semantic integrity check is now based on the+      binary representation instead of a text representation of the+      expression+    * You can pin the new hashes by supplying the `--protocol-version 1.0`+      option on the command line until you need support for newer language+      features +    * This also includes a breaking change to `ImportType` in the API+* BREAKING CHANGE TO THE LANGUAGE: Disallow combining records of terms and+  types+    * This is mainly for consistency and to improve type errors that would+      have otherwise happened further downstream+    * This should not affect the vast majority of code+    * See: https://github.com/dhall-lang/dhall-haskell/pull/538+* BUG FIX: Semantic integrity checks now work for imported expression using+  the `constructors` keyword+    * See: https://github.com/dhall-lang/dhall-haskell/pull/554+* BUG FIX: Fix α-normalization of expressions with bound variables named `_`+    * See: https://github.com/dhall-lang/dhall-haskell/pull/524+* BUG FIX: Fix `isNormalized` to match `normalize`+    * See: https://github.com/dhall-lang/dhall-haskell/pull/522+* BUG FIX: `dhall lint` now correctly handles nested `let` expressions+    * See: https://github.com/dhall-lang/dhall-haskell/pull/555+* FEATURE: Imports protected by a semantic integrity check are now cached+    * See: https://github.com/dhall-lang/dhall-haskell/pull/533+* The default `dhall` command no longer outputs the type to `stderr`+    * You can add back the type as a type annotation using the+      `--annotate` switch+    * See: https://github.com/dhall-lang/dhall-haskell/pull/544+* New utilities for building `InputTypes`+    * See: https://github.com/dhall-lang/dhall-haskell/pull/530+* Improve parsing performance for long variable names+    * See: https://github.com/dhall-lang/dhall-haskell/pull/526+* More succinct type diffs for function types+    * See: https://github.com/dhall-lang/dhall-haskell/pull/540+* Identifier names can now begin with keywords+    * i.e. `ifChanged` and `lettuce` are now legal identifiers+    * See: https://github.com/dhall-lang/dhall-haskell/pull/551+ 1.16.1  * Fix test failure due to missing test data file
benchmark/deep-nested-large-record/Main.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE OverloadedStrings #-} module Main (main) where -import qualified Criterion as Criterion import Criterion.Main (defaultMain)++import qualified Criterion as Criterion import qualified Data.Sequence as Seq import qualified Dhall.Core as Core import qualified Dhall.Import as Import
benchmark/parser/Main.hs view
@@ -8,9 +8,12 @@  import System.Directory +import qualified Codec.Serialise import qualified Criterion.Main as Criterion+import qualified Data.ByteString.Lazy import qualified Data.Text as T import qualified Data.Text.IO as TIO+import qualified Dhall.Binary import qualified Dhall.Parser as Dhall  type PreludeFiles = Map FilePath T.Text@@ -42,12 +45,29 @@ benchExprFromText name expr =     bench name $ whnf (Dhall.exprFromText "(input)") expr +benchExprFromBytes+    :: String -> Data.ByteString.Lazy.ByteString -> Criterion.Benchmark+benchExprFromBytes name bytes = bench name (whnf f bytes)+  where+    f bytes = do+        term <- case Codec.Serialise.deserialiseOrFail bytes of+            Left  _    -> Nothing+            Right term -> return term+        case Dhall.Binary.decode term of+            Left  _          -> Nothing+            Right expression -> return expression+ main :: IO () main = do     prelude <- loadPreludeFiles-    issue108 <- TIO.readFile "benchmark/examples/issue108.dhall"+    issue108Text  <- TIO.readFile "benchmark/examples/issue108.dhall"+    issue108Bytes <- Data.ByteString.Lazy.readFile "benchmark/examples/issue108.dhall.bin"     defaultMain-        [ benchParser prelude-        , bgroup "Issue #108" $-            [ benchExprFromText "108" issue108 ]+        [ bgroup "Issue #108"+            [ benchExprFromText  "Text"   issue108Text+            , benchExprFromBytes "Binary" issue108Bytes+            ]+        , benchExprFromText "Long variable names" (T.replicate 1000000 "x")+        , benchExprFromText "Large number of function arguments" (T.replicate 10000 "x ")+        , benchParser prelude         ]
dhall.cabal view
@@ -1,5 +1,5 @@ Name: dhall-Version: 1.16.1+Version: 1.17.0 Cabal-Version: >=1.10 Build-Type: Simple Tested-With: GHC == 8.0.1@@ -173,13 +173,15 @@         ansi-terminal               >= 0.6.3.1  && < 0.9 ,         bytestring                                 < 0.11,         case-insensitive                           < 1.3 ,+        cborg                       >= 0.2.0.0  && < 0.3 ,         containers                  >= 0.5.0.0  && < 0.6 ,         contravariant                              < 1.6 ,         cryptonite                  >= 0.23     && < 1.0 ,         Diff                        >= 0.2      && < 0.4 ,-        directory                   >= 1.3      && < 1.4 ,+        directory                   >= 1.2.7.1  && < 1.4 ,         exceptions                  >= 0.8.3    && < 0.11,         filepath                    >= 1.4      && < 1.5 ,+        hashable                                   < 1.3 ,         haskeline                   >= 0.7.3.0  && < 0.8 ,         insert-ordered-containers   >= 0.2.1.0  && < 0.3 ,         lens-family-core            >= 1.0.0    && < 1.3 ,@@ -191,6 +193,7 @@         prettyprinter               >= 1.2.0.1  && < 1.3 ,         prettyprinter-ansi-terminal >= 1.1.1    && < 1.2 ,         repline                     >= 0.1.6.0  && < 0.2 ,+        serialise                   >= 0.2.0.0  && < 0.3 ,         scientific                  >= 0.3.0.0  && < 0.4 ,         template-haskell                           < 2.14,         text                        >= 0.11.1.0 && < 1.3 ,@@ -207,6 +210,7 @@      Exposed-Modules:         Dhall,+        Dhall.Binary,         Dhall.Context,         Dhall.Core,         Dhall.Diff,@@ -253,19 +257,27 @@         Import         Normalization         Parser+        QuickCheck         Regression         Tutorial         TypeCheck         Util     Build-Depends:         base                      >= 4        && < 5   ,+        containers                                     ,         deepseq                   >= 1.2.0.1  && < 1.5 ,         dhall                                          ,-        insert-ordered-containers                      ,+        hashable                                       ,+        insert-ordered-containers == 0.2.1.0           ,         prettyprinter                                  ,+        QuickCheck                >= 2.10     && < 2.12,+        quickcheck-instances      >= 0.3.12   && < 0.4 ,+        serialise                                      ,         tasty                     >= 0.11.2   && < 1.2 ,         tasty-hunit               >= 0.9.2    && < 0.11,+        tasty-quickcheck          >= 0.9.2    && < 0.11,         text                      >= 0.11.1.0 && < 1.3 ,+        transformers                                   ,         vector                    >= 0.11.0.0 && < 0.13     Default-Language: Haskell2010 @@ -287,10 +299,12 @@     Main-Is: benchmark/parser/Main.hs     Build-Depends:         base                      >= 4        && < 5  ,+        bytestring                                    ,         containers                >= 0.5.0.0  && < 0.6,         criterion                 >= 1.1      && < 1.6,         dhall                                         ,-        directory                 >= 1.3      && < 1.4,+        directory                 >= 1.2.7.1  && < 1.4,+        serialise                                     ,         text                      >= 0.11.1.0 && < 1.3     Default-Language: Haskell2010 
src/Dhall.hs view
@@ -28,6 +28,7 @@     , sourceName     , startingContext     , normalizer+    , protocolVersion     , defaultInputSettings     , InputSettings     , defaultEvaluateSettings@@ -66,9 +67,15 @@      , Inject(..)     , inject+    , RecordInputType(..)+    , inputFieldWith+    , inputField+    , inputRecord      -- * Miscellaneous     , rawInput+    , (>$<)+    , (>*<)      -- * Re-exports     , Natural@@ -81,7 +88,8 @@ import Control.Applicative (empty, liftA2, (<|>), Alternative) import Control.Exception (Exception) import Control.Monad.Trans.State.Strict-import Data.Functor.Contravariant (Contravariant(..))+import Data.Functor.Contravariant (Contravariant(..), (>$<))+import Data.Functor.Contravariant.Divisible (Divisible(..), divided) import Data.Monoid ((<>)) import Data.Scientific (Scientific) import Data.Sequence (Seq)@@ -89,18 +97,20 @@ import Data.Typeable (Typeable) import Data.Vector (Vector) import Data.Word (Word8, Word16, Word32, Word64)+import Dhall.Binary (ProtocolVersion(..)) import Dhall.Core (Expr(..), Chunks(..)) import Dhall.Import (Imported(..)) import Dhall.Parser (Src(..)) import Dhall.TypeCheck (DetailedTypeError(..), TypeError, X) import GHC.Generics-import Lens.Family (LensLike', view)+import Lens.Family (LensLike', set, view) import Numeric.Natural (Natural) import Prelude hiding (maybe, sequence) import System.FilePath (takeDirectory)  import qualified Control.Applicative import qualified Control.Exception+import qualified Control.Monad.Trans.State.Strict as State import qualified Data.Foldable import qualified Data.Functor.Compose import qualified Data.Functor.Product@@ -112,6 +122,7 @@ import qualified Data.Text.IO import qualified Data.Text.Lazy import qualified Data.Vector+import qualified Dhall.Binary import qualified Dhall.Context import qualified Dhall.Core import qualified Dhall.Import@@ -189,7 +200,8 @@ -- | @since 1.16 data EvaluateSettings = EvaluateSettings   { _startingContext :: Dhall.Context.Context (Expr Src X)-  , _normalizer :: Dhall.Core.ReifiedNormalizer X+  , _normalizer      :: Dhall.Core.ReifiedNormalizer X+  , _protocolVersion :: ProtocolVersion   }  -- | Default evaluation settings: no extra entries in the initial@@ -199,7 +211,8 @@ defaultEvaluateSettings :: EvaluateSettings defaultEvaluateSettings = EvaluateSettings   { _startingContext = Dhall.Context.empty-  , _normalizer = Dhall.Core.ReifiedNormalizer (const Nothing)+  , _normalizer      = Dhall.Core.ReifiedNormalizer (const Nothing)+  , _protocolVersion = Dhall.Binary.defaultProtocolVersion   }  -- | Access the starting context used for evaluation and type-checking.@@ -226,6 +239,17 @@       => LensLike' f EvaluateSettings (Dhall.Core.ReifiedNormalizer X)     l k s = fmap (\x -> s { _normalizer = x }) (k (_normalizer s)) +-- | Access the protocol version used when encoding or decoding Dhall+-- expressions to and from a binary representation+--+-- @since 1.17+protocolVersion+    :: (Functor f, HasEvaluateSettings s)+    => LensLike' f s ProtocolVersion+protocolVersion = evaluateSettings . l+  where+  l k s = fmap (\x -> s { _protocolVersion = x}) (k (_protocolVersion s))+ -- | @since 1.16 class HasEvaluateSettings s where   evaluateSettings@@ -282,12 +306,20 @@     -- ^ The decoded value in Haskell inputWithSettings settings (Type {..}) txt = do     expr  <- throws (Dhall.Parser.exprFromText (view sourceName settings) txt)-    expr' <- Dhall.Import.loadDirWith-               (view rootDirectory settings)-               Dhall.Import.exprFromImport-               (view startingContext settings)-               (Dhall.Core.getReifiedNormalizer (view normalizer settings))-               expr++    let InputSettings {..} = settings++    let EvaluateSettings {..} = _evaluateSettings++    let transform =+               set Dhall.Import.protocolVersion _protocolVersion+            .  set Dhall.Import.normalizer      _normalizer+            .  set Dhall.Import.startingContext _startingContext++    let status = transform (Dhall.Import.emptyStatus _rootDirectory)++    expr' <- State.evalStateT (Dhall.Import.loadWith expr) status+     let suffix = Dhall.Pretty.Internal.prettyToStrictText expected     let annot = case expr' of             Note (Src begin end bytes) _ ->@@ -367,12 +399,20 @@     -- ^ The fully normalized AST inputExprWithSettings settings txt = do     expr  <- throws (Dhall.Parser.exprFromText (view sourceName settings) txt)-    expr' <- Dhall.Import.loadDirWith-               (view rootDirectory settings)-               Dhall.Import.exprFromImport-               (view startingContext settings)-               (Dhall.Core.getReifiedNormalizer (view normalizer settings))-               expr++    let InputSettings {..} = settings++    let EvaluateSettings {..} = _evaluateSettings++    let transform =+               set Dhall.Import.protocolVersion _protocolVersion+            .  set Dhall.Import.normalizer      _normalizer+            .  set Dhall.Import.startingContext _startingContext++    let status = transform (Dhall.Import.emptyStatus _rootDirectory)++    expr' <- State.evalStateT (Dhall.Import.loadWith expr) status+     _ <- throws (Dhall.TypeCheck.typeWith (view startingContext settings) expr')     pure (Dhall.Core.normalizeWith (Dhall.Core.getReifiedNormalizer (view normalizer settings)) expr') @@ -658,9 +698,9 @@ vector :: Type a -> Type (Vector a) vector = fmap Data.Vector.fromList . list -{-| Decode `()` from an empty record.+{-| Decode @()@ from an empty record. ->>> input unit "{=}"  -- GHC doesn't print the result if it is @()@+>>> input unit "{=}"  -- GHC doesn't print the result if it is ()  -} unit :: Type ()@@ -677,7 +717,7 @@ >>> input string "\"ABC\"" "ABC" -"-}+-} string :: Type String string = Data.Text.Lazy.unpack <$> lazyText @@ -1333,3 +1373,87 @@           )           ( Data.Functor.Compose.Compose extractBody )       )++{-| The 'RecordInputType' divisible (contravariant) functor allows you to build +    an 'InputType' injector for a Dhall record.++    For example, let's take the following Haskell data type:++> data Project = Project+>   { projectName :: Text+>   , projectDescription :: Text+>   , projectStars :: Natural+>   }++    And assume that we have the following Dhall record that we would like to+    parse as a @Project@:++> { name =+>     "dhall-haskell"+> , description =+>     "A configuration language guaranteed to terminate"+> , stars =+>     289+> }++    Our injector has type 'InputType' @Project@, but we can't build that out of any+    smaller injectors, as 'InputType's cannot be combined (they are only 'Contravariant's).+    However, we can use an 'InputRecordType' to build an 'InputType' for @Project@:++> injectProject :: InputType Project+> injectProject =+>   inputRecord+>     (  adapt >$< inputFieldWith "name" inject+>              >*< inputFieldWith "description" inject+>              >*< inputFieldWith "stars" inject+>     )+>   where+>     adapt (Project{..}) = (projectName, (projectDescription, projectStars))++    Or, since we are simply using the `Inject` instance to inject each field, we could write++> injectProject :: InputType Project+> injectProject =+>   inputRecord+>     (  adapt >$< inputField "name"+>              >*< inputField "description"+>              >*< inputField "stars"+>     )+>   where+>     adapt (Project{..}) = (projectName, (projectDescription, projectStars))++-}++-- | Infix 'divided'+(>*<) :: Divisible f => f a -> f b -> f (a, b)+(>*<) = divided++infixr 5 >*<++newtype RecordInputType a+  = RecordInputType (Data.HashMap.Strict.InsOrd.InsOrdHashMap Text (InputType a))++instance Contravariant RecordInputType where+  contramap f (RecordInputType inputTypeRecord) = RecordInputType $ contramap f <$> inputTypeRecord++instance Divisible RecordInputType where+  divide f (RecordInputType bInputTypeRecord) (RecordInputType cInputTypeRecord) =+      RecordInputType+    $ Data.HashMap.Strict.InsOrd.union+      ((contramap $ fst . f) <$> bInputTypeRecord)+      ((contramap $ snd . f) <$> cInputTypeRecord)+  conquer = RecordInputType Data.HashMap.Strict.InsOrd.empty++inputFieldWith :: Text -> InputType a -> RecordInputType a+inputFieldWith name inputType = RecordInputType $ Data.HashMap.Strict.InsOrd.singleton name inputType++inputField :: Inject a => Text -> RecordInputType a+inputField name = inputFieldWith name inject++inputRecord :: RecordInputType a -> InputType a+inputRecord (RecordInputType inputTypeRecord) = InputType makeRecordLit recordType+  where+    recordType = Record $ declared <$> inputTypeRecord+    makeRecordLit x = RecordLit $ (($ x) . embed) <$> inputTypeRecord++
+ src/Dhall/Binary.hs view
@@ -0,0 +1,795 @@+{-# LANGUAGE RecordWildCards    #-}+{-# LANGUAGE OverloadedStrings  #-}++{-| This module contains logic for converting Dhall expressions to and from+    CBOR expressions which can in turn be converted to and from a binary+    representation+-}++module Dhall.Binary+    ( -- * Protocol versions+      ProtocolVersion(..)+    , defaultProtocolVersion+    , parseProtocolVersion++    -- * Encoding and decoding+    , encode+    , decode++    -- * Exceptions+    , DecodingFailure(..)+    ) where++import Codec.CBOR.Term (Term(..))+import Control.Applicative (empty)+import Control.Exception (Exception)+import Dhall.Core+    ( Chunks(..)+    , Const(..)+    , Directory(..)+    , Expr(..)+    , File(..)+    , FilePrefix(..)+    , Import(..)+    , ImportHashed(..)+    , ImportMode(..)+    , ImportType(..)+    , Scheme(..)+    , URL(..)+    , Var(..)+    )+import Data.Monoid ((<>))+import Data.Text (Text)+import Options.Applicative (Parser)+import Prelude hiding (exponent)++import qualified Data.Foldable+import qualified Data.HashMap.Strict.InsOrd+import qualified Data.Scientific+import qualified Data.Sequence+import qualified Data.Set+import qualified Data.Text+import qualified Options.Applicative++-- | Supported protocol version strings+data ProtocolVersion+    = V_1_0+    -- ^ Protocol version string "1.0"++defaultProtocolVersion :: ProtocolVersion+defaultProtocolVersion = V_1_0++parseProtocolVersion :: Parser ProtocolVersion+parseProtocolVersion =+    Options.Applicative.option readProtocolVersion+        (   Options.Applicative.long "protocol-version"+        <>  Options.Applicative.metavar "X.Y"+        <>  Options.Applicative.value defaultProtocolVersion+        )+  where+    readProtocolVersion = do+        string <- Options.Applicative.str+        case string :: Text of+            "1.0" -> return V_1_0+            _     -> fail "Unsupported protocol version"++{-| Convert a function applied to multiple arguments to the base function and+    the list of arguments+-}+unApply :: Expr s a -> (Expr s a, [Expr s a])+unApply e = (baseFunction₀, diffArguments₀ [])+  where+    ~(baseFunction₀, diffArguments₀) = go e++    go (App f a) = (baseFunction, diffArguments . (a :))+      where+        ~(baseFunction, diffArguments) = go f+    go baseFunction = (baseFunction, id)++encode_1_0 :: Expr s Import -> Term+encode_1_0 (Var (V "_" n)) =+    TInteger n+encode_1_0 (Var (V x 0)) =+    TString x+encode_1_0 (Var (V x n)) =+    TList [ TString x, TInteger n ]+encode_1_0 NaturalBuild =+    TString "Natural/build"+encode_1_0 NaturalFold =+    TString "Natural/fold"+encode_1_0 NaturalIsZero =+    TString "Natural/isZero"+encode_1_0 NaturalEven =+    TString "Natural/even"+encode_1_0 NaturalOdd =+    TString "Natural/odd"+encode_1_0 NaturalToInteger =+    TString "Natural/toInteger"+encode_1_0 NaturalShow =+    TString "Natural/show"+encode_1_0 IntegerToDouble =+    TString "Integer/toDouble"+encode_1_0 IntegerShow =+    TString "Integer/show"+encode_1_0 DoubleShow =+    TString "Double/show"+encode_1_0 ListBuild =+    TString "List/build"+encode_1_0 ListFold =+    TString "List/fold"+encode_1_0 ListLength =+    TString "List/length"+encode_1_0 ListHead =+    TString "List/head"+encode_1_0 ListLast =+    TString "List/last"+encode_1_0 ListIndexed =+    TString "List/indexed"+encode_1_0 ListReverse =+    TString "List/reverse"+encode_1_0 OptionalFold =+    TString "Optional/fold"+encode_1_0 OptionalBuild =+    TString "Optional/build"+encode_1_0 Bool =+    TString "Bool"+encode_1_0 Optional =+    TString "Optional"+encode_1_0 Natural =+    TString "Natural"+encode_1_0 Integer =+    TString "Integer"+encode_1_0 Double =+    TString "Double"+encode_1_0 Text =+    TString "Text"+encode_1_0 List =+    TString "List"+encode_1_0 (Const Type) =+    TString "Type"+encode_1_0 (Const Kind) =+    TString "Kind"+encode_1_0 e@(App _ _) =+    TList ([ TInt 0, f₁ ] ++ map encode_1_0 arguments)+  where+    (f₀, arguments) = unApply e++    f₁ = encode_1_0 f₀+encode_1_0 (Lam "_" _A₀ b₀) =+    TList [ TInt 1, _A₁, b₁ ]+  where+    _A₁ = encode_1_0 _A₀+    b₁  = encode_1_0 b₀+encode_1_0 (Lam x _A₀ b₀) =+    TList [ TInt 1, TString x, _A₁, b₁ ]+  where+    _A₁ = encode_1_0 _A₀+    b₁  = encode_1_0 b₀+encode_1_0 (Pi "_" _A₀ _B₀) =+    TList [ TInt 2, _A₁, _B₁ ]+  where+    _A₁ = encode_1_0 _A₀+    _B₁ = encode_1_0 _B₀+encode_1_0 (Pi x _A₀ _B₀) =+    TList [ TInt 2, TString x, _A₁, _B₁ ]+  where+    _A₁ = encode_1_0 _A₀+    _B₁ = encode_1_0 _B₀+encode_1_0 (BoolOr l₀ r₀) =+    TList [ TInt 3, TInt 0, l₁, r₁ ]+  where+    l₁ = encode_1_0 l₀+    r₁ = encode_1_0 r₀+encode_1_0 (BoolAnd l₀ r₀) =+    TList [ TInt 3, TInt 1, l₁, r₁ ]+  where+    l₁ = encode_1_0 l₀+    r₁ = encode_1_0 r₀+encode_1_0 (BoolEQ l₀ r₀) =+    TList [ TInt 3, TInt 2, l₁, r₁ ]+  where+    l₁ = encode_1_0 l₀+    r₁ = encode_1_0 r₀+encode_1_0 (BoolNE l₀ r₀) =+    TList [ TInt 3, TInt 3, l₁, r₁ ]+  where+    l₁ = encode_1_0 l₀+    r₁ = encode_1_0 r₀+encode_1_0 (NaturalPlus l₀ r₀) =+    TList [ TInt 3, TInt 4, l₁, r₁ ]+  where+    l₁ = encode_1_0 l₀+    r₁ = encode_1_0 r₀+encode_1_0 (NaturalTimes l₀ r₀) =+    TList [ TInt 3, TInt 5, l₁, r₁ ]+  where+    l₁ = encode_1_0 l₀+    r₁ = encode_1_0 r₀+encode_1_0 (TextAppend l₀ r₀) =+    TList [ TInt 3, TInt 6, l₁, r₁ ]+  where+    l₁ = encode_1_0 l₀+    r₁ = encode_1_0 r₀+encode_1_0 (ListAppend l₀ r₀) =+    TList [ TInt 3, TInt 7, l₁, r₁ ]+  where+    l₁ = encode_1_0 l₀+    r₁ = encode_1_0 r₀+encode_1_0 (Combine l₀ r₀) =+    TList [ TInt 3, TInt 8, l₁, r₁ ]+  where+    l₁ = encode_1_0 l₀+    r₁ = encode_1_0 r₀+encode_1_0 (Prefer l₀ r₀) =+    TList [ TInt 3, TInt 9, l₁, r₁ ]+  where+    l₁ = encode_1_0 l₀+    r₁ = encode_1_0 r₀+encode_1_0 (CombineTypes l₀ r₀) =+    TList [ TInt 3, TInt 10, l₁, r₁ ]+  where+    l₁ = encode_1_0 l₀+    r₁ = encode_1_0 r₀+encode_1_0 (ImportAlt l₀ r₀) =+    TList [ TInt 3, TInt 11, l₁, r₁ ]+  where+    l₁ = encode_1_0 l₀+    r₁ = encode_1_0 r₀+encode_1_0 (ListLit _T₀ xs₀)+    | null xs₀  = TList [ TInt 4, _T₁ ]+    | otherwise = TList ([ TInt 4, TNull ] ++ xs₁)+  where+    _T₁ = case _T₀ of+        Nothing -> TNull+        Just t  -> encode_1_0 t++    xs₁ = map encode_1_0 (Data.Foldable.toList xs₀)+encode_1_0 (OptionalLit _T₀ Nothing) =+    TList [ TInt 5, _T₁ ]+  where+    _T₁ = encode_1_0 _T₀+encode_1_0 (OptionalLit _T₀ (Just t₀)) =+    TList [ TInt 5, _T₁, t₁ ]+  where+    _T₁ = encode_1_0 _T₀+    t₁  = encode_1_0 t₀+encode_1_0 (Merge t₀ u₀ Nothing) =+    TList [ TInt 6, t₁, u₁ ]+  where+    t₁ = encode_1_0 t₀+    u₁ = encode_1_0 u₀+encode_1_0 (Merge t₀ u₀ (Just _T₀)) =+    TList [ TInt 6, t₁, u₁, _T₁ ]+  where+    t₁  = encode_1_0 t₀+    u₁  = encode_1_0 u₀+    _T₁ = encode_1_0 _T₀+encode_1_0 (Record xTs₀) =+    TList [ TInt 7, TMap xTs₁ ]+  where+    xTs₁ = do+        (x₀, _T₀) <- Data.HashMap.Strict.InsOrd.toList xTs₀+        let x₁  = TString x₀+        let _T₁ = encode_1_0 _T₀+        return (x₁, _T₁)+encode_1_0 (RecordLit xts₀) =+    TList [ TInt 8, TMap xts₁ ]+  where+    xts₁ = do+        (x₀, t₀) <- Data.HashMap.Strict.InsOrd.toList xts₀+        let x₁ = TString x₀+        let t₁ = encode_1_0 t₀+        return (x₁, t₁)+encode_1_0 (Field t₀ x) =+    TList [ TInt 9, t₁, TString x ]+  where+    t₁ = encode_1_0 t₀+encode_1_0 (Project t₀ xs₀) =+    TList ([ TInt 10, t₁ ] ++ xs₁)+  where+    t₁  = encode_1_0 t₀+    xs₁ = map TString (Data.Foldable.toList xs₀)+encode_1_0 (Union xTs₀) =+    TList [ TInt 11, TMap xTs₁ ]+  where+    xTs₁ = do+        (x₀, _T₀) <- Data.HashMap.Strict.InsOrd.toList xTs₀+        let x₁  = TString x₀+        let _T₁ = encode_1_0 _T₀+        return (x₁, _T₁)+encode_1_0 (UnionLit x t₀ yTs₀) =+    TList [ TInt 12, TString x, t₁, TMap yTs₁ ]+  where+    t₁ = encode_1_0 t₀++    yTs₁ = do+        (y₀, _T₀) <- Data.HashMap.Strict.InsOrd.toList yTs₀+        let y₁  = TString y₀+        let _T₁ = encode_1_0 _T₀+        return (y₁, _T₁)+encode_1_0 (Constructors u₀) =+    TList [ TInt 13, u₁ ]+  where+    u₁ = encode_1_0 u₀+encode_1_0 (BoolLit b) =+    TBool b+encode_1_0 (BoolIf t₀ l₀ r₀) =+    TList [ TInt 14, t₁, l₁, r₁ ]+  where+    t₁ = encode_1_0 t₀+    l₁ = encode_1_0 l₀+    r₁ = encode_1_0 r₀+encode_1_0 (NaturalLit n) =+    TList [ TInt 15, TInteger (fromIntegral n) ]+encode_1_0 (IntegerLit n) =+    TList [ TInt 16, TInteger n ]+encode_1_0 (DoubleLit n) =+    TList [ TInt 17, TTagged 4 (TList [ TInt exponent, TInteger mantissa ]) ]+  where+    normalized = Data.Scientific.normalize n++    exponent = Data.Scientific.base10Exponent normalized++    mantissa = Data.Scientific.coefficient normalized+encode_1_0 (TextLit (Chunks xys₀ z₀)) =+    TList ([ TInt 18 ] ++ xys₁ ++ [ z₁ ])+  where+    xys₁ = do+        (x₀, y₀) <- xys₀+        let x₁ = TString x₀+        let y₁ = encode_1_0 y₀+        [ x₁, y₁ ]++    z₁ = TString z₀+encode_1_0 (Embed x) =+    importToTerm x+encode_1_0 (Let x Nothing a₀ b₀) =+    TList [ TInt 25, TString x, a₁, b₁ ]+  where+    a₁ = encode_1_0 a₀+    b₁ = encode_1_0 b₀+encode_1_0 (Let x (Just _A₀) a₀ b₀) =+    TList [ TInt 25, TString x, _A₁, a₁, b₁ ]+  where+    a₁  = encode_1_0 a₀+    _A₁ = encode_1_0 _A₀+    b₁  = encode_1_0 b₀+encode_1_0 (Annot t₀ _T₀) =+    TList [ TInt 26, t₁, _T₁ ]+  where+    t₁  = encode_1_0 t₀+    _T₁ = encode_1_0 _T₀+encode_1_0 (Note _ e) =+    encode_1_0 e++importToTerm :: Import -> Term+importToTerm import_ =+    case importType of+        Remote (URL { scheme = scheme₀, ..}) ->+            TList+                (   [ TInt 24, TInt scheme₁, TString authority ]+                ++  map TString (reverse components)+                ++  [ TString file ]+                ++  (case query    of Nothing -> [ TNull ]; Just q -> [ TString q ])+                ++  (case fragment of Nothing -> [ TNull ]; Just f -> [ TString f ])+                )+          where+            scheme₁ = case scheme₀ of+                HTTP  -> 0+                HTTPS -> 1+            File {..} = path++            Directory {..} = directory++        Local prefix₀ path ->+                TList+                    (   [ TInt 24, TInt prefix₁ ]+                    ++  map TString components₁+                    ++  [ TString file ]+                    )+          where+            File {..} = path++            Directory {..} = directory++            (prefix₁, components₁) = case (prefix₀, reverse components) of+                (Absolute, rest       ) -> (2, rest)+                (Here    , ".." : rest) -> (4, rest)+                (Here    , rest       ) -> (3, rest)+                (Home    , rest       ) -> (5, rest)++        Env x ->+            TList [ TInt 24, TInt 6, TString x ]++        Missing ->+            TList [ TInt 24, TInt 7 ]+  where+    Import {..} = import_++    ImportHashed {..} = importHashed++decode_1_0 :: Term -> Maybe (Expr s Import)+decode_1_0 (TInt n) =+    return (Var (V "_" (fromIntegral n)))+decode_1_0 (TInteger n) =+    return (Var (V "_" n))+decode_1_0 (TString "Natural/build") =+    return NaturalBuild+decode_1_0 (TString "Natural/fold") =+    return NaturalFold+decode_1_0 (TString "Natural/isZero") =+    return NaturalIsZero+decode_1_0 (TString "Natural/even") =+    return NaturalEven+decode_1_0 (TString "Natural/odd") =+    return NaturalOdd+decode_1_0 (TString "Natural/toInteger") =+    return NaturalToInteger+decode_1_0 (TString "Natural/show") =+    return NaturalShow+decode_1_0 (TString "Integer/toDouble") =+    return IntegerToDouble+decode_1_0 (TString "Integer/show") =+    return IntegerShow+decode_1_0 (TString "Double/show") =+    return DoubleShow+decode_1_0 (TString "List/build") =+    return ListBuild+decode_1_0 (TString "List/fold") =+    return ListFold+decode_1_0 (TString "List/length") =+    return ListLength+decode_1_0 (TString "List/head") =+    return ListHead+decode_1_0 (TString "List/last") =+    return ListLast+decode_1_0 (TString "List/indexed") =+    return ListIndexed+decode_1_0 (TString "List/reverse") =+    return ListReverse+decode_1_0 (TString "Optional/fold") =+    return OptionalFold+decode_1_0 (TString "Optional/build") =+    return OptionalBuild+decode_1_0 (TString "Bool") =+    return Bool+decode_1_0 (TString "Optional") =+    return Optional+decode_1_0 (TString "Natural") =+    return Natural+decode_1_0 (TString "Integer") =+    return Integer+decode_1_0 (TString "Double") =+    return Double+decode_1_0 (TString "Text") =+    return Text+decode_1_0 (TString "List") =+    return List+decode_1_0 (TString "Type") =+    return (Const Type)+decode_1_0 (TString "Kind") =+    return (Const Kind)+decode_1_0 (TString x) =+    return (Var (V x 0))+decode_1_0 (TList [ TString x, TInt n ]) =+    return (Var (V x (fromIntegral n)))+decode_1_0 (TList [ TString x, TInteger n ]) =+    return (Var (V x n))+decode_1_0 (TList (TInt 0 : f₁ : xs₁)) = do+    f₀  <- decode_1_0 f₁+    xs₀ <- traverse decode_1_0 xs₁+    return (foldl App f₀ xs₀)+decode_1_0 (TList [ TInt 1, _A₁, b₁ ]) = do+    _A₀ <- decode_1_0 _A₁+    b₀  <- decode_1_0 b₁+    return (Lam "_" _A₀ b₀)+decode_1_0 (TList [ TInt 1, TString x, _A₁, b₁ ]) = do+    _A₀ <- decode_1_0 _A₁+    b₀  <- decode_1_0 b₁+    return (Lam x _A₀ b₀)+decode_1_0 (TList [ TInt 2, _A₁, _B₁ ]) = do+    _A₀ <- decode_1_0 _A₁+    _B₀ <- decode_1_0 _B₁+    return (Pi "_" _A₀ _B₀)+decode_1_0 (TList [ TInt 2, TString x, _A₁, _B₁ ]) = do+    _A₀ <- decode_1_0 _A₁+    _B₀ <- decode_1_0 _B₁+    return (Pi x _A₀ _B₀)+decode_1_0 (TList [ TInt 3, TInt n, l₁, r₁ ]) = do+    l₀ <- decode_1_0 l₁+    r₀ <- decode_1_0 r₁+    op <- case n of+            0  -> return BoolOr+            1  -> return BoolAnd+            2  -> return BoolEQ+            3  -> return BoolNE+            4  -> return NaturalPlus+            5  -> return NaturalTimes+            6  -> return TextAppend+            7  -> return ListAppend+            8  -> return Combine+            9  -> return Prefer+            10 -> return CombineTypes+            11 -> return ImportAlt+            _  -> empty+    return (op l₀ r₀)+decode_1_0 (TList [ TInt 4, _T₁ ]) = do+    _T₀ <- decode_1_0 _T₁+    return (ListLit (Just _T₀) empty)+decode_1_0 (TList (TInt 4 : TNull : xs₁ )) = do+    xs₀ <- traverse decode_1_0 xs₁+    return (ListLit Nothing (Data.Sequence.fromList xs₀))+decode_1_0 (TList [ TInt 5, _T₁ ]) = do+    _T₀ <- decode_1_0 _T₁+    return (OptionalLit _T₀ Nothing)+decode_1_0 (TList [ TInt 5, _T₁, t₁ ]) = do+    _T₀ <- decode_1_0 _T₁+    t₀  <- decode_1_0 t₁+    return (OptionalLit _T₀ (Just t₀))+decode_1_0 (TList [ TInt 6, t₁, u₁ ]) = do+    t₀ <- decode_1_0 t₁+    u₀ <- decode_1_0 u₁+    return (Merge t₀ u₀ Nothing)+decode_1_0 (TList [ TInt 6, t₁, u₁, _T₁ ]) = do+    t₀  <- decode_1_0 t₁+    u₀  <- decode_1_0 u₁+    _T₀ <- decode_1_0 _T₁+    return (Merge t₀ u₀ (Just _T₀))+decode_1_0 (TList [ TInt 7, TMap xTs₁ ]) = do+    let process (TString x, _T₁) = do+            _T₀ <- decode_1_0 _T₁++            return (x, _T₀)+        process _ =+            empty++    xTs₀ <- traverse process xTs₁++    return (Record (Data.HashMap.Strict.InsOrd.fromList xTs₀))+decode_1_0 (TList [ TInt 8, TMap xts₁ ]) = do+    let process (TString x, t₁) = do+           t₀ <- decode_1_0 t₁++           return (x, t₀)+        process _ =+            empty++    xts₀ <- traverse process xts₁++    return (RecordLit (Data.HashMap.Strict.InsOrd.fromList xts₀))+decode_1_0 (TList [ TInt 9, t₁, TString x ]) = do+    t₀ <- decode_1_0 t₁++    return (Field t₀ x)+decode_1_0 (TList (TInt 10 : t₁ : xs₁)) = do+    t₀ <- decode_1_0 t₁++    let process (TString x) = return x+        process  _          = empty++    xs₀ <- traverse process xs₁++    return (Project t₀ (Data.Set.fromList xs₀))+decode_1_0 (TList [ TInt 11, TMap xTs₁ ]) = do+    let process (TString x, _T₁) = do+            _T₀ <- decode_1_0 _T₁++            return (x, _T₀)+        process _ =+            empty++    xTs₀ <- traverse process xTs₁++    return (Union (Data.HashMap.Strict.InsOrd.fromList xTs₀))+decode_1_0 (TList [ TInt 12, TString x, t₁, TMap yTs₁ ]) = do+    t₀ <- decode_1_0 t₁++    let process (TString y, _T₁) = do+            _T₀ <- decode_1_0 _T₁++            return (y, _T₀)+        process _ =+            empty++    yTs₀ <- traverse process yTs₁++    return (UnionLit x t₀ (Data.HashMap.Strict.InsOrd.fromList yTs₀))+decode_1_0 (TList [ TInt 13, u₁ ]) = do+    u₀ <- decode_1_0 u₁++    return (Constructors u₀)+decode_1_0 (TBool b) = do+    return (BoolLit b)+decode_1_0 (TList [ TInt 14, t₁, l₁, r₁ ]) = do+    t₀ <- decode_1_0 t₁+    l₀ <- decode_1_0 l₁+    r₀ <- decode_1_0 r₁++    return (BoolIf t₀ l₀ r₀)+decode_1_0 (TList [ TInt 15, TInt n ]) = do+    return (NaturalLit (fromIntegral n))+decode_1_0 (TList [ TInt 15, TInteger n ]) = do+    return (NaturalLit (fromInteger n))+decode_1_0 (TList [ TInt 16, TInt n ]) = do+    return (IntegerLit (fromIntegral n))+decode_1_0 (TList [ TInt 16, TInteger n ]) = do+    return (IntegerLit n)+decode_1_0 (TList [ TInt 17, TTagged 4 (TList [ TInt exponent, TInteger mantissa ]) ]) = do+    return (DoubleLit (Data.Scientific.scientific mantissa exponent))+decode_1_0 (TList [ TInt 17, TTagged 4 (TList [ TInt exponent, TInt mantissa ]) ]) = do+    return (DoubleLit (Data.Scientific.scientific (fromIntegral mantissa) exponent))+decode_1_0 (TList (TInt 18 : xs)) = do+    let process (TString x : y₁ : zs) = do+            y₀ <- decode_1_0 y₁++            ~(xys, z) <- process zs++            return ((x, y₀) : xys, z)+        process [ TString z ] = do+            return ([], z)+        process _ = do+            empty++    (xys, z) <- process xs++    return (TextLit (Chunks xys z))+decode_1_0 (TList (TInt 24 : TInt n : xs)) = do+    let remote scheme = do+            let process [ TString file, q, f ] = do+                    query <- case q of+                        TNull     -> return Nothing+                        TString x -> return (Just x)+                        _         -> empty+                    fragment <- case f of+                        TNull     -> return Nothing+                        TString x -> return (Just x)+                        _         -> empty+                    return ([], file, query, fragment)+                process (TString path : ys) = do+                    (paths, file, query, fragment) <- process ys+                    return (path : paths, file, query, fragment)+                process _ = do+                    empty++            (authority, paths, file, query, fragment) <- case xs of+                TString authority : ys -> do+                    (paths, file, query, fragment) <- process ys+                    return (authority, paths, file, query, fragment)+                _                      -> empty++            let components = reverse paths+            let directory  = Directory {..}+            let path       = File {..}+            let headers    = Nothing++            return (Remote (URL {..}))++    let local prefix = do+            let process [ TString file ] = do+                    return ([], file)+                process (TString path : ys) = do+                    (paths, file) <- process ys+                    return (path : paths, file)+                process _ =+                    empty++            (paths, file) <- process xs++            let finalPaths = case n of+                    4 -> ".." : paths+                    _ -> paths++            let components = reverse finalPaths+            let directory  = Directory {..}++            return (Local prefix (File {..}))++    let env = do+            case xs of+                [ TString x ] -> return (Env x)+                _             -> empty++    let missing = return Missing++    importType <- case n of+        0 -> remote HTTP+        1 -> remote HTTPS+        2 -> local Absolute+        3 -> local Here+        4 -> local Here+        5 -> local Home+        6 -> env+        7 -> missing+        _ -> empty++    let hash         = Nothing+    let importHashed = ImportHashed {..}+    let importMode   = Code+    return (Embed (Import {..}))+decode_1_0 (TList [ TInt 25, TString x, a₁, b₁ ]) = do+    a₀ <- decode_1_0 a₁+    b₀ <- decode_1_0 b₁+    return (Let x Nothing a₀ b₀)+decode_1_0 (TList [ TInt 25, TString x, _A₁, a₁, b₁ ]) = do+    _A₀ <- decode_1_0 _A₁+    a₀  <- decode_1_0 a₁+    b₀  <- decode_1_0 b₁+    return (Let x (Just _A₀) a₀ b₀)+decode_1_0 (TList [ TInt 26, t₁, _T₁ ]) = do+    t₀  <- decode_1_0 t₁+    _T₀ <- decode_1_0 _T₁+    return (Annot t₀ _T₀)+decode_1_0 _ =+    empty++-- | Encode a Dhall expression using protocol version @1.0@+encodeWithVersion_1_0 :: Expr s Import -> Term+encodeWithVersion_1_0 expression =+    TList [ TString "1.0", encode_1_0 expression ]++{-| Decode a Dhall expression++    This auto-detects whiich protocol version to decode based on the included+    protocol version string in the decoded expression+-}+decode :: Term -> Either DecodingFailure (Expr s Import)+decode term = do+    (version, subTerm) <- case term of+        TList [ TString version, subTerm ] ->+            return (version, subTerm)+        _ ->+            fail ("Cannot decode the version from this decoded CBOR expression: " <> show term)++    maybeExpression <- case version of+        "1.0" -> do+            return (decode_1_0 subTerm)+        _ -> do+            fail ("This decoded version is not supported: " <> Data.Text.unpack version)++    case maybeExpression of+        Nothing ->+            fail ("This decoded CBOR expression does not represent a valid Dhall expression: " <> show subTerm)+        Just expression ->+            return expression++-- | Encode a Dhall expression using the specified `ProtocolVersion`+encode :: ProtocolVersion -> Expr s Import -> Term+encode V_1_0 = encodeWithVersion_1_0++data DecodingFailure+    = CannotDecodeProtocolVersionString Term+    | UnsupportedProtocolVersionString Text+    | CBORIsNotDhall Term+    deriving (Eq)++instance Exception DecodingFailure++_ERROR :: String+_ERROR = "\ESC[1;31mError\ESC[0m"++instance Show DecodingFailure where+    show (CannotDecodeProtocolVersionString term) =+            _ERROR <> ": Cannot decode version string\n"+        <>  "\n"+        <>  "This CBOR expression does not contain a protocol version string in any\n"+        <>  "recognizable format\n"+        <>  "\n"+        <>  "↳ " <> show term <> "\n"+    show (UnsupportedProtocolVersionString version) =+            _ERROR <> ": Unsupported version string\n"+        <>  "\n"+        <>  "The encoded Dhall expression was tagged with a protocol version string of:\n"+        <>  "\n"+        <>  "↳ " <> show version <> "\n"+        <>  "\n"+        <>  "... but this implementation cannot decode that protocol version\n"+        <>  "\n"+        <>  "Some common reasons why you might get this error:\n"+        <>  "\n"+        <>  "● You are using an old version of the interpreter and need to upgrade\n"+    show (CBORIsNotDhall term) =+            _ERROR <> ": Cannot decode CBOR to Dhall\n"+        <>  "\n"+        <>  "The following CBOR expression does not encode a valid Dhall expression\n"+        <>  "\n"+        <>  "↳ " <> show term <> "\n"
src/Dhall/Core.hs view
@@ -1,8 +1,7 @@ {-# LANGUAGE BangPatterns       #-} {-# LANGUAGE CPP                #-} {-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveFoldable     #-}-{-# LANGUAGE DeriveFunctor      #-}+{-# LANGUAGE DeriveGeneric      #-} {-# LANGUAGE DeriveTraversable  #-} {-# LANGUAGE OverloadedStrings  #-} {-# LANGUAGE RankNTypes         #-}@@ -26,7 +25,9 @@     , ImportHashed(..)     , ImportMode(..)     , ImportType(..)+    , URL(..)     , Path+    , Scheme(..)     , Var(..)     , Chunks(..)     , Expr(..)@@ -63,6 +64,7 @@ import Data.Bifunctor (Bifunctor(..)) import Data.Data (Data) import Data.Foldable+import Data.Hashable (Hashable) import Data.HashMap.Strict.InsOrd (InsOrdHashMap) import Data.HashSet (HashSet) import Data.String (IsString(..))@@ -74,13 +76,16 @@ import Data.Text.Prettyprint.Doc (Pretty) import Data.Traversable import {-# SOURCE #-} Dhall.Pretty.Internal+import GHC.Generics (Generic) import Numeric.Natural (Natural) import Prelude hiding (succ)  import qualified Control.Monad import qualified Crypto.Hash+import qualified Data.List import qualified Data.HashMap.Strict.InsOrd import qualified Data.HashSet+import qualified Data.Ord import qualified Data.Sequence import qualified Data.Set import qualified Data.Text@@ -103,7 +108,7 @@     Note that Dhall does not support functions from terms to types and therefore     Dhall is not a dependently typed language -}-data Const = Type | Kind deriving (Show, Eq, Data, Bounded, Enum)+data Const = Type | Kind deriving (Show, Eq, Data, Bounded, Enum, Generic)  instance Pretty Const where     pretty = Pretty.unAnnotate . prettyConst@@ -115,7 +120,7 @@     @Directory { components = [ "baz", "bar", "foo" ] }@ -} newtype Directory = Directory { components :: [Text] }-    deriving (Eq, Ord, Show)+    deriving (Eq, Generic, Ord, Show)  instance Semigroup Directory where     Directory components₀ <> Directory components₁ =@@ -133,7 +138,7 @@ data File = File     { directory :: Directory     , file      :: Text-    } deriving (Eq, Ord, Show)+    } deriving (Eq, Generic, Ord, Show)  instance Pretty File where     pretty (File {..}) = Pretty.pretty directory <> "/" <> Pretty.pretty file@@ -142,6 +147,7 @@     File directory₀ _ <> File directory₁ file =         File (directory₀ <> directory₁) file +-- | The beginning of a file path which anchors subsequent path components data FilePrefix     = Absolute     -- ^ Absolute path@@ -149,29 +155,40 @@     -- ^ Path relative to @.@     | Home     -- ^ Path relative to @~@-    deriving (Eq, Ord, Show)+    deriving (Eq, Generic, Ord, Show)  instance Pretty FilePrefix where     pretty Absolute = ""     pretty Here     = "."     pretty Home     = "~" +data Scheme = HTTP | HTTPS deriving (Eq, Generic, Ord, Show)++data URL = URL+    { scheme    :: Scheme+    , authority :: Text+    , path      :: File+    , query     :: Maybe Text+    , fragment  :: Maybe Text+    , headers   :: Maybe ImportHashed+    } deriving (Eq, Generic, Ord, Show)+ -- | The type of import (i.e. local vs. remote vs. environment) data ImportType     = Local FilePrefix File     -- ^ Local path-    | URL Text File Text (Maybe ImportHashed)+    | Remote URL     -- ^ URL of remote resource and optional headers stored in an import     | Env  Text     -- ^ Environment variable     | Missing-    deriving (Eq, Ord, Show)+    deriving (Eq, Generic, Ord, Show)  instance Semigroup ImportType where     Local prefix file₀ <> Local Here file₁ = Local prefix (file₀ <> file₁) -    URL prefix file₀ suffix headers <> Local Here file₁ =-        URL prefix (file₀ <> file₁) suffix headers+    Remote (URL { path = path₀, ..}) <> Local Here path₁ =+        Remote (URL { path = path₀ <> path₁, ..})      _ <> import₁ =         import₁@@ -180,26 +197,41 @@     pretty (Local prefix file) =         Pretty.pretty prefix <> Pretty.pretty file -    pretty (URL prefix file suffix headers) =-            Pretty.pretty prefix-        <>  Pretty.pretty file-        <>  Pretty.pretty suffix+    pretty (Remote (URL {..})) =+            schemeDoc+        <>  "://"+        <>  Pretty.pretty authority+        <>  Pretty.pretty path+        <>  queryDoc+        <>  fragmentDoc         <>  foldMap prettyHeaders headers       where         prettyHeaders h = " using " <> Pretty.pretty h +        schemeDoc = case scheme of+            HTTP  -> "http"+            HTTPS -> "https"++        queryDoc = case query of+            Nothing -> ""+            Just q  -> "?" <> Pretty.pretty q++        fragmentDoc = case fragment of+            Nothing -> ""+            Just f  -> "#" <> Pretty.pretty f+     pretty (Env env) = "env:" <> Pretty.pretty env      pretty Missing = "missing"  -- | How to interpret the import's contents (i.e. as Dhall code or raw text)-data ImportMode = Code | RawText deriving (Eq, Ord, Show)+data ImportMode = Code | RawText deriving (Eq, Generic, Ord, Show)  -- | A `ImportType` extended with an optional hash for semantic integrity checks data ImportHashed = ImportHashed     { hash       :: Maybe (Crypto.Hash.Digest SHA256)     , importType :: ImportType-    } deriving (Eq, Ord, Show)+    } deriving (Eq, Generic, Ord, Show)  instance Semigroup ImportHashed where     ImportHashed _ importType₀ <> ImportHashed hash importType₁ =@@ -215,7 +247,7 @@ data Import = Import     { importHashed :: ImportHashed     , importMode   :: ImportMode-    } deriving (Eq, Ord, Show)+    } deriving (Eq, Generic, Ord, Show)  instance Semigroup Import where     Import importHashed₀ _ <> Import importHashed₁ code =@@ -267,7 +299,7 @@     appear as a numeric suffix. -} data Var = V Text !Integer-    deriving (Data, Eq, Show)+    deriving (Data, Generic, Eq, Show)  instance IsString Var where     fromString str = V (fromString str) 0@@ -409,7 +441,7 @@     | ImportAlt (Expr s a) (Expr s a)     -- | > Embed import                             ~  import     | Embed a-    deriving (Functor, Foldable, Traversable, Show, Eq, Data)+    deriving (Functor, Foldable, Generic, Traversable, Show, Eq, Data)  instance Applicative (Expr s) where     pure = Embed@@ -555,7 +587,7 @@  -- | The body of an interpolated @Text@ literal data Chunks s a = Chunks [(Text, Expr s a)] Text-    deriving (Functor, Foldable, Traversable, Show, Eq, Data)+    deriving (Functor, Foldable, Generic, Traversable, Show, Eq, Data)  instance Data.Semigroup.Semigroup (Chunks s a) where     Chunks xysL zL <> Chunks         []    zR =@@ -962,62 +994,73 @@ -- and `subst` does nothing to a closed expression subst _ _ (Embed p) = Embed p -{-| α-normalize an expression by renaming all variables to @\"_\"@ and using-    De Bruijn indices to distinguish them+{-| α-normalize an expression by renaming all bound variables to @\"_\"@ and+    using De Bruijn indices to distinguish them++>>> alphaNormalize (Lam "a" (Const Type) (Lam "b" (Const Type) (Lam "x" "a" (Lam "y" "b" "x"))))+Lam "_" (Const Type) (Lam "_" (Const Type) (Lam "_" (Var (V "_" 1)) (Lam "_" (Var (V "_" 1)) (Var (V "_" 1)))))++    α-normalization does not affect free variables:++>>> alphaNormalize "x"+Var (V "x" 0)+ -} alphaNormalize :: Expr s a -> Expr s a alphaNormalize (Const c) =     Const c alphaNormalize (Var v) =     Var v+alphaNormalize (Lam "_" _A₀ b₀) =+    Lam "_" _A₁ b₁+  where+    _A₁ = alphaNormalize _A₀+    b₁  = alphaNormalize b₀ alphaNormalize (Lam x _A₀ b₀) =-    Lam "_" _A₁ b₃+    Lam "_" _A₁ b₄   where     _A₁ = alphaNormalize _A₀ -    v₀ = Var (V "_" 0)-    v₁ = shift 1 (V x 0) v₀-    b₁ = subst (V x 0) v₁ b₀-    b₂ = shift (-1) (V x 0) b₁-    b₃ = alphaNormalize b₂+    b₁ = shift 1 (V "_" 0) b₀+    b₂ = subst (V x 0) (Var (V "_" 0)) b₁+    b₃ = shift (-1) (V x 0) b₂+    b₄ = alphaNormalize b₃+alphaNormalize (Pi "_" _A₀ _B₀) =+    Pi "_" _A₁ _B₁+  where+    _A₁ = alphaNormalize _A₀+    _B₁ = alphaNormalize _B₀ alphaNormalize (Pi x _A₀ _B₀) =-    Pi "_" _A₁ _B₃+    Pi "_" _A₁ _B₄   where     _A₁ = alphaNormalize _A₀ -    v₀  = Var (V "_" 0)-    v₁  = shift 1 (V x 0) v₀-    _B₁ = subst (V x 0) v₁ _B₀-    _B₂ = shift (-1) (V x 0) _B₁-    _B₃ = alphaNormalize _B₂+    _B₁ = shift 1 (V "_" 0) _B₀+    _B₂ = subst (V x 0) (Var (V "_" 0)) _B₁+    _B₃ = shift (-1) (V x 0) _B₂+    _B₄ = alphaNormalize _B₃ alphaNormalize (App f₀ a₀) =     App f₁ a₁   where     f₁ = alphaNormalize f₀      a₁ = alphaNormalize a₀-alphaNormalize (Let x (Just _A₀) a₀ b₀) =-    Let "_" (Just _A₁) a₁ b₃+alphaNormalize (Let "_" mA₀ a₀ b₀) =+    Let "_" mA₁ a₁ b₁   where-    _A₁ = alphaNormalize _A₀--    a₁ = alphaNormalize a₀--    v₀ = Var (V "_" 0)-    v₁ = shift 1 (V x 0) v₀-    b₁ = subst (V x 0) v₁ b₀-    b₂ = shift (-1) (V x 0) b₁-    b₃ = alphaNormalize b₂-alphaNormalize (Let x Nothing a₀ b₀) =-    Let "_" Nothing a₁ b₃+    mA₁ = fmap alphaNormalize mA₀+    a₁  =      alphaNormalize a₀+    b₁  =      alphaNormalize b₀+alphaNormalize (Let x mA₀ a₀ b₀) =+    Let "_" mA₁ a₁ b₄   where-    a₁ = alphaNormalize a₀+    mA₁ = fmap alphaNormalize mA₀+    a₁  =      alphaNormalize a₀ -    v₀ = Var (V "_" 0)-    v₁ = shift 1 (V x 0) v₀-    b₁ = subst (V x 0) v₁ b₀-    b₂ = shift (-1) (V x 0) b₁-    b₃ = alphaNormalize b₂+    b₁ = shift 1 (V "_" 0) b₀+    b₂ = subst (V x 0) (Var (V "_" 0)) b₁+    b₃ = shift (-1) (V x 0) b₂+    b₄ = alphaNormalize b₃ alphaNormalize (Annot t₀ _T₀) =     Annot t₁ _T₁   where@@ -1331,6 +1374,12 @@ denote (ImportAlt a b       ) = ImportAlt (denote a) (denote b) denote (Embed a             ) = Embed a +sortMap :: (Ord k, Hashable k) => InsOrdHashMap k v -> InsOrdHashMap k v+sortMap =+      Data.HashMap.Strict.InsOrd.fromList+    . Data.List.sortBy (Data.Ord.comparing fst)+    . Data.HashMap.Strict.InsOrd.toList+ {-| Reduce an expression to its normal form, performing beta reduction and applying     any custom definitions. @@ -1606,16 +1655,16 @@         es' = fmap loop es     OptionalFold -> OptionalFold     OptionalBuild -> OptionalBuild-    Record kts -> Record kts'+    Record kts -> Record (sortMap kts')       where         kts' = fmap loop kts-    RecordLit kvs -> RecordLit kvs'+    RecordLit kvs -> RecordLit (sortMap kvs')       where         kvs' = fmap loop kvs-    Union kts -> Union kts'+    Union kts -> Union (sortMap kts')       where         kts' = fmap loop kts-    UnionLit k v kvs -> UnionLit k v' kvs'+    UnionLit k v kvs -> UnionLit k v' (sortMap kvs')       where         v'   =      loop v         kvs' = fmap loop kvs@@ -1729,7 +1778,7 @@   -- | Quickly check if an expression is in normal form-isNormalized :: Expr s a -> Bool+isNormalized :: Eq a => Expr s a -> Bool isNormalized e = case denote e of     Const _ -> True     Var _ -> True@@ -1773,37 +1822,32 @@     Annot _ _ -> False     Bool -> True     BoolLit _ -> True-    BoolAnd x y -> isNormalized x && isNormalized y &&-        case x of-            BoolLit _ ->-                case y of-                    BoolLit _ -> False-                    _ -> True-            _ -> True-    BoolOr x y -> isNormalized x && isNormalized y &&-        case x of-            BoolLit _ ->-                case y of-                    BoolLit _ -> False-                    _ -> True-            _ -> True-    BoolEQ x y -> isNormalized x && isNormalized y &&-        case x of-            BoolLit _ ->-                case y of-                    BoolLit _ -> False-                    _ -> True-            _ -> True-    BoolNE x y -> isNormalized x && isNormalized y &&-        case x of-            BoolLit _ ->-                case y of-                    BoolLit _ -> False-                    _ -> True-            _ -> True-    BoolIf b true false -> isNormalized b && case b of-        BoolLit _ -> False-        _         -> isNormalized true && isNormalized false+    BoolAnd x y -> isNormalized x && isNormalized y && decide x y+      where+        decide (BoolLit _)  _          = False+        decide  _          (BoolLit _) = False+        decide  l           r          = not (judgmentallyEqual l r)+    BoolOr x y -> isNormalized x && isNormalized y && decide x y+      where+        decide (BoolLit _)  _          = False+        decide  _          (BoolLit _) = False+        decide  l           r          = not (judgmentallyEqual l r)+    BoolEQ x y -> isNormalized x && isNormalized y && decide x y+      where+        decide (BoolLit True)  _             = False+        decide  _             (BoolLit True) = False+        decide  l              r             = not (judgmentallyEqual l r)+    BoolNE x y -> isNormalized x && isNormalized y && decide x y+      where+        decide (BoolLit False)  _               = False+        decide  _              (BoolLit False ) = False+        decide  l               r               = not (judgmentallyEqual l r)+    BoolIf x y z ->+        isNormalized x && isNormalized y && isNormalized z && decide x y z+      where+        decide (BoolLit _)  _              _              = False+        decide  _          (BoolLit True) (BoolLit False) = False+        decide  _           l              r              = not (judgmentallyEqual l r)     Natural -> True     NaturalLit _ -> True     NaturalFold -> True@@ -1813,20 +1857,20 @@     NaturalOdd -> True     NaturalShow -> True     NaturalToInteger -> True-    NaturalPlus x y -> isNormalized x && isNormalized y &&-        case x of-            NaturalLit _ ->-                case y of-                    NaturalLit _ -> False-                    _ -> True-            _ -> True-    NaturalTimes x y -> isNormalized x && isNormalized y &&-        case x of-            NaturalLit _ ->-                case y of-                    NaturalLit _ -> False-                    _ -> True-            _ -> True+    NaturalPlus x y -> isNormalized x && isNormalized y && decide x y+      where+        decide (NaturalLit 0)  _             = False+        decide  _             (NaturalLit 0) = False+        decide (NaturalLit _) (NaturalLit _) = False+        decide  _              _             = True+    NaturalTimes x y -> isNormalized x && isNormalized y && decide x y+      where+        decide (NaturalLit 0)  _             = False+        decide  _             (NaturalLit 0) = False+        decide (NaturalLit 1)  _             = False+        decide  _             (NaturalLit 1) = False+        decide (NaturalLit _) (NaturalLit _) = False+        decide  _              _             = True     Integer -> True     IntegerLit _ -> True     IntegerShow -> True@@ -1835,23 +1879,29 @@     DoubleLit _ -> True     DoubleShow -> True     Text -> True-    TextLit (Chunks xys _) -> all (all isNormalized) xys-    TextAppend x y -> isNormalized x && isNormalized y &&-        case x of-            TextLit _ ->-                case y of-                    TextLit _ -> False-                    _ -> True-            _ -> True+    TextLit (Chunks [("", _)] "") -> False+    TextLit (Chunks xys _) -> all (all check) xys+      where+        check y = isNormalized y && case y of+            TextLit _ -> False+            _         -> True+    TextAppend x y -> isNormalized x && isNormalized y && decide x y+      where+        isEmpty (Chunks [] "") = True+        isEmpty  _             = False++        decide (TextLit m)  _          | isEmpty m = False+        decide  _          (TextLit n) | isEmpty n = False+        decide (TextLit _) (TextLit _)             = False+        decide  _           _                      = True     List -> True     ListLit t es -> all isNormalized t && all isNormalized es-    ListAppend x y -> isNormalized x && isNormalized y &&-        case x of-            ListLit _ _ ->-                case y of-                    ListLit _ _ -> False-                    _ -> True-            _ -> True+    ListAppend x y -> isNormalized x && isNormalized y && decide x y+      where+        decide (ListLit _ m)  _            | Data.Sequence.null m = False+        decide  _            (ListLit _ n) | Data.Sequence.null n = False+        decide (ListLit _ _) (ListLit _ _)                        = False+        decide  _             _                                   = True     ListBuild -> True     ListFold -> True     ListLength -> True@@ -1867,28 +1917,25 @@     RecordLit kvs -> all isNormalized kvs     Union kts -> all isNormalized kts     UnionLit _ v kvs -> isNormalized v && all isNormalized kvs-    Combine x y -> isNormalized x && isNormalized y && combine+    Combine x y -> isNormalized x && isNormalized y && decide x y       where-        combine = case x of-            RecordLit _ -> case y of-                RecordLit _ -> False-                _ -> True-            _ -> True-    CombineTypes x y -> isNormalized x && isNormalized y && combine+        decide (RecordLit m) _ | Data.HashMap.Strict.InsOrd.null m = False+        decide _ (RecordLit n) | Data.HashMap.Strict.InsOrd.null n = False+        decide (RecordLit _) (RecordLit _) = False+        decide  _ _ = True+    CombineTypes x y -> isNormalized x && isNormalized y && decide x y       where-        combine = case x of-            Record _ -> case y of-                Record _ -> False-                _ -> True-            _ -> True-    Prefer x y -> isNormalized x && isNormalized y && combine+        decide (Record m) _ | Data.HashMap.Strict.InsOrd.null m = False+        decide _ (Record n) | Data.HashMap.Strict.InsOrd.null n = False+        decide (Record _) (Record _) = False+        decide  _ _ = True+    Prefer x y -> isNormalized x && isNormalized y && decide x y       where-        combine = case x of-            RecordLit _ -> case y of-                RecordLit _ -> False-                _ -> True-            _ -> True-    Merge x y t -> isNormalized x && isNormalized y && any isNormalized t &&+        decide (RecordLit m) _ | Data.HashMap.Strict.InsOrd.null m = False+        decide _ (RecordLit n) | Data.HashMap.Strict.InsOrd.null n = False+        decide (RecordLit _) (RecordLit _) = False+        decide  _ _ = True+    Merge x y t -> isNormalized x && isNormalized y && all isNormalized t &&         case x of             RecordLit kvsX ->                 case y of@@ -2007,3 +2054,4 @@         , "Optional/build"         , "Optional/fold"         ]+
src/Dhall/Diff.hs view
@@ -453,14 +453,7 @@     <>  " "     <>  ignore skeleton (Pi {}) =-        forall-    <>  lparen-    <>  ignore-    <>  " "-    <>  colon-    <>  " "-    <>  ignore-    <>  rparen+        ignore     <>  " "     <>  rarrow     <>  " "@@ -723,13 +716,20 @@     docs (Pi aL bL cL) (Pi aR bR cR) =         Data.List.NonEmpty.cons (align doc) (docs cL cR)       where-        doc =   forall+        doc | same docA && same docB = ignore+            | otherwise =+                forall             <>  lparen-            <>  format " " (diffLabel aL aR)+            <>  format " " docA             <>  colon             <>  " "-            <>  format mempty (diffExpression bL bR)+            <>  format mempty docB             <>  rparen+          where+            docA = diffLabel aL aR++            docB = diffExpression bL bR+     docs aL aR = pure (diffExpression aL aR) diffExpression l@(Pi {}) r =     mismatch l r
src/Dhall/Format.hs view
@@ -1,7 +1,12 @@ {-# LANGUAGE OverloadedStrings #-} -module Dhall.Format ( format ) where+-- | This module contains the implementation of the @dhall format@ subcommand +module Dhall.Format+    ( -- * Format+      format+    ) where+ import Dhall.Parser (exprAndHeaderFromText) import Dhall.Pretty (annToAnsiStyle, prettyExpr, layoutOpts) @@ -14,7 +19,12 @@ import qualified System.Console.ANSI import qualified System.IO -format :: Maybe FilePath -> IO ()+-- | Implementation of the @dhall format@ subcommand+format+    :: Maybe FilePath+    -- ^ Modify file in-place if present, otherwise read from @stdin@ and write+    --   to @stdout@+    -> IO () format inplace = do         case inplace of             Just file -> do
src/Dhall/Freeze.hs view
@@ -1,34 +1,57 @@ {-# LANGUAGE OverloadedStrings #-} -module Dhall.Freeze (+-- | This module contains the implementation of the @dhall freeze@ subcommand++module Dhall.Freeze+    ( -- * Freeze       freeze     , hashImport     ) where -import Dhall.Core-import Dhall.Import (load, hashExpression)-import Dhall.Parser (exprAndHeaderFromText, Src)-import Dhall.Pretty (annToAnsiStyle, layoutOpts)--import System.Console.ANSI (hSupportsANSI) import Data.Monoid ((<>)) import Data.Maybe (fromMaybe) import Data.Text+import Dhall.Binary (ProtocolVersion(..))+import Dhall.Core (Expr(..), Import(..), ImportHashed(..))+import Dhall.Import (hashExpression, protocolVersion)+import Dhall.Parser (exprAndHeaderFromText, Src)+import Dhall.Pretty (annToAnsiStyle, layoutOpts)+import Lens.Family (set)+import System.Console.ANSI (hSupportsANSI) +import qualified Control.Exception+import qualified Control.Monad.Trans.State.Strict          as State import qualified Data.Text.Prettyprint.Doc                 as Pretty import qualified Data.Text.Prettyprint.Doc.Render.Terminal as Pretty-import qualified Control.Exception import qualified Data.Text.IO+import qualified Dhall.Core+import qualified Dhall.Import+import qualified Dhall.TypeCheck import qualified System.IO  readInput :: Maybe FilePath -> IO Text readInput = maybe Data.Text.IO.getContents Data.Text.IO.readFile -hashImport :: Import -> IO Import-hashImport import_ = do-    expression <- Dhall.Import.load (Embed import_)-    let expressionHash = Just (Dhall.Import.hashExpression expression)+-- | Retrieve an `Import` and update the hash to match the latest contents+hashImport :: ProtocolVersion -> Import -> IO Import+hashImport _protocolVersion import_ = do+    let status =+            set protocolVersion _protocolVersion (Dhall.Import.emptyStatus ".")++    expression <- State.evalStateT (Dhall.Import.loadWith (Embed import_)) status++    case Dhall.TypeCheck.typeOf expression of+        Left  exception -> Control.Exception.throwIO exception+        Right _         -> return ()++    let normalizedExpression =+            Dhall.Core.alphaNormalize (Dhall.Core.normalize expression)++    let expressionHash =+            Just (Dhall.Import.hashExpression _protocolVersion normalizedExpression)+     let newImportHashed = (importHashed import_) { hash = expressionHash }+     return $ import_ { importHashed = newImportHashed }  parseExpr :: String -> Text -> IO (Text, Expr Src Import)@@ -37,11 +60,6 @@         Left err -> Control.Exception.throwIO err         Right x  -> return x -freezeExpr :: (Text, Expr s Import) -> IO (Text, Expr s Import)-freezeExpr (t, e) = do-    e' <- traverse hashImport e-    return (t, e')- writeExpr :: Maybe FilePath -> (Text, Expr s Import) -> IO () writeExpr inplace (header, expr) = do     let doc = Pretty.pretty header <> Pretty.pretty expr@@ -60,9 +78,17 @@                else                  Pretty.renderIO System.IO.stdout (Pretty.layoutSmart layoutOpts (Pretty.unAnnotate doc))  -freeze :: Maybe FilePath -> IO ()-freeze inplace = do-    expr <- readInput inplace-    parseExpr srcInfo expr >>= freezeExpr >>= writeExpr inplace+-- | Implementation of the @dhall freeze@ subcommand+freeze+    :: Maybe FilePath+    -- ^ Modify file in-place if present, otherwise read from @stdin@ and write+    --   to @stdout@+    -> ProtocolVersion+    -> IO ()+freeze inplace _protocolVersion = do+    text <- readInput inplace+    (header, parsedExpression) <- parseExpr srcInfo text+    frozenExpression <- traverse (hashImport _protocolVersion) parsedExpression+    writeExpr inplace (header, frozenExpression)         where             srcInfo = fromMaybe "(stdin)" inplace
src/Dhall/Hash.hs view
@@ -1,26 +1,44 @@ {-# LANGUAGE OverloadedStrings #-} -module Dhall.Hash ( hash ) where+-- | This module contains the implementation of the @dhall hash@ subcommand +module Dhall.Hash+    ( -- * Hash+      hash+    ) where++import Dhall.Binary (ProtocolVersion) import Dhall.Parser (exprFromText)-import Dhall.Import (hashExpressionToCode, load)+import Dhall.Import (hashExpressionToCode, protocolVersion)+import Lens.Family (set) +import qualified Control.Monad.Trans.State.Strict as State import qualified Control.Exception+import qualified Dhall.Core+import qualified Dhall.Import import qualified Dhall.TypeCheck import qualified Data.Text.IO -hash :: IO ()-hash = do-        inText <- Data.Text.IO.getContents+-- | Implementation of the @dhall hash@ subcommand+hash :: ProtocolVersion -> IO ()+hash _protocolVersion = do+    inText <- Data.Text.IO.getContents -        expr <- case exprFromText "(stdin)" inText of-            Left  err  -> Control.Exception.throwIO err-            Right expr -> return expr+    parsedExpression <- case exprFromText "(stdin)" inText of+        Left  exception        -> Control.Exception.throwIO exception+        Right parsedExpression -> return parsedExpression -        expr' <- load expr+    let status =+            set protocolVersion _protocolVersion (Dhall.Import.emptyStatus ".") -        _ <- case Dhall.TypeCheck.typeOf expr' of-            Left  err -> Control.Exception.throwIO err-            Right _   -> return ()+    resolvedExpression <- State.evalStateT (Dhall.Import.loadWith parsedExpression) status -        Data.Text.IO.putStrLn (hashExpressionToCode expr')+    case Dhall.TypeCheck.typeOf resolvedExpression of+        Left  exception -> Control.Exception.throwIO exception+        Right _         -> return ()++    let normalizedExpression =+            Dhall.Core.alphaNormalize (Dhall.Core.normalize resolvedExpression)++    Data.Text.IO.putStrLn+        (hashExpressionToCode _protocolVersion normalizedExpression)
src/Dhall/Import.hs view
@@ -102,12 +102,17 @@       exprFromImport     , load     , loadWith-    , loadDirWith-    , loadWithContext     , hashExpression     , hashExpressionToCode-    , Status(..)+    , Status     , emptyStatus+    , stack+    , cache+    , manager+    , protocolVersion+    , normalizer+    , startingContext+    , resolver     , Cycle(..)     , ReferentiallyOpaque(..)     , Imported(..)@@ -117,8 +122,11 @@     , MissingImports(..)     ) where +import Control.Applicative (Alternative(..))+import Codec.CBOR.Term (Term) import Control.Applicative (empty) import Control.Exception (Exception, SomeException, throwIO, toException)+import Control.Monad (guard) import Control.Monad.Catch (throwM, MonadCatch(catch), catches, Handler(..)) import Control.Monad.IO.Class (MonadIO(..)) import Control.Monad.Trans.State.Strict (StateT)@@ -133,6 +141,7 @@ #endif import Data.Typeable (Typeable) import System.FilePath ((</>))+import Dhall.Binary (ProtocolVersion(..)) import Dhall.Core     ( Expr(..)     , Chunks(..)@@ -143,6 +152,9 @@     , ImportType(..)     , ImportMode(..)     , Import(..)+    , ReifiedNormalizer(..)+    , Scheme(..)+    , URL(..)     ) #ifdef MIN_VERSION_http_client import Dhall.Import.HTTP@@ -153,25 +165,28 @@ import Dhall.TypeCheck (X(..)) import Lens.Family.State.Strict (zoom) -import qualified Control.Monad.Trans.State.Strict        as State+import qualified Codec.Serialise+import qualified Control.Monad.Trans.Maybe        as Maybe+import qualified Control.Monad.Trans.State.Strict as State import qualified Crypto.Hash import qualified Data.ByteString+import qualified Data.ByteString.Lazy import qualified Data.CaseInsensitive import qualified Data.Foldable- import qualified Data.HashMap.Strict.InsOrd-import qualified Data.List.NonEmpty                      as NonEmpty-import qualified Data.Map.Strict                         as Map+import qualified Data.List.NonEmpty               as NonEmpty+import qualified Data.Map.Strict                  as Map import qualified Data.Text.Encoding-import qualified Data.Text                               as Text+import qualified Data.Text                        as Text import qualified Data.Text.IO+import qualified Dhall.Binary import qualified Dhall.Core import qualified Dhall.Parser-import qualified Dhall.Context import qualified Dhall.Pretty.Internal import qualified Dhall.TypeCheck import qualified System.Environment-import qualified System.Directory+import qualified System.Directory                 as Directory+import qualified System.FilePath                  as FilePath import qualified Text.Megaparsec import qualified Text.Parser.Combinators import qualified Text.Parser.Token@@ -355,8 +370,8 @@     canonicalize (Local prefix file) =         Local prefix (canonicalize file) -    canonicalize (URL prefix file suffix header) =-        URL prefix (canonicalize file) suffix header+    canonicalize (Remote (URL {..})) =+        Remote (URL { path = canonicalize path, ..})      canonicalize (Env name) =         Env name@@ -420,23 +435,153 @@         <>  "↳ " <> show actualHash <> "\n"  -- | Parse an expression from a `Import` containing a Dhall program-exprFromImport :: Import -> StateT Status IO (Expr Src Import)-exprFromImport (Import {..}) = do+exprFromImport :: Import -> StateT (Status IO) IO (Expr Src Import)+exprFromImport import_@(Import {..}) = do     let ImportHashed {..} = importHashed +    case hash of+        Nothing -> do+            exprFromUncachedImport import_++        Just expectedHash -> do+            Status {..} <- State.get++            result <- Maybe.runMaybeT (getCacheFile expectedHash)++            case result of+                Just (Read, cacheFile) -> do+                    bytesStrict <- liftIO (Data.ByteString.readFile cacheFile)++                    let actualHash = Crypto.Hash.hash bytesStrict++                    if expectedHash == actualHash+                        then return ()+                        else liftIO (Control.Exception.throwIO (HashMismatch {..}))++                    let bytesLazy = Data.ByteString.Lazy.fromStrict bytesStrict++                    term <- case Codec.Serialise.deserialiseOrFail bytesLazy of+                        Left exception ->+                            liftIO (Control.Exception.throwIO exception)++                        Right term ->+                            return term++                    case Dhall.Binary.decode term of+                        Left exception ->+                            liftIO (Control.Exception.throwIO exception)++                        Right expression ->+                            return expression++                Just (Write, cacheFile) -> do+                    expression <- exprFromUncachedImport import_++                    let _stack' = NonEmpty.cons import_ _stack+                    zoom stack (State.put _stack')+                    resolvedExpression <- loadWith expression+                    zoom stack (State.put _stack)++                    case Dhall.TypeCheck.typeWith _startingContext resolvedExpression of+                        Left  _ -> do+                            return ()++                        Right _ -> do+                            let normalizedExpression =+                                    Dhall.Core.alphaNormalize+                                        (Dhall.Core.normalizeWith+                                            (getReifiedNormalizer _normalizer)+                                            resolvedExpression+                                        )++                            let bytes =+                                    encodeExpression _protocolVersion normalizedExpression++                            let actualHash = Crypto.Hash.hash bytes++                            if expectedHash == actualHash+                                then return ()+                                else liftIO (Control.Exception.throwIO (HashMismatch {..}))++                            liftIO (Data.ByteString.writeFile cacheFile bytes)++                    return expression++                Nothing -> do+                    exprFromUncachedImport import_++data CacheMode = Read | Write++getCacheFile+    :: (Alternative m, MonadIO m)+    => Crypto.Hash.Digest SHA256 -> m (CacheMode, FilePath)+getCacheFile hash = do+    let assertDirectory directory = do+            let private = transform Directory.emptyPermissions+                  where+                    transform =+                            Directory.setOwnerReadable   True+                        .   Directory.setOwnerWritable   True+                        .   Directory.setOwnerSearchable True++            let accessible path =+                       Directory.readable   path+                    && Directory.writable   path+                    && Directory.searchable path++            directoryExists <- liftIO (Directory.doesDirectoryExist directory)++            if directoryExists+                then do+                    permissions <- liftIO (Directory.getPermissions directory)++                    guard (accessible permissions)++                else do+                    assertDirectory (FilePath.takeDirectory directory)++                    liftIO (Directory.createDirectory directory)++                    liftIO (Directory.setPermissions directory private)++    cacheDirectory <- liftIO $ Directory.getXdgDirectory Directory.XdgCache ""+            +    assertDirectory cacheDirectory++    let dhallDirectory = cacheDirectory </> "dhall"++    assertDirectory dhallDirectory++    let cacheFile = dhallDirectory </> show hash++    cacheFileExists <- liftIO (Directory.doesPathExist cacheFile)++    if cacheFileExists+        then do+            True <- liftIO (Directory.doesFileExist cacheFile)++            return (Read, cacheFile)++        else do+            return (Write, cacheFile)++exprFromUncachedImport :: Import -> StateT (Status IO) IO (Expr Src Import)+exprFromUncachedImport (Import {..}) = do+    let ImportHashed {..} = importHashed+     (path, text) <- case importType of         Local prefix (File {..}) -> liftIO $ do             let Directory {..} = directory              prefixPath <- case prefix of                 Home -> do-                    System.Directory.getHomeDirectory+                    Directory.getHomeDirectory                  Absolute -> do                     return "/"                  Here -> do-                    System.Directory.getCurrentDirectory+                    Directory.getCurrentDirectory              let cs = map Text.unpack (file : components) @@ -444,7 +589,7 @@              let path = foldr cons prefixPath cs -            exists <- System.Directory.doesFileExist path+            exists <- Directory.doesFileExist path              if exists                 then return ()@@ -454,18 +599,23 @@              return (path, text) -        URL prefix file suffix maybeHeaders -> do+        Remote (URL scheme authority file query fragment maybeHeaders) -> do+            let prefix =+                        (case scheme of HTTP -> "http"; HTTPS -> "https")+                    <>  "://"+                    <>  authority+             let fileText = Dhall.Pretty.Internal.prettyToStrictText file++            let suffix =+                        (case query    of Nothing -> ""; Just q -> "?" <> q)+                    <>  (case fragment of Nothing -> ""; Just f -> "#" <> f)             let url      = Text.unpack (prefix <> fileText <> suffix)              mheaders <- case maybeHeaders of                 Nothing            -> return Nothing                 Just importHashed_ -> do-                    expr <- loadStaticWith-                        exprFromImport-                        Dhall.Context.empty-                        (const Nothing)-                        (Embed (Import importHashed_ Code))+                    expr <- loadWith (Embed (Import importHashed_ Code))                      let expected :: Expr Src X                         expected =@@ -528,63 +678,23 @@         RawText -> do             return (TextLit (Chunks [] text)) --- | Resolve all imports within an expression using a custom typing--- context and `Import`-resolving callback in arbitrary `MonadCatch`--- monad.------ This resolves imports relative to @.@ (the current working directory).-loadWith-    :: MonadCatch m-    => (Import -> StateT Status m (Expr Src Import))-    -> Dhall.Context.Context (Expr Src X)-    -> Dhall.Core.Normalizer X-    -> Expr Src Import-    -> m (Expr Src X)-loadWith from_import ctx n expr =-    loadDirWith "." from_import ctx n expr---- | Resolve all imports within an expression using a custom typing--- context and `Import`-resolving callback in arbitrary `MonadCatch`--- monad, relative to a given directory.------ @since 1.16-loadDirWith-    :: MonadCatch m-    => FilePath-    -> (Import -> StateT Status m (Expr Src Import))-    -> Dhall.Context.Context (Expr Src X)-    -> Dhall.Core.Normalizer X-    -> Expr Src Import-    -> m (Expr Src X)-loadDirWith dir from_import ctx n expr = do-    State.evalStateT (loadStaticWith from_import ctx n expr) (emptyStatus dir)---- | Resolve all imports within an expression, relative to @.@ (the--- current working directory), using a custom typing context.------ @load = loadWithContext Dhall.Context.empty@-loadWithContext-    :: Dhall.Context.Context (Expr Src X)-    -> Dhall.Core.Normalizer X-    -> Expr Src Import-    -> IO (Expr Src X)-loadWithContext ctx n expr =-    loadDirWith "." exprFromImport ctx n expr+-- | Default starting `Status`, importing relative to the given directory.+emptyStatus :: FilePath -> Status IO+emptyStatus = emptyStatusWith exprFromImport +{-| Generalized version of `load` --- | This loads a \"static\" expression (i.e. an expression free of imports)-loadStaticWith-    :: MonadCatch m-    => (Import -> StateT Status m (Expr Src Import))-    -> Dhall.Context.Context (Expr Src X)-    -> Dhall.Core.Normalizer X-    -> Expr Src Import-    -> StateT Status m (Expr Src X)-loadStaticWith from_import ctx n expr₀ = case expr₀ of+    You can configure the desired behavior through the initial `Status` that you+    supply+-}+loadWith :: MonadCatch m => Expr Src Import -> StateT (Status m) m (Expr Src X)+loadWith expr₀ = case expr₀ of   Embed import_ -> do-    imports <- zoom stack State.get+    Status {..} <- State.get -    let local (Import (ImportHashed _ (URL     {})) _) = False+    let imports = _stack++    let local (Import (ImportHashed _ (Remote  {})) _) = False         local (Import (ImportHashed _ (Local   {})) _) = True         local (Import (ImportHashed _ (Env     {})) _) = True         local (Import (ImportHashed _ (Missing {})) _) = True@@ -600,8 +710,7 @@     expr <- if here `elem` canonicalizeAll imports         then throwMissingImport (Imported imports (Cycle import_))         else do-            m <- zoom cache State.get-            case Map.lookup here m of+            case Map.lookup here _cache of                 Just expr -> return expr                 Nothing   -> do                     -- Here we have to match and unwrap the @MissingImports@@@ -614,7 +723,7 @@                     let handler₀                             :: (MonadCatch m)                             => MissingImports-                            -> StateT Status m (Expr Src Import)+                            -> StateT (Status m) m (Expr Src Import)                         handler₀ e@(MissingImports []) = throwM e                         handler₀ (MissingImports [e]) =                           throwMissingImport (Imported imports' e)@@ -626,19 +735,18 @@                         handler₁                             :: (MonadCatch m)                             => SomeException-                            -> StateT Status m (Expr Src Import)+                            -> StateT (Status m) m (Expr Src Import)                         handler₁ e =                           throwMissingImport (Imported imports' e)                      -- This loads a \"dynamic\" expression (i.e. an expression                     -- that might still contain imports)-                    let loadDynamic =-                            from_import here+                    let loadDynamic = _resolver here                      expr' <- loadDynamic `catches` [ Handler handler₀, Handler handler₁ ]                      zoom stack (State.put imports')-                    expr'' <- loadStaticWith from_import ctx n expr'+                    expr'' <- loadWith expr'                     zoom stack (State.put imports)                      -- Type-check expressions here for three separate reasons:@@ -651,43 +759,45 @@                     --                     -- There is no need to check expressions that have been                     -- cached, since they have already been checked-                    expr''' <- case Dhall.TypeCheck.typeWith ctx expr'' of+                    expr''' <- case Dhall.TypeCheck.typeWith _startingContext expr'' of                         Left  err -> throwM (Imported imports' err)-                        Right _   -> return (Dhall.Core.normalizeWith n expr'')-                    zoom cache (State.put $! Map.insert here expr''' m)+                        Right _   -> return (Dhall.Core.normalizeWith (getReifiedNormalizer _normalizer) expr'')+                    zoom cache (State.put $! Map.insert here expr''' _cache)                     return expr'''      case hash (importHashed import_) of         Nothing -> do             return ()         Just expectedHash -> do-            let actualHash = hashExpression expr+            let actualHash =+                    hashExpression _protocolVersion (Dhall.Core.alphaNormalize expr)+             if expectedHash == actualHash                 then return ()                 else throwMissingImport (Imported imports' (HashMismatch {..}))      return expr-  ImportAlt a b -> loop a `catch` handler₀+  ImportAlt a b -> loadWith a `catch` handler₀     where       handler₀ (MissingImports es₀) =-        loop b `catch` handler₁+        loadWith b `catch` handler₁         where           handler₁ (MissingImports es₁) =             throwM (MissingImports (es₀ ++ es₁))   Const a              -> pure (Const a)   Var a                -> pure (Var a)-  Lam a b c            -> Lam <$> pure a <*> loop b <*> loop c-  Pi a b c             -> Pi <$> pure a <*> loop b <*> loop c-  App a b              -> App <$> loop a <*> loop b-  Let a b c d          -> Let <$> pure a <*> mapM loop b <*> loop c <*> loop d-  Annot a b            -> Annot <$> loop a <*> loop b+  Lam a b c            -> Lam <$> pure a <*> loadWith b <*> loadWith c+  Pi a b c             -> Pi <$> pure a <*> loadWith b <*> loadWith c+  App a b              -> App <$> loadWith a <*> loadWith b+  Let a b c d          -> Let <$> pure a <*> mapM loadWith b <*> loadWith c <*> loadWith d+  Annot a b            -> Annot <$> loadWith a <*> loadWith b   Bool                 -> pure Bool   BoolLit a            -> pure (BoolLit a)-  BoolAnd a b          -> BoolAnd <$> loop a <*> loop b-  BoolOr a b           -> BoolOr <$> loop a <*> loop b-  BoolEQ a b           -> BoolEQ <$> loop a <*> loop b-  BoolNE a b           -> BoolNE <$> loop a <*> loop b-  BoolIf a b c         -> BoolIf <$> loop a <*> loop b <*> loop c+  BoolAnd a b          -> BoolAnd <$> loadWith a <*> loadWith b+  BoolOr a b           -> BoolOr <$> loadWith a <*> loadWith b+  BoolEQ a b           -> BoolEQ <$> loadWith a <*> loadWith b+  BoolNE a b           -> BoolNE <$> loadWith a <*> loadWith b+  BoolIf a b c         -> BoolIf <$> loadWith a <*> loadWith b <*> loadWith c   Natural              -> pure Natural   NaturalLit a         -> pure (NaturalLit a)   NaturalFold          -> pure NaturalFold@@ -697,8 +807,8 @@   NaturalOdd           -> pure NaturalOdd   NaturalToInteger     -> pure NaturalToInteger   NaturalShow          -> pure NaturalShow-  NaturalPlus a b      -> NaturalPlus <$> loop a <*> loop b-  NaturalTimes a b     -> NaturalTimes <$> loop a <*> loop b+  NaturalPlus a b      -> NaturalPlus <$> loadWith a <*> loadWith b+  NaturalTimes a b     -> NaturalTimes <$> loadWith a <*> loadWith b   Integer              -> pure Integer   IntegerLit a         -> pure (IntegerLit a)   IntegerShow          -> pure IntegerShow@@ -707,11 +817,11 @@   DoubleLit a          -> pure (DoubleLit a)   DoubleShow           -> pure DoubleShow   Text                 -> pure Text-  TextLit (Chunks a b) -> fmap TextLit (Chunks <$> mapM (mapM loop) a <*> pure b)-  TextAppend a b       -> TextAppend <$> loop a <*> loop b+  TextLit (Chunks a b) -> fmap TextLit (Chunks <$> mapM (mapM loadWith) a <*> pure b)+  TextAppend a b       -> TextAppend <$> loadWith a <*> loadWith b   List                 -> pure List-  ListLit a b          -> ListLit <$> mapM loop a <*> mapM loop b-  ListAppend a b       -> ListAppend <$> loop a <*> loop b+  ListLit a b          -> ListLit <$> mapM loadWith a <*> mapM loadWith b+  ListAppend a b       -> ListAppend <$> loadWith a <*> loadWith b   ListBuild            -> pure ListBuild   ListFold             -> pure ListFold   ListLength           -> pure ListLength@@ -720,41 +830,51 @@   ListIndexed          -> pure ListIndexed   ListReverse          -> pure ListReverse   Optional             -> pure Optional-  OptionalLit a b      -> OptionalLit <$> loop a <*> mapM loop b+  OptionalLit a b      -> OptionalLit <$> loadWith a <*> mapM loadWith b   OptionalFold         -> pure OptionalFold   OptionalBuild        -> pure OptionalBuild-  Record a             -> Record <$> mapM loop a-  RecordLit a          -> RecordLit <$> mapM loop a-  Union a              -> Union <$> mapM loop a-  UnionLit a b c       -> UnionLit <$> pure a <*> loop b <*> mapM loop c-  Combine a b          -> Combine <$> loop a <*> loop b-  CombineTypes a b     -> CombineTypes <$> loop a <*> loop b-  Prefer a b           -> Prefer <$> loop a <*> loop b-  Merge a b c          -> Merge <$> loop a <*> loop b <*> mapM loop c-  Constructors a       -> Constructors <$> loop a-  Field a b            -> Field <$> loop a <*> pure b-  Project a b          -> Project <$> loop a <*> pure b-  Note a b             -> Note <$> pure a <*> loop b-  where-    loop = loadStaticWith from_import ctx n-+  Record a             -> Record <$> mapM loadWith a+  RecordLit a          -> RecordLit <$> mapM loadWith a+  Union a              -> Union <$> mapM loadWith a+  UnionLit a b c       -> UnionLit <$> pure a <*> loadWith b <*> mapM loadWith c+  Combine a b          -> Combine <$> loadWith a <*> loadWith b+  CombineTypes a b     -> CombineTypes <$> loadWith a <*> loadWith b+  Prefer a b           -> Prefer <$> loadWith a <*> loadWith b+  Merge a b c          -> Merge <$> loadWith a <*> loadWith b <*> mapM loadWith c+  Constructors a       -> Constructors <$> loadWith a+  Field a b            -> Field <$> loadWith a <*> pure b+  Project a b          -> Project <$> loadWith a <*> pure b+  Note a b             -> Note <$> pure a <*> loadWith b  -- | Resolve all imports within an expression load :: Expr Src Import -> IO (Expr Src X)-load = loadWithContext Dhall.Context.empty (const Nothing)+load expression = State.evalStateT (loadWith expression) (emptyStatus ".") --- | Hash a fully resolved expression-hashExpression :: Expr s X -> (Crypto.Hash.Digest SHA256)-hashExpression expr = Crypto.Hash.hash actualBytes+encodeExpression+    :: forall s . ProtocolVersion -> Expr s X -> Data.ByteString.ByteString+encodeExpression _protocolVersion expression = bytesStrict   where-    text = Dhall.Core.pretty (Dhall.Core.normalize expr)-    actualBytes = Data.Text.Encoding.encodeUtf8 text+    intermediateExpression :: Expr s Import+    intermediateExpression = fmap absurd expression +    term :: Term+    term = Dhall.Binary.encode _protocolVersion intermediateExpression++    bytesLazy = Codec.Serialise.serialise term++    bytesStrict = Data.ByteString.Lazy.toStrict bytesLazy++-- | Hash a fully resolved expression+hashExpression :: ProtocolVersion -> Expr s X -> (Crypto.Hash.Digest SHA256)+hashExpression _protocolVersion expression =+    Crypto.Hash.hash (encodeExpression _protocolVersion expression)+ {-| Convenience utility to hash a fully resolved expression and return the     base-16 encoded hash with the @sha256:@ prefix      In other words, the output of this function can be pasted into Dhall     source code to add an integrity check to an import -}-hashExpressionToCode :: Expr s X -> Text-hashExpressionToCode expr = "sha256:" <> Text.pack (show (hashExpression expr))+hashExpressionToCode :: ProtocolVersion -> Expr s X -> Text+hashExpressionToCode _protocolVersion expr =+    "sha256:" <> Text.pack (show (hashExpression _protocolVersion expr))
src/Dhall/Import/HTTP.hs view
@@ -75,7 +75,7 @@         <> show e' #endif -needManager :: StateT Status IO Manager+needManager :: StateT (Status m) IO Manager needManager = do     x <- zoom manager State.get     case join (fmap fromDynamic x) of@@ -97,7 +97,7 @@ fetchFromHttpUrl     :: String     -> Maybe [(CI ByteString, ByteString)]-    -> StateT Status IO (String, Text.Text)+    -> StateT (Status m) IO (String, Text.Text) fetchFromHttpUrl url mheaders = do     m <- needManager 
src/Dhall/Import/Types.hs view
@@ -1,48 +1,87 @@-{-# LANGUAGE OverloadedStrings   #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards   #-} {-# OPTIONS_GHC -Wall #-}  module Dhall.Import.Types where  import Control.Exception (Exception)+import Control.Monad.Trans.State.Strict (StateT) import Data.Dynamic import Data.List.NonEmpty (NonEmpty) import Data.Map (Map) import Data.Semigroup ((<>))-import Lens.Family (LensLike')-import System.FilePath (isRelative, splitDirectories)--import qualified Data.Map as Map-import qualified Data.Text-+import Dhall.Binary (ProtocolVersion(..))+import Dhall.Context (Context) import Dhall.Core-  ( Directory (..), Expr, File (..), FilePrefix (..), Import (..)-  , ImportHashed (..), ImportMode (..), ImportType (..)+  ( Directory (..)+  , Expr+  , File (..)+  , FilePrefix (..)+  , Import (..)+  , ImportHashed (..)+  , ImportMode (..)+  , ImportType (..)+  , ReifiedNormalizer(..)   ) import Dhall.Parser (Src) import Dhall.TypeCheck (X)+import Lens.Family (LensLike')+import System.FilePath (isRelative, splitDirectories) +import qualified Dhall.Binary+import qualified Dhall.Context+import qualified Data.Map      as Map+import qualified Data.Text  -- | State threaded throughout the import process-data Status = Status-    { _stack   :: NonEmpty Import+data Status m = Status+    { _stack :: NonEmpty Import     -- ^ Stack of `Import`s that we've imported along the way to get to the     -- current point-    , _cache   :: Map Import (Expr Src X)++    , _cache :: Map Import (Expr Src X)     -- ^ Cache of imported expressions in order to avoid importing the same     --   expression twice with different values+     , _manager :: Maybe Dynamic     -- ^ Cache for the HTTP `Manager` so that we only acquire it once++    , _protocolVersion :: ProtocolVersion++    , _normalizer :: ReifiedNormalizer X++    , _startingContext :: Context (Expr Src X)++    , _resolver :: Import -> StateT (Status m) m (Expr Src Import)     } --- | Default starting `Status`, importing relative to the given directory.-emptyStatus :: FilePath -> Status-emptyStatus dir = Status (pure rootImport) Map.empty Nothing+-- | Default starting `Status` that is polymorphic in the base `Monad`+emptyStatusWith+    :: (Import -> StateT (Status m) m (Expr Src Import))+    -> FilePath+    -> Status m+emptyStatusWith _resolver rootDirectory = Status {..}   where-    prefix = if isRelative dir+    _stack = pure rootImport++    _cache = Map.empty++    _manager = Nothing++    _protocolVersion = Dhall.Binary.defaultProtocolVersion++    _normalizer = ReifiedNormalizer (const Nothing)++    _startingContext = Dhall.Context.empty++    prefix = if isRelative rootDirectory       then Here       else Absolute-    pathComponents = fmap Data.Text.pack (reverse (splitDirectories dir))+    pathComponents =+        fmap Data.Text.pack (reverse (splitDirectories rootDirectory))+     dirAsFile = File (Directory pathComponents) "."+     -- Fake import to set the directory we're relative to.     rootImport = Import       { importHashed = ImportHashed@@ -52,17 +91,30 @@       , importMode = Code       } --stack :: Functor f => LensLike' f Status (NonEmpty Import)+stack :: Functor f => LensLike' f (Status m) (NonEmpty Import) stack k s = fmap (\x -> s { _stack = x }) (k (_stack s)) -cache :: Functor f => LensLike' f Status (Map Import (Expr Src X))+cache :: Functor f => LensLike' f (Status m) (Map Import (Expr Src X)) cache k s = fmap (\x -> s { _cache = x }) (k (_cache s)) -manager :: Functor f => LensLike' f Status (Maybe Dynamic)+manager :: Functor f => LensLike' f (Status m) (Maybe Dynamic) manager k s = fmap (\x -> s { _manager = x }) (k (_manager s)) +protocolVersion :: Functor f => LensLike' f (Status m) ProtocolVersion+protocolVersion k s =+    fmap (\x -> s { _protocolVersion = x }) (k (_protocolVersion s)) +normalizer :: Functor f => LensLike' f (Status m) (ReifiedNormalizer X)+normalizer k s = fmap (\x -> s { _normalizer = x }) (k (_normalizer s))++startingContext :: Functor f => LensLike' f (Status m) (Context (Expr Src X))+startingContext k s =+    fmap (\x -> s { _startingContext = x }) (k (_startingContext s))++resolver+    :: Functor f+    => LensLike' f (Status m) (Import -> StateT (Status m) m (Expr Src Import))+resolver k s = fmap (\x -> s { _resolver = x }) (k (_resolver s))  {-| This exception indicates that there was an internal error in Dhall's     import-related logic
src/Dhall/Lint.hs view
@@ -1,3 +1,5 @@+-- | This module contains the implementation of the @dhall lint@ command+ module Dhall.Lint     ( -- * Lint       lint@@ -8,6 +10,10 @@  import qualified Dhall.Core +{-| Automatically improve a Dhall expression++    Currently this only removes unused @let@ bindings+-} lint :: Expr s Import -> Expr t Import lint expression = loop (Dhall.Core.denote expression)   where@@ -28,8 +34,8 @@         a' = loop a         b' = loop b     loop (Let a b c d)-        | not (V a 0 `Dhall.Core.freeIn` d) =-            loop d+        | not (V a 0 `Dhall.Core.freeIn` d') =+            d'         | otherwise =             Let a b' c' d'       where
src/Dhall/Main.hs view
@@ -1,26 +1,36 @@+{-| This module contains the top-level entrypoint and options parsing for the+    @dhall@ executable+-}+ {-# LANGUAGE DeriveAnyClass    #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards   #-}  module Dhall.Main-    ( -- Commands-      parseOptions+    ( -- * Options+      Options(..)+    , Mode(..)+    , parseOptions     , parserInfoOptions++      -- * Execution     , command     , main     ) where  import Control.Applicative (optional, (<|>)) import Control.Exception (Exception, SomeException)-import Data.Monoid (mempty, (<>))+import Data.Monoid ((<>)) import Data.Text (Text) import Data.Text.Prettyprint.Doc (Pretty) import Data.Version (showVersion)-import Dhall.Core (Expr, Import)-import Dhall.Import (Imported(..), load)+import Dhall.Binary (ProtocolVersion)+import Dhall.Core (Expr(..), Import)+import Dhall.Import (Imported(..)) import Dhall.Parser (Src) import Dhall.Pretty (annToAnsiStyle, prettyExpr, layoutOpts) import Dhall.TypeCheck (DetailedTypeError(..), TypeError, X)+import Lens.Family (set) import Options.Applicative (Parser, ParserInfo) import System.Exit (exitFailure) import System.IO (Handle)@@ -28,16 +38,19 @@ import qualified Paths_dhall as Meta  import qualified Control.Exception+import qualified Control.Monad.Trans.State.Strict          as State import qualified Data.Text import qualified Data.Text.IO import qualified Data.Text.Prettyprint.Doc                 as Pretty import qualified Data.Text.Prettyprint.Doc.Render.Terminal as Pretty import qualified Dhall+import qualified Dhall.Binary import qualified Dhall.Core import qualified Dhall.Diff import qualified Dhall.Format import qualified Dhall.Freeze import qualified Dhall.Hash+import qualified Dhall.Import import qualified Dhall.Lint import qualified Dhall.Parser import qualified Dhall.Repl@@ -47,14 +60,17 @@ import qualified System.Console.ANSI import qualified System.IO +-- | Top-level program options data Options = Options-    { mode    :: Mode-    , explain :: Bool-    , plain   :: Bool+    { mode            :: Mode+    , explain         :: Bool+    , plain           :: Bool+    , protocolVersion :: ProtocolVersion     } +-- | The subcommands for the @dhall@ executable data Mode-    = Default+    = Default { annotate :: Bool }     | Version     | Resolve     | Type@@ -74,8 +90,14 @@         <>  Options.Applicative.metavar "FILE"         ) +-- | `Parser` for the `Options` type parseOptions :: Parser Options-parseOptions = Options <$> parseMode <*> parseExplain <*> parsePlain+parseOptions =+        Options+    <$> parseMode+    <*> parseExplain+    <*> parsePlain+    <*> Dhall.Binary.parseProtocolVersion   where     parseExplain =         Options.Applicative.switch@@ -102,7 +124,7 @@     <|> subcommand "lint"      "Improve Dhall code"              parseLint     <|> formatSubcommand     <|> freezeSubcommand-    <|> pure Default+    <|> parseDefault   where     subcommand name description modeParser =         Options.Applicative.subparser@@ -147,6 +169,13 @@         where             parseFreeze = Freeze <$> optional parseInplace +    parseDefault = Default <$> parseAnnotate+      where+        parseAnnotate =+            Options.Applicative.switch+                (   Options.Applicative.long "annotate"+                )+ data ImportResolutionDisabled = ImportResolutionDisabled deriving (Exception)  instance Show ImportResolutionDisabled where@@ -166,6 +195,7 @@ assertNoImports expression =     throws (traverse (\_ -> Left ImportResolutionDisabled) expression) +-- | `ParserInfo` for the `Options` type parserInfoOptions :: ParserInfo Options parserInfoOptions =     Options.Applicative.info@@ -174,10 +204,15 @@         <>  Options.Applicative.fullDesc         ) +-- | Run the command specified by the `Options` type command :: Options -> IO () command (Options {..}) = do     GHC.IO.Encoding.setLocaleEncoding System.IO.utf8 +    let status =+            set Dhall.Import.protocolVersion protocolVersion (Dhall.Import.emptyStatus ".")++     let handle =                 Control.Exception.handle handler2             .   Control.Exception.handle handler1@@ -225,23 +260,26 @@         Version -> do             putStrLn (showVersion Meta.version) -        Default -> do+        Default {..} -> do             expression <- getExpression -            resolvedExpression <- load expression+            resolvedExpression <- State.evalStateT (Dhall.Import.loadWith expression) status              inferredType <- throws (Dhall.TypeCheck.typeOf resolvedExpression) -            render System.IO.stderr (Dhall.Core.normalize inferredType)+            let normalizedExpression = Dhall.Core.normalize resolvedExpression -            Data.Text.IO.hPutStrLn System.IO.stderr mempty+            let annotatedExpression =+                    if annotate+                        then Annot normalizedExpression inferredType+                        else normalizedExpression -            render System.IO.stdout (Dhall.Core.normalize resolvedExpression)+            render System.IO.stdout annotatedExpression          Resolve -> do             expression <- getExpression -            resolvedExpression <- load expression+            resolvedExpression <- State.evalStateT (Dhall.Import.loadWith expression) status              render System.IO.stdout resolvedExpression @@ -264,7 +302,7 @@             render System.IO.stdout (Dhall.Core.normalize inferredType)          Repl -> do-            Dhall.Repl.repl explain+            Dhall.Repl.repl explain protocolVersion          Diff expr1 expr2 -> do             expression1 <- Dhall.inputExpr expr1@@ -280,10 +318,10 @@             Dhall.Format.format inplace          Freeze inplace -> do-            Dhall.Freeze.freeze inplace+            Dhall.Freeze.freeze inplace protocolVersion          Hash -> do-            Dhall.Hash.hash +            Dhall.Hash.hash protocolVersion          Lint inplace -> do             case inplace of@@ -320,6 +358,7 @@                           System.IO.stdout                           (Pretty.layoutSmart layoutOpts (Pretty.unAnnotate doc)) +-- | Entry point for the @dhall@ executable main :: IO () main = do     options <- Options.Applicative.execParser parserInfoOptions
src/Dhall/Parser.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE RecordWildCards            #-}+ -- | This module contains Dhall's parsing logic  module Dhall.Parser (
src/Dhall/Parser/Combinators.hs view
@@ -131,6 +131,12 @@ satisfy :: (Char -> Bool) -> Parser Text satisfy = fmap Data.Text.singleton . Text.Parser.Char.satisfy +takeWhile :: (Char -> Bool) -> Parser Text+takeWhile predicate = Parser (Text.Megaparsec.takeWhileP Nothing predicate)++takeWhile1 :: (Char -> Bool) -> Parser Text+takeWhile1 predicate = Parser (Text.Megaparsec.takeWhile1P Nothing predicate)+ noDuplicates :: Ord a => [a] -> Parser (Set a) noDuplicates = go Data.Set.empty   where
src/Dhall/Parser/Expression.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE NamedFieldPuns  #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE OverloadedStrings #-} -- | Parsing Dhall expressions.@@ -168,7 +169,7 @@ makeOperatorExpression subExpression operatorParser operator embedded =     noted (do         a <- subExpression embedded-        b <- many (do operatorParser; subExpression embedded)+        b <- Text.Megaparsec.many (do operatorParser; subExpression embedded)         return (foldr1 operator (a:b)) )  importAltExpression :: Parser a -> Parser (Expr Src a)@@ -223,7 +224,7 @@ applicationExpression embedded = do     f <- (do _constructors; return Constructors) <|> return id     a <- noted (importExpression embedded)-    b <- many (noted (importExpression embedded))+    b <- Text.Megaparsec.many (noted (importExpression embedded))     return (foldl app (f a) b)   where     app nL@(Note (Src before _ bytesL) _) nR@(Note (Src _ after bytesR) _) =@@ -246,7 +247,7 @@      let left  x  e = Field   e x     let right xs e = Project e xs-    b <- many (try (do _dot; fmap left label <|> fmap right labels))+    b <- Text.Megaparsec.many (try (do _dot; fmap left label <|> fmap right labels))     return (foldl (\e k -> k e) a b) )  primitiveExpression :: Parser a -> Parser (Expr Src a)@@ -526,7 +527,7 @@ doubleQuotedLiteral :: Parser a -> Parser (Chunks Src a) doubleQuotedLiteral embedded = do     _      <- Text.Parser.Char.char '"'-    chunks <- many (doubleQuotedChunk embedded)+    chunks <- Text.Megaparsec.many (doubleQuotedChunk embedded)     _      <- Text.Parser.Char.char '"'     return (mconcat chunks) @@ -626,7 +627,7 @@     let nonEmptyRecordType = do             _colon             b <- expression embedded-            e <- many (do+            e <- Text.Megaparsec.many (do                 _comma                 c <- label                 _colon@@ -638,7 +639,7 @@     let nonEmptyRecordLiteral = do             _equal             b <- expression embedded-            e <- many (do+            e <- Text.Megaparsec.many (do                 _comma                 c <- label                 _equal@@ -666,7 +667,7 @@         let alternative0 = do                 _equal                 b <- expression embedded-                kvs <- many (do+                kvs <- Text.Megaparsec.many (do                     _bar                     c <- label                     _colon@@ -693,7 +694,7 @@ nonEmptyListLiteral embedded = (do     _openBracket     a <- expression embedded-    b <- many (do _comma; expression embedded)+    b <- Text.Megaparsec.many (do _comma; expression embedded)     _closeBracket     return (ListLit Nothing (Data.Sequence.fromList (a:b))) ) <?> "list literal" @@ -757,12 +758,12 @@  http :: Parser ImportType http = do-    (prefix, path, suffix) <- httpRaw+    url <- httpRaw     whitespace     headers <- optional (do         _using         (importHashed_ <|> (_openParens *> importHashed_ <* _closeParens)) )-    return (URL prefix path suffix headers)+    return (Remote (url { headers }))  missing :: Parser ImportType missing = do
src/Dhall/Parser/Token.hs view
@@ -6,7 +6,7 @@  import           Dhall.Parser.Combinators -import Control.Applicative (Alternative(..))+import Control.Applicative (Alternative(..), optional) import Data.Functor (void) import Data.Semigroup (Semigroup(..)) import Data.Set (Set)@@ -34,6 +34,9 @@ whitespace :: Parser () whitespace = Text.Parser.Combinators.skipMany whitespaceChunk +nonemptyWhitespace :: Parser ()+nonemptyWhitespace = Text.Parser.Combinators.skipSome whitespaceChunk+ alpha :: Char -> Bool alpha c = ('\x41' <= c && c <= '\x5A') || ('\x61' <= c && c <= '\x7A') @@ -155,10 +158,9 @@  simpleLabel :: Parser Text simpleLabel = try (do-    c  <- Text.Parser.Char.satisfy headCharacter-    cs <- many (Text.Parser.Char.satisfy tailCharacter)-    let string = c:cs-    let text = Data.Text.pack string+    c    <- Text.Parser.Char.satisfy headCharacter+    rest <- Dhall.Parser.Combinators.takeWhile tailCharacter+    let text = Data.Text.cons c rest     Control.Monad.guard (not (Data.HashSet.member text reservedIdentifiers))     return text )   where@@ -169,9 +171,9 @@ backtickLabel :: Parser Text backtickLabel = do     _ <- Text.Parser.Char.char '`'-    t <- some (Text.Parser.Char.satisfy predicate)+    t <- takeWhile1 predicate     _ <- Text.Parser.Char.char '`'-    return (Data.Text.pack t)+    return t   where     predicate c = alpha c || digit c || elem c ("$-/_:." :: String) @@ -247,20 +249,27 @@      return (File {..}) -scheme :: Parser Text-scheme = "http" <> option "s"+scheme_ :: Parser Scheme+scheme_ =+        ("http" :: Parser Text)+    *>  ((("s" :: Parser Text) *> pure HTTPS) <|> pure HTTP)+    <*  ("://" :: Parser Text) -httpRaw :: Parser (Text, File, Text)+httpRaw :: Parser URL httpRaw = do-    prefixText <- scheme <> "://" <> authority-    file   <- file_-    suffixText <- option ("?" <> query) <> option ("#" <> fragment)+    scheme    <- scheme_+    authority <- authority_+    path      <- file_+    query     <- optional (("?" :: Parser Text) *> query_)+    fragment  <- optional (("#" :: Parser Text) *> fragment_) -    return (prefixText, file, suffixText)+    let headers = Nothing -authority :: Parser Text-authority = option (try (userinfo <> "@")) <> host <> option (":" <> port)+    return (URL {..}) +authority_ :: Parser Text+authority_ = option (try (userinfo <> "@")) <> host <> option (":" <> port)+ userinfo :: Parser Text userinfo = star (satisfy predicate <|> pctEncoded)   where@@ -369,13 +378,13 @@   where     predicate c = unreserved c || subDelims c || c == ':' || c == '@' -query :: Parser Text-query = star (pchar <|> satisfy predicate)+query_ :: Parser Text+query_ = star (pchar <|> satisfy predicate)   where     predicate c = c == '/' || c == '?' -fragment :: Parser Text-fragment = star (pchar <|> satisfy predicate)+fragment_ :: Parser Text+fragment_ = star (pchar <|> satisfy predicate)   where     predicate c = c == '/' || c == '?' @@ -392,32 +401,35 @@ reserved :: Data.Text.Text -> Parser () reserved x = do _ <- Text.Parser.Char.text x; whitespace +keyword :: Data.Text.Text -> Parser ()+keyword x = try (do _ <- Text.Parser.Char.text x; nonemptyWhitespace)+ _if :: Parser ()-_if = reserved "if"+_if = keyword "if"  _then :: Parser ()-_then = reserved "then"+_then = keyword "then"  _else :: Parser ()-_else = reserved "else"+_else = keyword "else"  _let :: Parser ()-_let = reserved "let"+_let = keyword "let"  _in :: Parser ()-_in = reserved "in"+_in = keyword "in"  _as :: Parser ()-_as = reserved "as"+_as = keyword "as"  _using :: Parser ()-_using = reserved "using"+_using = keyword "using"  _merge :: Parser ()-_merge = reserved "merge"+_merge = keyword "merge"  _constructors :: Parser ()-_constructors = reserved "constructors"+_constructors = keyword "constructors"  _NaturalFold :: Parser () _NaturalFold = reserved "Natural/fold"
src/Dhall/Pretty.hs view
@@ -1,11 +1,18 @@ {-| This module contains logic for pretty-printing expressions, including     support for syntax highlighting -}-module Dhall.Pretty ( Ann(..), annToAnsiStyle, prettyExpr, layoutOpts ) where+module Dhall.Pretty+    ( -- * Pretty+      Ann(..)+    , annToAnsiStyle+    , prettyExpr+    , layoutOpts+    ) where  import Dhall.Pretty.Internal import qualified Data.Text.Prettyprint.Doc as Pretty +-- | Default layout options layoutOpts :: Pretty.LayoutOptions layoutOpts =     Pretty.defaultLayoutOptions
src/Dhall/Repl.hs view
@@ -1,19 +1,29 @@+-- | This module contains the implementation of the @dhall repl@ subcommand+ {-# language FlexibleContexts #-} {-# language NamedFieldPuns #-} {-# language OverloadedStrings #-} -module Dhall.Repl ( repl ) where+module Dhall.Repl+    ( -- * Repl+      repl+    ) where  import Control.Exception ( SomeException(SomeException), displayException, throwIO ) import Control.Monad.IO.Class ( MonadIO, liftIO ) import Control.Monad.State.Class ( MonadState, get, modify ) import Control.Monad.State.Strict ( evalStateT ) import Data.List ( foldl' )+import Dhall.Binary (ProtocolVersion(..))+import Dhall.Import (protocolVersion)+import Lens.Family (set) +import qualified Control.Monad.Trans.State.Strict as State import qualified Data.Text as Text import qualified Data.Text.Prettyprint.Doc as Pretty import qualified Data.Text.Prettyprint.Doc.Render.Terminal as Pretty ( renderIO ) import qualified Dhall+import qualified Dhall.Binary import qualified Dhall.Context import qualified Dhall.Core as Dhall ( Var(V), Expr, normalize ) import qualified Dhall.Pretty@@ -26,9 +36,9 @@ import qualified System.Console.Repline as Repline import qualified System.IO --repl :: Bool -> IO ()-repl explain = if explain then Dhall.detailed io else io+-- | Implementation of the @dhall repl@ subcommand+repl :: Bool -> ProtocolVersion -> IO ()+repl explain _protocolVersion = if explain then Dhall.detailed io else io   where     io =       evalStateT@@ -39,13 +49,14 @@         ( Repline.Word completer )             greeter         )-        (emptyEnv { explain })+        (emptyEnv { explain, _protocolVersion })   data Env = Env-  { envBindings :: Dhall.Context.Context Binding-  , envIt :: Maybe Binding-  , explain :: Bool+  { envBindings      :: Dhall.Context.Context Binding+  , envIt            :: Maybe Binding+  , explain          :: Bool+  , _protocolVersion :: ProtocolVersion   }  @@ -55,6 +66,7 @@     { envBindings = Dhall.Context.empty     , envIt = Nothing     , explain = False+    , _protocolVersion = Dhall.Binary.defaultProtocolVersion     }  @@ -78,6 +90,9 @@   :: ( MonadIO m, MonadState Env m )   => String -> m ( Dhall.Expr Dhall.Src Dhall.X ) parseAndLoad src = do+  env <-+    get+   parsed <-     case Dhall.exprFromText "(stdin)" ( Text.pack src ) of       Left e ->@@ -86,7 +101,10 @@       Right a ->         return a -  liftIO ( Dhall.load parsed )+  let status =+        set protocolVersion (_protocolVersion env) (Dhall.emptyStatus ".")++  liftIO ( State.evalStateT (Dhall.loadWith parsed) status )   eval :: ( MonadIO m, MonadState Env m ) => String -> m ()
src/Dhall/TH.hs view
@@ -21,7 +21,10 @@     time with all imports resolved, making it easy to keep your Dhall configs     and Haskell interpreters in sync. -}-module Dhall.TH where+module Dhall.TH+    ( -- * Template Haskell+      staticDhallExpression+    ) where  import Data.Typeable import Language.Haskell.TH.Quote (dataToExpQ) -- 7.10 compatibility.
src/Dhall/Tutorial.hs view
@@ -1427,7 +1427,7 @@ -- Dhall URL imports let you add or modify request headers with the @using@ -- keyword: ----- > https://example.com using ./headers+-- > https://example.com/example.dhall using ./headers -- -- ... where you can replace @./headers@ with any import that points to a Dhall -- expression of the following type:@@ -1451,16 +1451,16 @@ -- You cannot inline the headers within the same file as the URL.  You must -- provide them as a separate import.  That means that this is /not/ legal code: ----- > http://example.com using [ { header = "Accept", value = "application/dhall" } ]  -- NOT legal+-- > http://example.com/example.dhall using [ { header = "Accept", value = "application/dhall" } ]  -- NOT legal -- -- Dhall will forward imports if you import an expression from a URL that -- contains a relative import.  For example, if you import an expression like -- this: -- --- > http://example.com using ./headers+-- > http://example.com/example.dhall using ./headers -- --- ... and @http:\/\/example.com@ contains a relative import of @./foo@ then--- Dhall will import @http:\/\/example.com/foo@ using the same @./headers@ file.+-- ... and @http:\/\/example.com/example.dhall@ contains a relative import of @./foo@+-- then Dhall will import @http:\/\/example.com/foo@ using the same @./headers@ file.  -- $integrity --
src/Dhall/TypeCheck.hs view
@@ -556,6 +556,20 @@             Record kts -> return kts             _          -> Left (TypeError ctx e (MustCombineARecord '∧' kvsY tKvsY)) +        ttKvsX <- fmap Dhall.Core.normalize (loop ctx tKvsX)+        constX <- case ttKvsX of+            Const constX -> return constX+            _            -> Left (TypeError ctx e (MustCombineARecord '∧' kvsY tKvsY))++        ttKvsY <- fmap Dhall.Core.normalize (loop ctx tKvsY)+        constY <- case ttKvsY of+            Const constY -> return constY+            _            -> Left (TypeError ctx e (MustCombineARecord '∧' kvsY tKvsY))++        if constX == constY+            then return ()+            else Left (TypeError ctx e (RecordMismatch '∧' kvsX kvsY constX constY))+         let combineTypes ktsL ktsR = do                 let ksL =                         Data.Set.fromList (Data.HashMap.Strict.InsOrd.keys ktsL)@@ -632,6 +646,21 @@         ktsY  <- case tKvsY of             Record kts -> return kts             _          -> Left (TypeError ctx e (MustCombineARecord '⫽' kvsY tKvsY))++        ttKvsX <- fmap Dhall.Core.normalize (loop ctx tKvsX)+        constX <- case ttKvsX of+            Const constX -> return constX+            _            -> Left (TypeError ctx e (MustCombineARecord '⫽' kvsY tKvsY))++        ttKvsY <- fmap Dhall.Core.normalize (loop ctx tKvsY)+        constY <- case ttKvsY of+            Const constY -> return constY+            _            -> Left (TypeError ctx e (MustCombineARecord '⫽' kvsY tKvsY))++        if constX == constY+            then return ()+            else Left (TypeError ctx e (RecordMismatch '⫽' kvsX kvsY constX constY))+         return (Record (Data.HashMap.Strict.InsOrd.union ktsY ktsX))     loop ctx e@(Merge kvsX kvsY (Just t)) = do         _ <- loop ctx t@@ -807,6 +836,7 @@     | ListAppendMismatch (Expr s a) (Expr s a)     | DuplicateAlternative Text     | MustCombineARecord Char (Expr s a) (Expr s a)+    | RecordMismatch Char (Expr s a) (Expr s a) Const Const     | CombineTypesRequiresRecordType (Expr s a) (Expr s a)     | RecordTypeMismatch Const Const (Expr s a) (Expr s a)     | FieldCollision Text@@ -2453,6 +2483,61 @@         op   = pretty c         txt0 = insert expr0         txt1 = insert expr1++prettyTypeMessage (RecordMismatch c expr0 expr1 const0 const1) = ErrorMessages {..}+  where+    short = "Record mismatch"++    long =+        "Explanation: You can only use the ❰" <> op <> "❱ operator to combine records if they both  \n\+        \store terms or both store types.                                                \n\+        \                                                                                \n\+        \                                                                                \n\+        \    ┌──────────────────────────────┐                                            \n\+        \    │ { foo = 1 } " <> op <> " { baz = True } │  Valid: Both records store terms           \n\+        \    └──────────────────────────────┘                                            \n\+        \                                                                                \n\+        \                                                                                \n\+        \    ┌─────────────────────────────────┐                                         \n\+        \    │ { foo = Bool } " <> op <> " { bar = Text } │  Valid: Both records store types        \n\+        \    └─────────────────────────────────┘                                         \n\+        \                                                                                \n\+        \                                                                                \n\+        \... but you cannot combine two records if one of the records stores types and   \n\+        \the other record stores terms.                                                  \n\+        \                                                                                \n\+        \For example, the following expression is " <> _NOT <> " valid:                  \n\+        \                                                                                \n\+        \                                                                                \n\+        \      Record of terms                                                           \n\+        \      ⇩                                                                         \n\+        \    ┌──────────────────────────────┐                                            \n\+        \    │ { foo = 1 } " <> op <> " { bar = Text } │  Invalid: Mixing terms and types           \n\+        \    └──────────────────────────────┘                                            \n\+        \                               ⇧                                                \n\+        \                               Record of types                                  \n\+        \                                                                                \n\+        \                                                                                \n\+        \You tried to combine the following record:                                      \n\+        \                                                                                \n\+        \" <> txt0 <> "\n\+        \                                                                                \n\+        \... which stores " <> class0 <> ", with the following record:                   \n\+        \                                                                                \n\+        \" <> txt1 <> "\n\+        \                                                                                \n\+        \... which stores " <> class1 <> ".                                              \n"+      where+        op   = pretty c++        txt0 = insert expr0+        txt1 = insert expr1++        toClass Type = "terms"+        toClass Kind = "types"++        class0 = toClass const0+        class1 = toClass const1  prettyTypeMessage (CombineTypesRequiresRecordType expr0 expr1) =     ErrorMessages {..}
tests/Import.hs view
@@ -8,9 +8,9 @@ import Control.Exception (catch, throwIO) import Data.Monoid ((<>)) +import qualified Control.Monad.Trans.State.Strict as State import qualified Data.Text import qualified Data.Text.IO-import qualified Dhall.Context import qualified Dhall.Parser import qualified Dhall.Import import qualified Test.Tasty@@ -47,6 +47,14 @@                 "works"                 "./tests/import/data/foo/bar"                 "./tests/import/relative.dhall"+            , shouldNotFailRelative+                "a semantic integrity check if fields are reordered"+                "./tests/import/"+                "./tests/import/fieldOrderC.dhall"+            , shouldNotFailRelative+                "a semantic integrity check when importing an expression using `constructors`"+                "./tests/import/"+                "./tests/import/issue553B.dhall"             ]         ] @@ -65,7 +73,9 @@     expr <- case Dhall.Parser.exprFromText mempty text of                      Left  err  -> throwIO err                      Right expr -> return expr-    _ <- Dhall.Import.loadDirWith dir Dhall.Import.exprFromImport Dhall.Context.empty (const Nothing) expr++    _ <- State.evalStateT (Dhall.Import.loadWith expr) (Dhall.Import.emptyStatus dir)+     return ())  shouldFail :: Int -> Text -> FilePath -> TestTree
tests/Normalization.hs view
@@ -186,6 +186,7 @@         [ shouldNormalize "Natural/plus"   "naturalPlus"         , shouldNormalize "Optional/fold"  "optionalFold"         , shouldNormalize "Optional/build" "optionalBuild"+        , shouldNormalize "Natural/build"  "naturalBuild"         ]  conversions :: TestTree
+ tests/QuickCheck.hs view
@@ -0,0 +1,347 @@+{-# LANGUAGE OverloadedStrings  #-}+{-# LANGUAGE RecordWildCards    #-}+{-# LANGUAGE StandaloneDeriving #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}++module QuickCheck where++import Codec.Serialise (DeserialiseFailure(..))+import Control.Monad (guard)+import Data.Hashable (Hashable)+import Data.HashMap.Strict.InsOrd (InsOrdHashMap)+import Dhall.Core+    ( Chunks(..)+    , Const(..)+    , Directory(..)+    , Expr(..)+    , File(..)+    , FilePrefix(..)+    , Import(..)+    , ImportHashed(..)+    , ImportMode(..)+    , ImportType(..)+    , Scheme(..)+    , URL(..)+    , Var(..)+    )+import Numeric.Natural (Natural)+import Test.QuickCheck+    (Arbitrary(..), Gen, Property, genericShrink, (===))+import Test.QuickCheck.Instances ()+import Test.Tasty (TestTree)++import qualified Codec.Serialise+import qualified Data.Coerce+import qualified Data.HashMap.Strict.InsOrd+import qualified Data.Sequence+import qualified Dhall.Binary+import qualified Dhall.Core+import qualified Test.QuickCheck+import qualified Test.Tasty.QuickCheck++newtype DeserialiseFailureWithEq = D DeserialiseFailure+    deriving (Show)++instance Eq DeserialiseFailureWithEq where+    D (DeserialiseFailure aL bL) == D (DeserialiseFailure aR bR) =+        aL == aR && bL == bR++lift0 :: a -> Gen a+lift0 = pure++lift1 :: Arbitrary a => (a -> b) -> Gen b+lift1 f = f <$> arbitrary++lift2 :: (Arbitrary a, Arbitrary b) => (a -> b -> c) -> Gen c+lift2 f = f <$> arbitrary <*> arbitrary++lift3 :: (Arbitrary a, Arbitrary b, Arbitrary c) => (a -> b -> c -> d) -> Gen d+lift3 f = f <$> arbitrary <*> arbitrary <*> arbitrary++lift4+    :: (Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d)+    => (a -> b -> c -> d -> e) -> Gen e+lift4 f = f <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary++lift5+    :: ( Arbitrary a+       , Arbitrary b+       , Arbitrary c+       , Arbitrary d+       , Arbitrary e+       )+    => (a -> b -> c -> d -> e -> f) -> Gen f+lift5 f =+      f <$> arbitrary+        <*> arbitrary+        <*> arbitrary+        <*> arbitrary+        <*> arbitrary++lift6+    :: ( Arbitrary a+       , Arbitrary b+       , Arbitrary c+       , Arbitrary d+       , Arbitrary e+       , Arbitrary f+       )+    => (a -> b -> c -> d -> e -> f -> g) -> Gen g+lift6 f =+      f <$> arbitrary+        <*> arbitrary+        <*> arbitrary+        <*> arbitrary+        <*> arbitrary+        <*> arbitrary++natural :: (Arbitrary a, Num a) => Gen a+natural =+    Test.QuickCheck.frequency+        [ (7, arbitrary)+        , (1, fmap (\x -> x + (2 ^ (64 :: Int))) arbitrary)+        ]++integer :: (Arbitrary a, Num a) => Gen a+integer =+    Test.QuickCheck.frequency+        [ (7, arbitrary)+        , (1, fmap (\x -> x + (2 ^ (64 :: Int))) arbitrary)+        , (1, fmap (\x -> x - (2 ^ (64 :: Int))) arbitrary)+        ]++instance (Eq k, Hashable k, Arbitrary k, Arbitrary v) => Arbitrary (InsOrdHashMap k v) where+    arbitrary = do+        n   <- Test.QuickCheck.choose (0, 2)+        kvs <- Test.QuickCheck.vectorOf n ((,) <$> arbitrary <*> arbitrary)+        return (Data.HashMap.Strict.InsOrd.fromList kvs)++    shrink =+            map Data.HashMap.Strict.InsOrd.fromList+        .   shrink+        .   Data.HashMap.Strict.InsOrd.toList++instance (Arbitrary s, Arbitrary a) => Arbitrary (Chunks s a) where+    arbitrary = do+        n <- Test.QuickCheck.choose (0, 2)+        Chunks <$> Test.QuickCheck.vectorOf n arbitrary <*> arbitrary++    shrink = genericShrink++instance Arbitrary Const where+    arbitrary = Test.QuickCheck.oneof [ pure Type, pure Kind ]++    shrink = genericShrink++instance Arbitrary Directory where+    arbitrary = lift1 Directory++    shrink = genericShrink++averageDepth :: Natural+averageDepth = 3++averageNumberOfSubExpressions :: Double+averageNumberOfSubExpressions = 1 - 1 / fromIntegral averageDepth++probabilityOfNullaryConstructor :: Double+probabilityOfNullaryConstructor = 1 / fromIntegral averageDepth++numberOfConstructors :: Natural+numberOfConstructors = 50++instance (Arbitrary s, Arbitrary a) => Arbitrary (Expr s a) where+    arbitrary =+        Test.QuickCheck.suchThat+            (Test.QuickCheck.frequency+                [ ( 7, lift1 Const)+                , ( 7, lift1 Var)+                , ( 1, Test.QuickCheck.oneof [ lift2 (Lam "_"), lift3 Lam ])+                , ( 1, Test.QuickCheck.oneof [ lift2 (Pi "_"), lift3 Pi ])+                , ( 1, lift2 App)+                , ( 1, Test.QuickCheck.oneof [ lift3 (Let "_"), lift4 Let ])+                , ( 1, lift2 Annot)+                , ( 7, lift0 Bool)+                , ( 7, lift1 BoolLit)+                , ( 1, lift2 BoolAnd)+                , ( 1, lift2 BoolOr)+                , ( 1, lift2 BoolEQ)+                , ( 1, lift2 BoolNE)+                , ( 1, lift3 BoolIf)+                , ( 7, lift0 Natural)+                , ( 7, fmap NaturalLit natural)+                , ( 7, lift0 NaturalFold)+                , ( 7, lift0 NaturalBuild)+                , ( 7, lift0 NaturalIsZero)+                , ( 7, lift0 NaturalEven)+                , ( 7, lift0 NaturalOdd)+                , ( 7, lift0 NaturalToInteger)+                , ( 7, lift0 NaturalShow)+                , ( 1, lift2 NaturalPlus)+                , ( 1, lift2 NaturalTimes)+                , ( 7, lift0 Integer)+                , ( 7, fmap IntegerLit integer)+                , ( 7, lift0 IntegerShow)+                , ( 7, lift0 Double)+                , ( 7, lift1 DoubleLit)+                , ( 7, lift0 DoubleShow)+                , ( 7, lift0 Text)+                , ( 1, lift1 TextLit)+                , ( 1, lift2 TextAppend)+                , ( 7, lift0 List)+                , let listLit = do+                          n  <- Test.QuickCheck.choose (0, 3)+                          xs <- Test.QuickCheck.vectorOf n arbitrary+                          let ys = Data.Sequence.fromList xs+                          ListLit <$> arbitrary <*> pure ys++                  in  ( 1, listLit)+                , ( 1, lift2 ListAppend)+                , ( 7, lift0 ListBuild)+                , ( 7, lift0 ListFold)+                , ( 7, lift0 ListLength)+                , ( 7, lift0 ListHead)+                , ( 7, lift0 ListLast)+                , ( 7, lift0 ListIndexed)+                , ( 7, lift0 ListReverse)+                , ( 7, lift0 Optional)+                , ( 1, lift2 OptionalLit)+                , ( 7, lift0 OptionalFold)+                , ( 7, lift0 OptionalBuild)+                , ( 1, lift1 Record)+                , ( 1, lift1 RecordLit)+                , ( 1, lift1 Union)+                , ( 1, lift3 UnionLit)+                , ( 1, lift2 Combine)+                , ( 1, lift2 CombineTypes)+                , ( 1, lift2 Prefer)+                , ( 1, lift3 Merge)+                , ( 1, lift1 Constructors)+                , ( 1, lift2 Field)+                , ( 1, lift2 Project)+                , ( 7, lift1 Embed)+                ]+            )+            standardizedExpression++    shrink expression = filter standardizedExpression (genericShrink expression)++standardizedExpression :: Expr s a -> Bool+standardizedExpression (ListLit  Nothing  xs) = not (Data.Sequence.null xs)+standardizedExpression (ListLit (Just _ ) xs) = Data.Sequence.null xs+standardizedExpression (Note _ _            ) = False+standardizedExpression  _                     = True++instance Arbitrary File where+    arbitrary = lift2 File++    shrink = genericShrink++instance Arbitrary FilePrefix where+    arbitrary = Test.QuickCheck.oneof [ pure Absolute, pure Here, pure Home ]++    shrink = genericShrink++instance Arbitrary ImportType where+    arbitrary =+        Test.QuickCheck.suchThat+            (Test.QuickCheck.oneof+                [ lift2 Local+                , lift5 (\a b c d e -> Remote (URL a b c d e Nothing))+                , lift1 Env+                , lift0 Missing+                ]+            )+            standardizedImportType++    shrink importType =+        filter standardizedImportType (genericShrink importType)++standardizedImportType :: ImportType -> Bool+standardizedImportType (Remote (URL _ _ _ _ _ (Just _))) = False+standardizedImportType  _                                = True++instance Arbitrary ImportHashed where+    arbitrary =+        Test.QuickCheck.suchThat+            (lift1 (ImportHashed Nothing))+            standardizedImportHashed++    shrink (ImportHashed { importType = oldImportType, .. }) = do+        newImportType <- shrink oldImportType+        let importHashed = ImportHashed { importType = newImportType, .. }+        guard (standardizedImportHashed importHashed)+        return importHashed++standardizedImportHashed :: ImportHashed -> Bool+standardizedImportHashed (ImportHashed (Just _) _) = False+standardizedImportHashed  _                        = True++-- The standard does not yet specify how to encode `as Text`, so don't test it+-- yet+instance Arbitrary ImportMode where+    arbitrary = lift0 Code++    shrink = genericShrink++instance Arbitrary Import where+    arbitrary = lift2 Import++    shrink = genericShrink++instance Arbitrary Scheme where+    arbitrary = Test.QuickCheck.oneof [ pure HTTP, pure HTTPS ]++    shrink = genericShrink++instance Arbitrary URL where+    arbitrary = lift6 URL++    shrink = genericShrink++instance Arbitrary Var where+    arbitrary =+        Test.QuickCheck.oneof+            [ fmap (V "_") natural+            , lift1 (\t -> V t 0)+            , lift1 V <*> natural+            ]++    shrink = genericShrink++binaryRoundtrip :: Expr () Import -> Property+binaryRoundtrip expression =+        wrap+            (fmap+                Dhall.Binary.decode+                (Codec.Serialise.deserialiseOrFail+                  (Codec.Serialise.serialise+                    (Dhall.Binary.encode Dhall.Binary.defaultProtocolVersion expression)+                  )+                )+            )+    === wrap (Right (Right expression))+  where+    wrap+        :: Either DeserialiseFailure       a+        -> Either DeserialiseFailureWithEq a+    wrap = Data.Coerce.coerce++isNormalizedIsConsistentWithNormalize :: Expr () Import -> Property+isNormalizedIsConsistentWithNormalize expression =+        Dhall.Core.isNormalized expression+    === (Dhall.Core.normalize expression == expression)++quickcheckTests :: TestTree+quickcheckTests+    = Test.Tasty.QuickCheck.testProperties+        "QuickCheck"+        [ ( "Binary serialization should round-trip"+          , Test.QuickCheck.property binaryRoundtrip+          )+        , ( "isNormalized should be consistent with normalize"+          , Test.QuickCheck.property+              (Test.QuickCheck.withMaxSuccess 10000 isNormalizedIsConsistentWithNormalize)+          )+        ]
tests/Tests.hs view
@@ -3,6 +3,7 @@ import Normalization (normalizationTests) import Parser (parserTests) import Regression (regressionTests)+import QuickCheck (quickcheckTests) import Tutorial (tutorialTests) import TypeCheck (typecheckTests) import Format (formatTests)@@ -19,6 +20,7 @@         , formatTests         , typecheckTests         , importTests+        , quickcheckTests         ]  main :: IO ()
tests/TypeCheck.hs view
@@ -4,6 +4,9 @@  import Data.Monoid (mempty, (<>)) import Data.Text (Text)+import Dhall.Import (Imported)+import Dhall.Parser (Src)+import Dhall.TypeCheck (TypeError, X) import Test.Tasty (TestTree)  import qualified Control.Exception@@ -43,6 +46,13 @@         , should             "correctly handle α-equivalent merge alternatives"             "mergeEquivalence"++        , shouldNotTypeCheck+            "combining records of terms and types"+            "combineMixedRecords"+        , shouldNotTypeCheck+            "preferring a record of types over a record of terms"+            "preferMixedRecords"         ]  should :: Text -> Text -> TestTree@@ -64,3 +74,26 @@         case Dhall.TypeCheck.typeOf resolvedExpr of             Left  err -> Control.Exception.throwIO err             Right _   -> return ()++shouldNotTypeCheck :: Text -> Text -> TestTree+shouldNotTypeCheck name basename =+    Test.Tasty.HUnit.testCase (Data.Text.unpack name) $ do+        let code = "./tests/typecheck/" <> basename <> ".dhall"++        expression <- case Dhall.Parser.exprFromText mempty code of+            Left  exception  -> Control.Exception.throwIO exception+            Right expression -> return expression++        let io :: IO Bool+            io = do+                _ <- Dhall.Import.load expression+                return True++        let handler :: Imported (TypeError Src X)-> IO Bool+            handler _ = return False++        typeChecked <- Control.Exception.handle handler io++        if typeChecked+            then fail (Data.Text.unpack code <> " should not have type-checked")+            else return ()
+ tests/import/fieldOrderA.dhall view
@@ -0,0 +1,1 @@+{ foo = 1, bar = True }
+ tests/import/fieldOrderB.dhall view
@@ -0,0 +1,1 @@+{ bar = True, foo = 1 }
+ tests/import/fieldOrderC.dhall view
@@ -0,0 +1,3 @@+{ example0 = ./fieldOrderA.dhall sha256:c8e5944a964f1b35da4f2a7dc16b8de0221a518997fd84b0363a955d8f4459a4 +, example1 = ./fieldOrderB.dhall sha256:c8e5944a964f1b35da4f2a7dc16b8de0221a518997fd84b0363a955d8f4459a4 +}
+ tests/import/issue553A.dhall view
@@ -0,0 +1,5 @@+    let T = < A : {} | B : {} >++in  let t = constructors T++in  t
+ tests/import/issue553B.dhall view
@@ -0,0 +1,1 @@+./issue553A.dhall sha256:2324a5352105b6835db60b127c7096c5d5b81027969ccd368d163edc5d9692e0
tests/normalization/naturalBuildB.dhall view
@@ -1,7 +1,7 @@ { example0 =-    +1+    1 , example1 =-    +1+    1 , example2 =-    λ(id : ∀(a : Type) → a → a) → id Natural +1+    λ(id : ∀(a : Type) → a → a) → id Natural 1 }
+ tests/typecheck/combineMixedRecords.dhall view
@@ -0,0 +1,1 @@+{ foo = 1 } ∧ { bar = Text }
+ tests/typecheck/preferMixedRecords.dhall view
@@ -0,0 +1,1 @@+{ foo = 1 } ⫽ { bar = Text }