dhall 1.26.1 → 1.27.0
raw patch · 212 files changed
+8031/−6646 lines, 212 filesdep +hashabledep +special-valuesdep ~eitherdep ~unordered-containersbinary-addedPVP ok
version bump matches the API change (PVP)
Dependencies added: hashable, special-values
Dependency ranges changed: either, unordered-containers
API changes (from Hackage documentation)
- Dhall: class GenericInject f
- Dhall: class GenericInterpret f
- Dhall: class Inject a
- Dhall: class Interpret a
- Dhall: genericInject :: (Generic a, GenericInject (Rep a)) => InputType a
- Dhall: genericInjectWith :: GenericInject f => InterpretOptions -> State Int (InputType (f a))
- Dhall: instance (Dhall.Inject a, Dhall.Inject b) => Dhall.Inject (a, b)
- Dhall: instance (Dhall.Inject a, Dhall.Interpret b) => Dhall.Interpret (a -> b)
- Dhall: instance (Dhall.Interpret a, Dhall.Interpret b) => Dhall.Interpret (a, b)
- Dhall: instance (GHC.Base.Functor f, Dhall.Interpret (f (Dhall.Result f))) => Dhall.Interpret (Data.Fix.Fix f)
- Dhall: instance (GHC.Generics.Selector s, Dhall.Inject a) => Dhall.GenericInject (GHC.Generics.M1 GHC.Generics.S s (GHC.Generics.K1 i a))
- Dhall: instance (GHC.Generics.Selector s, Dhall.Interpret a) => Dhall.GenericInterpret (GHC.Generics.M1 GHC.Generics.S s (GHC.Generics.K1 i a))
- Dhall: instance (GHC.Generics.Selector s1, GHC.Generics.Selector s2, Dhall.Inject a1, Dhall.Inject a2) => Dhall.GenericInject (GHC.Generics.M1 GHC.Generics.S s1 (GHC.Generics.K1 i1 a1) GHC.Generics.:*: GHC.Generics.M1 GHC.Generics.S s2 (GHC.Generics.K1 i2 a2))
- Dhall: instance (GHC.Generics.Selector s1, GHC.Generics.Selector s2, Dhall.Interpret a1, Dhall.Interpret a2) => Dhall.GenericInterpret (GHC.Generics.M1 GHC.Generics.S s1 (GHC.Generics.K1 i1 a1) GHC.Generics.:*: GHC.Generics.M1 GHC.Generics.S s2 (GHC.Generics.K1 i2 a2))
- Dhall: instance Dhall.GenericInject GHC.Generics.U1
- Dhall: instance Dhall.GenericInterpret GHC.Generics.U1
- Dhall: instance Dhall.GenericInterpret GHC.Generics.V1
- Dhall: instance Dhall.Inject ()
- Dhall: instance Dhall.Inject Data.Scientific.Scientific
- Dhall: instance Dhall.Inject Data.Text.Internal.Lazy.Text
- Dhall: instance Dhall.Inject Data.Text.Internal.Text
- Dhall: instance Dhall.Inject GHC.Base.String
- Dhall: instance Dhall.Inject GHC.Integer.Type.Integer
- Dhall: instance Dhall.Inject GHC.Natural.Natural
- Dhall: instance Dhall.Inject GHC.Types.Bool
- Dhall: instance Dhall.Inject GHC.Types.Double
- Dhall: instance Dhall.Inject GHC.Types.Int
- Dhall: instance Dhall.Inject GHC.Word.Word16
- Dhall: instance Dhall.Inject GHC.Word.Word32
- Dhall: instance Dhall.Inject GHC.Word.Word64
- Dhall: instance Dhall.Inject GHC.Word.Word8
- Dhall: instance Dhall.Inject a => Dhall.Inject (Data.Sequence.Internal.Seq a)
- Dhall: instance Dhall.Inject a => Dhall.Inject (Data.Set.Internal.Set a)
- Dhall: instance Dhall.Inject a => Dhall.Inject (Data.Vector.Vector a)
- Dhall: instance Dhall.Inject a => Dhall.Inject (GHC.Maybe.Maybe a)
- Dhall: instance Dhall.Inject a => Dhall.Inject [a]
- Dhall: instance Dhall.Interpret (f (Dhall.Result f)) => Dhall.Interpret (Dhall.Result f)
- Dhall: instance Dhall.Interpret Data.Scientific.Scientific
- Dhall: instance Dhall.Interpret Data.Text.Internal.Lazy.Text
- Dhall: instance Dhall.Interpret Data.Text.Internal.Text
- Dhall: instance Dhall.Interpret GHC.Integer.Type.Integer
- Dhall: instance Dhall.Interpret GHC.Natural.Natural
- Dhall: instance Dhall.Interpret GHC.Types.Bool
- Dhall: instance Dhall.Interpret GHC.Types.Double
- Dhall: instance Dhall.Interpret [GHC.Types.Char]
- Dhall: instance Dhall.Interpret a => Dhall.Interpret (Data.Sequence.Internal.Seq a)
- Dhall: instance Dhall.Interpret a => Dhall.Interpret (Data.Vector.Vector a)
- Dhall: instance Dhall.Interpret a => Dhall.Interpret (GHC.Maybe.Maybe a)
- Dhall: instance Dhall.Interpret a => Dhall.Interpret [a]
- Dhall: instance forall k (c :: GHC.Generics.Meta) (f :: k -> *) (g :: k -> *) (h :: k -> *). (GHC.Generics.Constructor c, Dhall.GenericInject (f GHC.Generics.:+: g), Dhall.GenericInject h) => Dhall.GenericInject ((f GHC.Generics.:+: g) GHC.Generics.:+: GHC.Generics.M1 GHC.Generics.C c h)
- Dhall: instance forall k (c :: GHC.Generics.Meta) (f :: k -> *) (g :: k -> *) (h :: k -> *). (GHC.Generics.Constructor c, Dhall.GenericInject f, Dhall.GenericInject (g GHC.Generics.:+: h)) => Dhall.GenericInject (GHC.Generics.M1 GHC.Generics.C c f GHC.Generics.:+: (g GHC.Generics.:+: h))
- Dhall: instance forall k (c :: GHC.Generics.Meta) (f :: k -> *) (g :: k -> *) (h :: k -> *). (GHC.Generics.Constructor c, Dhall.GenericInterpret (f GHC.Generics.:+: g), Dhall.GenericInterpret h) => Dhall.GenericInterpret ((f GHC.Generics.:+: g) GHC.Generics.:+: GHC.Generics.M1 GHC.Generics.C c h)
- Dhall: instance forall k (c :: GHC.Generics.Meta) (f :: k -> *) (g :: k -> *) (h :: k -> *). (GHC.Generics.Constructor c, Dhall.GenericInterpret f, Dhall.GenericInterpret (g GHC.Generics.:+: h)) => Dhall.GenericInterpret (GHC.Generics.M1 GHC.Generics.C c f GHC.Generics.:+: (g GHC.Generics.:+: h))
- Dhall: instance forall k (c1 :: GHC.Generics.Meta) (c2 :: GHC.Generics.Meta) (f1 :: k -> *) (f2 :: k -> *). (GHC.Generics.Constructor c1, GHC.Generics.Constructor c2, Dhall.GenericInject f1, Dhall.GenericInject f2) => Dhall.GenericInject (GHC.Generics.M1 GHC.Generics.C c1 f1 GHC.Generics.:+: GHC.Generics.M1 GHC.Generics.C c2 f2)
- Dhall: instance forall k (c1 :: GHC.Generics.Meta) (c2 :: GHC.Generics.Meta) (f1 :: k -> *) (f2 :: k -> *). (GHC.Generics.Constructor c1, GHC.Generics.Constructor c2, Dhall.GenericInterpret f1, Dhall.GenericInterpret f2) => Dhall.GenericInterpret (GHC.Generics.M1 GHC.Generics.C c1 f1 GHC.Generics.:+: GHC.Generics.M1 GHC.Generics.C c2 f2)
- Dhall: instance forall k (f :: k -> *) (c :: GHC.Generics.Meta). Dhall.GenericInject f => Dhall.GenericInject (GHC.Generics.M1 GHC.Generics.C c f)
- Dhall: instance forall k (f :: k -> *) (c :: GHC.Generics.Meta). Dhall.GenericInterpret f => Dhall.GenericInterpret (GHC.Generics.M1 GHC.Generics.C c f)
- Dhall: instance forall k (f :: k -> *) (d :: GHC.Generics.Meta). Dhall.GenericInject f => Dhall.GenericInject (GHC.Generics.M1 GHC.Generics.D d f)
- Dhall: instance forall k (f :: k -> *) (d :: GHC.Generics.Meta). Dhall.GenericInterpret f => Dhall.GenericInterpret (GHC.Generics.M1 GHC.Generics.D d f)
- Dhall: instance forall k (f :: k -> *) (g :: k -> *) (h :: k -> *) (i :: k -> *). (Dhall.GenericInject (f GHC.Generics.:*: g), Dhall.GenericInject (h GHC.Generics.:*: i)) => Dhall.GenericInject ((f GHC.Generics.:*: g) GHC.Generics.:*: (h GHC.Generics.:*: i))
- Dhall: instance forall k (f :: k -> *) (g :: k -> *) (h :: k -> *) (i :: k -> *). (Dhall.GenericInject (f GHC.Generics.:+: g), Dhall.GenericInject (h GHC.Generics.:+: i)) => Dhall.GenericInject ((f GHC.Generics.:+: g) GHC.Generics.:+: (h GHC.Generics.:+: i))
- Dhall: instance forall k (f :: k -> *) (g :: k -> *) (h :: k -> *) (i :: k -> *). (Dhall.GenericInterpret (f GHC.Generics.:*: g), Dhall.GenericInterpret (h GHC.Generics.:*: i)) => Dhall.GenericInterpret ((f GHC.Generics.:*: g) GHC.Generics.:*: (h GHC.Generics.:*: i))
- Dhall: instance forall k (f :: k -> *) (g :: k -> *) (h :: k -> *) (i :: k -> *). (Dhall.GenericInterpret (f GHC.Generics.:+: g), Dhall.GenericInterpret (h GHC.Generics.:+: i)) => Dhall.GenericInterpret ((f GHC.Generics.:+: g) GHC.Generics.:+: (h GHC.Generics.:+: i))
- Dhall: instance forall k (f :: k -> *) (g :: k -> *) (s :: GHC.Generics.Meta) a i. (Dhall.GenericInject (f GHC.Generics.:*: g), GHC.Generics.Selector s, Dhall.Inject a) => Dhall.GenericInject ((f GHC.Generics.:*: g) GHC.Generics.:*: GHC.Generics.M1 GHC.Generics.S s (GHC.Generics.K1 i a))
- Dhall: instance forall k (f :: k -> *) (g :: k -> *) (s :: GHC.Generics.Meta) a i. (Dhall.GenericInterpret (f GHC.Generics.:*: g), GHC.Generics.Selector s, Dhall.Interpret a) => Dhall.GenericInterpret ((f GHC.Generics.:*: g) GHC.Generics.:*: GHC.Generics.M1 GHC.Generics.S s (GHC.Generics.K1 i a))
- Dhall: instance forall k (s :: GHC.Generics.Meta) a (f :: k -> *) (g :: k -> *) i. (GHC.Generics.Selector s, Dhall.Inject a, Dhall.GenericInject (f GHC.Generics.:*: g)) => Dhall.GenericInject (GHC.Generics.M1 GHC.Generics.S s (GHC.Generics.K1 i a) GHC.Generics.:*: (f GHC.Generics.:*: g))
- Dhall: instance forall k (s :: GHC.Generics.Meta) a (f :: k -> *) (g :: k -> *) i. (GHC.Generics.Selector s, Dhall.Interpret a, Dhall.GenericInterpret (f GHC.Generics.:*: g)) => Dhall.GenericInterpret (GHC.Generics.M1 GHC.Generics.S s (GHC.Generics.K1 i a) GHC.Generics.:*: (f GHC.Generics.:*: g))
- Dhall.Binary: instance Dhall.Binary.FromTerm Dhall.Core.Import
- Dhall.Binary: instance Dhall.Binary.FromTerm a => Dhall.Binary.FromTerm (Dhall.Core.Expr s a)
- Dhall.Binary: instance Dhall.Binary.ToTerm Dhall.Core.Import
- Dhall.Binary: instance Dhall.Binary.ToTerm a => Dhall.Binary.ToTerm (Dhall.Core.Expr Data.Void.Void a)
- Dhall.Core: instance (Control.DeepSeq.NFData s, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Dhall.Core.Binding s a)
- Dhall.Core: instance (Control.DeepSeq.NFData s, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Dhall.Core.Chunks s a)
- Dhall.Core: instance (Control.DeepSeq.NFData s, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Dhall.Core.Expr s a)
- Dhall.Core: instance (Data.Data.Data s, Data.Data.Data a) => Data.Data.Data (Dhall.Core.Binding s a)
- Dhall.Core: instance (Data.Data.Data s, Data.Data.Data a) => Data.Data.Data (Dhall.Core.Chunks s a)
- Dhall.Core: instance (Data.Data.Data s, Data.Data.Data a) => Data.Data.Data (Dhall.Core.Expr s a)
- Dhall.Core: instance (GHC.Classes.Eq s, GHC.Classes.Eq a) => GHC.Classes.Eq (Dhall.Core.Binding s a)
- Dhall.Core: instance (GHC.Classes.Eq s, GHC.Classes.Eq a) => GHC.Classes.Eq (Dhall.Core.Chunks s a)
- Dhall.Core: instance (GHC.Classes.Eq s, GHC.Classes.Eq a) => GHC.Classes.Eq (Dhall.Core.Expr s a)
- Dhall.Core: instance (GHC.Classes.Ord s, GHC.Classes.Ord a) => GHC.Classes.Ord (Dhall.Core.Binding s a)
- Dhall.Core: instance (GHC.Classes.Ord s, GHC.Classes.Ord a) => GHC.Classes.Ord (Dhall.Core.Chunks s a)
- Dhall.Core: instance (GHC.Classes.Ord s, GHC.Classes.Ord a) => GHC.Classes.Ord (Dhall.Core.Expr s a)
- Dhall.Core: instance (GHC.Show.Show s, GHC.Show.Show a) => GHC.Show.Show (Dhall.Core.Binding s a)
- Dhall.Core: instance (GHC.Show.Show s, GHC.Show.Show a) => GHC.Show.Show (Dhall.Core.Chunks s a)
- Dhall.Core: instance (GHC.Show.Show s, GHC.Show.Show a) => GHC.Show.Show (Dhall.Core.Expr s a)
- Dhall.Core: instance (Language.Haskell.TH.Syntax.Lift s, Language.Haskell.TH.Syntax.Lift a, Data.Data.Data s, Data.Data.Data a) => Language.Haskell.TH.Syntax.Lift (Dhall.Core.Chunks s a)
- Dhall.Core: instance (Language.Haskell.TH.Syntax.Lift s, Language.Haskell.TH.Syntax.Lift a, Data.Data.Data s, Data.Data.Data a) => Language.Haskell.TH.Syntax.Lift (Dhall.Core.Expr s a)
- Dhall.Core: instance Control.DeepSeq.NFData Dhall.Core.Const
- Dhall.Core: instance Control.DeepSeq.NFData Dhall.Core.Directory
- Dhall.Core: instance Control.DeepSeq.NFData Dhall.Core.File
- Dhall.Core: instance Control.DeepSeq.NFData Dhall.Core.FilePrefix
- Dhall.Core: instance Control.DeepSeq.NFData Dhall.Core.Import
- Dhall.Core: instance Control.DeepSeq.NFData Dhall.Core.ImportHashed
- Dhall.Core: instance Control.DeepSeq.NFData Dhall.Core.ImportMode
- Dhall.Core: instance Control.DeepSeq.NFData Dhall.Core.ImportType
- Dhall.Core: instance Control.DeepSeq.NFData Dhall.Core.Scheme
- Dhall.Core: instance Control.DeepSeq.NFData Dhall.Core.URL
- Dhall.Core: instance Control.DeepSeq.NFData Dhall.Core.Var
- Dhall.Core: instance Data.Bifunctor.Bifunctor Dhall.Core.Binding
- Dhall.Core: instance Data.Bifunctor.Bifunctor Dhall.Core.Expr
- Dhall.Core: instance Data.Data.Data Dhall.Core.Const
- Dhall.Core: instance Data.Data.Data Dhall.Core.Var
- Dhall.Core: instance Data.Foldable.Foldable (Dhall.Core.Binding s)
- Dhall.Core: instance Data.Foldable.Foldable (Dhall.Core.Chunks s)
- Dhall.Core: instance Data.Foldable.Foldable (Dhall.Core.Expr s)
- Dhall.Core: instance Data.String.IsString (Dhall.Core.Chunks s a)
- Dhall.Core: instance Data.String.IsString (Dhall.Core.Expr s a)
- Dhall.Core: instance Data.String.IsString Dhall.Core.Var
- Dhall.Core: instance Data.Text.Prettyprint.Doc.Internal.Pretty Dhall.Core.Const
- Dhall.Core: instance Data.Text.Prettyprint.Doc.Internal.Pretty Dhall.Core.Directory
- Dhall.Core: instance Data.Text.Prettyprint.Doc.Internal.Pretty Dhall.Core.File
- Dhall.Core: instance Data.Text.Prettyprint.Doc.Internal.Pretty Dhall.Core.FilePrefix
- Dhall.Core: instance Data.Text.Prettyprint.Doc.Internal.Pretty Dhall.Core.Import
- Dhall.Core: instance Data.Text.Prettyprint.Doc.Internal.Pretty Dhall.Core.ImportHashed
- Dhall.Core: instance Data.Text.Prettyprint.Doc.Internal.Pretty Dhall.Core.ImportType
- Dhall.Core: instance Data.Text.Prettyprint.Doc.Internal.Pretty Dhall.Core.URL
- Dhall.Core: instance Data.Text.Prettyprint.Doc.Internal.Pretty Dhall.Core.Var
- Dhall.Core: instance Data.Text.Prettyprint.Doc.Internal.Pretty a => Data.Text.Prettyprint.Doc.Internal.Pretty (Dhall.Core.Expr s a)
- Dhall.Core: instance Data.Traversable.Traversable (Dhall.Core.Binding s)
- Dhall.Core: instance Data.Traversable.Traversable (Dhall.Core.Chunks s)
- Dhall.Core: instance Data.Traversable.Traversable (Dhall.Core.Expr s)
- Dhall.Core: instance GHC.Base.Applicative (Dhall.Core.Expr s)
- Dhall.Core: instance GHC.Base.Functor (Dhall.Core.Binding s)
- Dhall.Core: instance GHC.Base.Functor (Dhall.Core.Chunks s)
- Dhall.Core: instance GHC.Base.Functor (Dhall.Core.Expr s)
- Dhall.Core: instance GHC.Base.Monad (Dhall.Core.Expr s)
- Dhall.Core: instance GHC.Base.Monoid (Dhall.Core.Chunks s a)
- Dhall.Core: instance GHC.Base.Semigroup (Dhall.Core.Chunks s a)
- Dhall.Core: instance GHC.Base.Semigroup Dhall.Core.Directory
- Dhall.Core: instance GHC.Base.Semigroup Dhall.Core.File
- Dhall.Core: instance GHC.Base.Semigroup Dhall.Core.Import
- Dhall.Core: instance GHC.Base.Semigroup Dhall.Core.ImportHashed
- Dhall.Core: instance GHC.Base.Semigroup Dhall.Core.ImportType
- Dhall.Core: instance GHC.Classes.Eq Dhall.Core.Const
- Dhall.Core: instance GHC.Classes.Eq Dhall.Core.Directory
- Dhall.Core: instance GHC.Classes.Eq Dhall.Core.File
- Dhall.Core: instance GHC.Classes.Eq Dhall.Core.FilePrefix
- Dhall.Core: instance GHC.Classes.Eq Dhall.Core.Import
- Dhall.Core: instance GHC.Classes.Eq Dhall.Core.ImportHashed
- Dhall.Core: instance GHC.Classes.Eq Dhall.Core.ImportMode
- Dhall.Core: instance GHC.Classes.Eq Dhall.Core.ImportType
- Dhall.Core: instance GHC.Classes.Eq Dhall.Core.Scheme
- Dhall.Core: instance GHC.Classes.Eq Dhall.Core.URL
- Dhall.Core: instance GHC.Classes.Eq Dhall.Core.Var
- Dhall.Core: instance GHC.Classes.Ord Dhall.Core.Const
- Dhall.Core: instance GHC.Classes.Ord Dhall.Core.Directory
- Dhall.Core: instance GHC.Classes.Ord Dhall.Core.File
- Dhall.Core: instance GHC.Classes.Ord Dhall.Core.FilePrefix
- Dhall.Core: instance GHC.Classes.Ord Dhall.Core.Import
- Dhall.Core: instance GHC.Classes.Ord Dhall.Core.ImportHashed
- Dhall.Core: instance GHC.Classes.Ord Dhall.Core.ImportMode
- Dhall.Core: instance GHC.Classes.Ord Dhall.Core.ImportType
- Dhall.Core: instance GHC.Classes.Ord Dhall.Core.Scheme
- Dhall.Core: instance GHC.Classes.Ord Dhall.Core.URL
- Dhall.Core: instance GHC.Classes.Ord Dhall.Core.Var
- Dhall.Core: instance GHC.Enum.Bounded Dhall.Core.Const
- Dhall.Core: instance GHC.Enum.Enum Dhall.Core.Const
- Dhall.Core: instance GHC.Generics.Generic (Dhall.Core.Binding s a)
- 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.Const
- Dhall.Core: instance GHC.Show.Show Dhall.Core.Directory
- Dhall.Core: instance GHC.Show.Show Dhall.Core.File
- Dhall.Core: instance GHC.Show.Show Dhall.Core.FilePrefix
- Dhall.Core: instance GHC.Show.Show Dhall.Core.Import
- Dhall.Core: instance GHC.Show.Show Dhall.Core.ImportHashed
- Dhall.Core: instance GHC.Show.Show Dhall.Core.ImportMode
- Dhall.Core: instance GHC.Show.Show Dhall.Core.ImportType
- Dhall.Core: instance GHC.Show.Show Dhall.Core.Scheme
- Dhall.Core: instance GHC.Show.Show Dhall.Core.URL
- Dhall.Core: instance GHC.Show.Show Dhall.Core.Var
- Dhall.Core: instance Language.Haskell.TH.Syntax.Lift Dhall.Core.Const
- Dhall.Core: instance Language.Haskell.TH.Syntax.Lift Dhall.Core.Var
- Dhall.Hash: hash :: Censor -> IO ()
- Dhall.Import: instance Dhall.Import.Canonicalize Dhall.Core.Directory
- Dhall.Import: instance Dhall.Import.Canonicalize Dhall.Core.File
- Dhall.Import: instance Dhall.Import.Canonicalize Dhall.Core.Import
- Dhall.Import: instance Dhall.Import.Canonicalize Dhall.Core.ImportHashed
- Dhall.Import: instance Dhall.Import.Canonicalize Dhall.Core.ImportType
- Dhall.Parser.Expression: localRaw :: Parser ImportType
- Dhall.Parser.Expression: renderChunks :: Chunks s a -> Text
- Dhall.Util: File :: FilePath -> Input
+ Dhall: class FromDhall a
+ Dhall: class GenericFromDhall f
+ Dhall: class GenericToDhall f
+ Dhall: class ToDhall a
+ Dhall: genericToDhall :: (Generic a, GenericToDhall (Rep a)) => InputType a
+ Dhall: genericToDhallWith :: GenericToDhall f => InterpretOptions -> State Int (InputType (f a))
+ Dhall: hashMap :: (Eq k, Hashable k) => Type k -> Type v -> Type (HashMap k v)
+ Dhall: hashSetFromDistinctList :: (Hashable a, Ord a, Show a) => Type a -> Type (HashSet a)
+ Dhall: hashSetIgnoringDuplicates :: (Hashable a, Ord a) => Type a -> Type (HashSet a)
+ Dhall: instance (Dhall.FromDhall a, Data.Hashable.Class.Hashable a, GHC.Classes.Ord a, GHC.Show.Show a) => Dhall.FromDhall (Data.HashSet.Base.HashSet a)
+ Dhall: instance (Dhall.FromDhall a, Dhall.FromDhall b) => Dhall.FromDhall (a, b)
+ Dhall: instance (Dhall.FromDhall a, GHC.Classes.Ord a, GHC.Show.Show a) => Dhall.FromDhall (Data.Set.Internal.Set a)
+ Dhall: instance (Dhall.ToDhall a, Dhall.FromDhall b) => Dhall.FromDhall (a -> b)
+ Dhall: instance (Dhall.ToDhall a, Dhall.ToDhall b) => Dhall.ToDhall (a, b)
+ Dhall: instance (Dhall.ToDhall k, Dhall.ToDhall v) => Dhall.ToDhall (Data.HashMap.Base.HashMap k v)
+ Dhall: instance (Dhall.ToDhall k, Dhall.ToDhall v) => Dhall.ToDhall (Data.Map.Internal.Map k v)
+ Dhall: instance (GHC.Base.Functor f, Dhall.FromDhall (f (Dhall.Result f))) => Dhall.FromDhall (Data.Fix.Fix f)
+ Dhall: instance (GHC.Classes.Eq k, Data.Hashable.Class.Hashable k, Dhall.FromDhall k, Dhall.FromDhall v) => Dhall.FromDhall (Data.HashMap.Base.HashMap k v)
+ Dhall: instance (GHC.Classes.Ord k, Dhall.FromDhall k, Dhall.FromDhall v) => Dhall.FromDhall (Data.Map.Internal.Map k v)
+ Dhall: instance (GHC.Generics.Selector s, Dhall.FromDhall a) => Dhall.GenericFromDhall (GHC.Generics.M1 GHC.Generics.S s (GHC.Generics.K1 i a))
+ Dhall: instance (GHC.Generics.Selector s, Dhall.ToDhall a) => Dhall.GenericToDhall (GHC.Generics.M1 GHC.Generics.S s (GHC.Generics.K1 i a))
+ Dhall: instance (GHC.Generics.Selector s1, GHC.Generics.Selector s2, Dhall.FromDhall a1, Dhall.FromDhall a2) => Dhall.GenericFromDhall (GHC.Generics.M1 GHC.Generics.S s1 (GHC.Generics.K1 i1 a1) GHC.Generics.:*: GHC.Generics.M1 GHC.Generics.S s2 (GHC.Generics.K1 i2 a2))
+ Dhall: instance (GHC.Generics.Selector s1, GHC.Generics.Selector s2, Dhall.ToDhall a1, Dhall.ToDhall a2) => Dhall.GenericToDhall (GHC.Generics.M1 GHC.Generics.S s1 (GHC.Generics.K1 i1 a1) GHC.Generics.:*: GHC.Generics.M1 GHC.Generics.S s2 (GHC.Generics.K1 i2 a2))
+ Dhall: instance Dhall.FromDhall ()
+ Dhall: instance Dhall.FromDhall (f (Dhall.Result f)) => Dhall.FromDhall (Dhall.Result f)
+ Dhall: instance Dhall.FromDhall Data.Scientific.Scientific
+ Dhall: instance Dhall.FromDhall Data.Text.Internal.Lazy.Text
+ Dhall: instance Dhall.FromDhall Data.Text.Internal.Text
+ Dhall: instance Dhall.FromDhall Data.Void.Void
+ Dhall: instance Dhall.FromDhall GHC.Integer.Type.Integer
+ Dhall: instance Dhall.FromDhall GHC.Natural.Natural
+ Dhall: instance Dhall.FromDhall GHC.Types.Bool
+ Dhall: instance Dhall.FromDhall GHC.Types.Double
+ Dhall: instance Dhall.FromDhall [GHC.Types.Char]
+ Dhall: instance Dhall.FromDhall a => Dhall.FromDhall (Data.Sequence.Internal.Seq a)
+ Dhall: instance Dhall.FromDhall a => Dhall.FromDhall (Data.Vector.Vector a)
+ Dhall: instance Dhall.FromDhall a => Dhall.FromDhall (GHC.Maybe.Maybe a)
+ Dhall: instance Dhall.FromDhall a => Dhall.FromDhall [a]
+ Dhall: instance Dhall.GenericFromDhall GHC.Generics.U1
+ Dhall: instance Dhall.GenericFromDhall GHC.Generics.V1
+ Dhall: instance Dhall.GenericToDhall GHC.Generics.U1
+ Dhall: instance Dhall.ToDhall ()
+ Dhall: instance Dhall.ToDhall Data.Scientific.Scientific
+ Dhall: instance Dhall.ToDhall Data.Text.Internal.Lazy.Text
+ Dhall: instance Dhall.ToDhall Data.Text.Internal.Text
+ Dhall: instance Dhall.ToDhall Data.Void.Void
+ Dhall: instance Dhall.ToDhall GHC.Base.String
+ Dhall: instance Dhall.ToDhall GHC.Integer.Type.Integer
+ Dhall: instance Dhall.ToDhall GHC.Natural.Natural
+ Dhall: instance Dhall.ToDhall GHC.Types.Bool
+ Dhall: instance Dhall.ToDhall GHC.Types.Double
+ Dhall: instance Dhall.ToDhall GHC.Types.Int
+ Dhall: instance Dhall.ToDhall GHC.Types.Word
+ Dhall: instance Dhall.ToDhall GHC.Word.Word16
+ Dhall: instance Dhall.ToDhall GHC.Word.Word32
+ Dhall: instance Dhall.ToDhall GHC.Word.Word64
+ Dhall: instance Dhall.ToDhall GHC.Word.Word8
+ Dhall: instance Dhall.ToDhall a => Dhall.ToDhall (Data.HashSet.Base.HashSet a)
+ Dhall: instance Dhall.ToDhall a => Dhall.ToDhall (Data.Sequence.Internal.Seq a)
+ Dhall: instance Dhall.ToDhall a => Dhall.ToDhall (Data.Set.Internal.Set a)
+ Dhall: instance Dhall.ToDhall a => Dhall.ToDhall (Data.Vector.Vector a)
+ Dhall: instance Dhall.ToDhall a => Dhall.ToDhall (GHC.Maybe.Maybe a)
+ Dhall: instance Dhall.ToDhall a => Dhall.ToDhall [a]
+ Dhall: instance forall k (c :: GHC.Generics.Meta) (f :: k -> *) (g :: k -> *) (h :: k -> *). (GHC.Generics.Constructor c, Dhall.GenericFromDhall (f GHC.Generics.:+: g), Dhall.GenericFromDhall h) => Dhall.GenericFromDhall ((f GHC.Generics.:+: g) GHC.Generics.:+: GHC.Generics.M1 GHC.Generics.C c h)
+ Dhall: instance forall k (c :: GHC.Generics.Meta) (f :: k -> *) (g :: k -> *) (h :: k -> *). (GHC.Generics.Constructor c, Dhall.GenericFromDhall f, Dhall.GenericFromDhall (g GHC.Generics.:+: h)) => Dhall.GenericFromDhall (GHC.Generics.M1 GHC.Generics.C c f GHC.Generics.:+: (g GHC.Generics.:+: h))
+ Dhall: instance forall k (c :: GHC.Generics.Meta) (f :: k -> *) (g :: k -> *) (h :: k -> *). (GHC.Generics.Constructor c, Dhall.GenericToDhall (f GHC.Generics.:+: g), Dhall.GenericToDhall h) => Dhall.GenericToDhall ((f GHC.Generics.:+: g) GHC.Generics.:+: GHC.Generics.M1 GHC.Generics.C c h)
+ Dhall: instance forall k (c :: GHC.Generics.Meta) (f :: k -> *) (g :: k -> *) (h :: k -> *). (GHC.Generics.Constructor c, Dhall.GenericToDhall f, Dhall.GenericToDhall (g GHC.Generics.:+: h)) => Dhall.GenericToDhall (GHC.Generics.M1 GHC.Generics.C c f GHC.Generics.:+: (g GHC.Generics.:+: h))
+ Dhall: instance forall k (c1 :: GHC.Generics.Meta) (c2 :: GHC.Generics.Meta) (f1 :: k -> *) (f2 :: k -> *). (GHC.Generics.Constructor c1, GHC.Generics.Constructor c2, Dhall.GenericFromDhall f1, Dhall.GenericFromDhall f2) => Dhall.GenericFromDhall (GHC.Generics.M1 GHC.Generics.C c1 f1 GHC.Generics.:+: GHC.Generics.M1 GHC.Generics.C c2 f2)
+ Dhall: instance forall k (c1 :: GHC.Generics.Meta) (c2 :: GHC.Generics.Meta) (f1 :: k -> *) (f2 :: k -> *). (GHC.Generics.Constructor c1, GHC.Generics.Constructor c2, Dhall.GenericToDhall f1, Dhall.GenericToDhall f2) => Dhall.GenericToDhall (GHC.Generics.M1 GHC.Generics.C c1 f1 GHC.Generics.:+: GHC.Generics.M1 GHC.Generics.C c2 f2)
+ Dhall: instance forall k (f :: k -> *) (c :: GHC.Generics.Meta). Dhall.GenericFromDhall f => Dhall.GenericFromDhall (GHC.Generics.M1 GHC.Generics.C c f)
+ Dhall: instance forall k (f :: k -> *) (c :: GHC.Generics.Meta). Dhall.GenericToDhall f => Dhall.GenericToDhall (GHC.Generics.M1 GHC.Generics.C c f)
+ Dhall: instance forall k (f :: k -> *) (d :: GHC.Generics.Meta). Dhall.GenericFromDhall f => Dhall.GenericFromDhall (GHC.Generics.M1 GHC.Generics.D d f)
+ Dhall: instance forall k (f :: k -> *) (d :: GHC.Generics.Meta). Dhall.GenericToDhall f => Dhall.GenericToDhall (GHC.Generics.M1 GHC.Generics.D d f)
+ Dhall: instance forall k (f :: k -> *) (g :: k -> *) (h :: k -> *) (i :: k -> *). (Dhall.GenericFromDhall (f GHC.Generics.:*: g), Dhall.GenericFromDhall (h GHC.Generics.:*: i)) => Dhall.GenericFromDhall ((f GHC.Generics.:*: g) GHC.Generics.:*: (h GHC.Generics.:*: i))
+ Dhall: instance forall k (f :: k -> *) (g :: k -> *) (h :: k -> *) (i :: k -> *). (Dhall.GenericFromDhall (f GHC.Generics.:+: g), Dhall.GenericFromDhall (h GHC.Generics.:+: i)) => Dhall.GenericFromDhall ((f GHC.Generics.:+: g) GHC.Generics.:+: (h GHC.Generics.:+: i))
+ Dhall: instance forall k (f :: k -> *) (g :: k -> *) (h :: k -> *) (i :: k -> *). (Dhall.GenericToDhall (f GHC.Generics.:*: g), Dhall.GenericToDhall (h GHC.Generics.:*: i)) => Dhall.GenericToDhall ((f GHC.Generics.:*: g) GHC.Generics.:*: (h GHC.Generics.:*: i))
+ Dhall: instance forall k (f :: k -> *) (g :: k -> *) (h :: k -> *) (i :: k -> *). (Dhall.GenericToDhall (f GHC.Generics.:+: g), Dhall.GenericToDhall (h GHC.Generics.:+: i)) => Dhall.GenericToDhall ((f GHC.Generics.:+: g) GHC.Generics.:+: (h GHC.Generics.:+: i))
+ Dhall: instance forall k (f :: k -> *) (g :: k -> *) (s :: GHC.Generics.Meta) a i. (Dhall.GenericFromDhall (f GHC.Generics.:*: g), GHC.Generics.Selector s, Dhall.FromDhall a) => Dhall.GenericFromDhall ((f GHC.Generics.:*: g) GHC.Generics.:*: GHC.Generics.M1 GHC.Generics.S s (GHC.Generics.K1 i a))
+ Dhall: instance forall k (f :: k -> *) (g :: k -> *) (s :: GHC.Generics.Meta) a i. (Dhall.GenericToDhall (f GHC.Generics.:*: g), GHC.Generics.Selector s, Dhall.ToDhall a) => Dhall.GenericToDhall ((f GHC.Generics.:*: g) GHC.Generics.:*: GHC.Generics.M1 GHC.Generics.S s (GHC.Generics.K1 i a))
+ Dhall: instance forall k (s :: GHC.Generics.Meta) a (f :: k -> *) (g :: k -> *) i. (GHC.Generics.Selector s, Dhall.FromDhall a, Dhall.GenericFromDhall (f GHC.Generics.:*: g)) => Dhall.GenericFromDhall (GHC.Generics.M1 GHC.Generics.S s (GHC.Generics.K1 i a) GHC.Generics.:*: (f GHC.Generics.:*: g))
+ Dhall: instance forall k (s :: GHC.Generics.Meta) a (f :: k -> *) (g :: k -> *) i. (GHC.Generics.Selector s, Dhall.ToDhall a, Dhall.GenericToDhall (f GHC.Generics.:*: g)) => Dhall.GenericToDhall (GHC.Generics.M1 GHC.Generics.S s (GHC.Generics.K1 i a) GHC.Generics.:*: (f GHC.Generics.:*: g))
+ Dhall: map :: Ord k => Type k -> Type v -> Type (Map k v)
+ Dhall: pairFromMapEntry :: Type k -> Type v -> Type (k, v)
+ Dhall: setFromDistinctList :: (Ord a, Show a) => Type a -> Type (Set a)
+ Dhall: setIgnoringDuplicates :: Ord a => Type a -> Type (Set a)
+ Dhall: type Inject = ToDhall
+ Dhall: type Interpret = FromDhall
+ Dhall: void :: Type Void
+ Dhall.Binary: instance Dhall.Binary.FromTerm Dhall.Syntax.Import
+ Dhall.Binary: instance Dhall.Binary.FromTerm a => Dhall.Binary.FromTerm (Dhall.Syntax.Expr s a)
+ Dhall.Binary: instance Dhall.Binary.ToTerm Dhall.Syntax.DhallDouble
+ Dhall.Binary: instance Dhall.Binary.ToTerm Dhall.Syntax.Import
+ Dhall.Binary: instance Dhall.Binary.ToTerm a => Dhall.Binary.ToTerm (Dhall.Syntax.Expr Data.Void.Void a)
+ Dhall.Core: DhallDouble :: Double -> DhallDouble
+ Dhall.Core: RecordCompletion :: Expr s a -> Expr s a -> Expr s a
+ Dhall.Core: [getDhallDouble] :: DhallDouble -> Double
+ Dhall.Core: newtype DhallDouble
+ Dhall.Import: instance Dhall.Import.Canonicalize Dhall.Syntax.Directory
+ Dhall.Import: instance Dhall.Import.Canonicalize Dhall.Syntax.File
+ Dhall.Import: instance Dhall.Import.Canonicalize Dhall.Syntax.Import
+ Dhall.Import: instance Dhall.Import.Canonicalize Dhall.Syntax.ImportHashed
+ Dhall.Import: instance Dhall.Import.Canonicalize Dhall.Syntax.ImportType
+ Dhall.Main: Tags :: Input -> Output -> Maybe [Text] -> Bool -> Mode
+ Dhall.Main: [followSymlinks] :: Mode -> Bool
+ Dhall.Main: [input] :: Mode -> Input
+ Dhall.Main: [output] :: Mode -> Output
+ Dhall.Main: [suffixes] :: Mode -> Maybe [Text]
+ Dhall.Parser.Expression: localOnly :: Parser ImportType
+ Dhall.Parser.Token: _doubleColon :: Parser ()
+ Dhall.Tags: generate :: Input -> Maybe [Text] -> Bool -> IO Text
+ Dhall.Tags: instance GHC.Base.Monoid Dhall.Tags.Tags
+ Dhall.Tags: instance GHC.Base.Semigroup Dhall.Tags.Tags
+ Dhall.Tags: instance GHC.Classes.Eq Dhall.Tags.LineColumn
+ Dhall.Tags: instance GHC.Classes.Eq Dhall.Tags.LineOffset
+ Dhall.Tags: instance GHC.Classes.Ord Dhall.Tags.LineColumn
+ Dhall.Tags: instance GHC.Classes.Ord Dhall.Tags.LineOffset
+ Dhall.Tags: instance GHC.Show.Show Dhall.Tags.LineColumn
+ Dhall.Tags: instance GHC.Show.Show Dhall.Tags.LineOffset
+ Dhall.Tags: instance GHC.Show.Show Dhall.Tags.Tag
+ Dhall.TypeCheck: DisallowedHandlerType :: Text -> Expr s a -> Expr s a -> Text -> TypeMessage s a
+ Dhall.TypeCheck: ListLitInvariant :: TypeMessage s a
+ Dhall.Util: InputFile :: FilePath -> Input
+ Dhall.Util: OutputFile :: FilePath -> Output
+ Dhall.Util: StandardOutput :: Output
+ Dhall.Util: data Output
- Dhall: InputType :: (a -> Expr Src X) -> Expr Src X -> InputType a
+ Dhall: InputType :: (a -> Expr Src Void) -> Expr Src Void -> InputType a
- Dhall: InterpretOptions :: (Text -> Text) -> (Text -> Text) -> SingletonConstructors -> ReifiedNormalizer X -> InterpretOptions
+ Dhall: InterpretOptions :: (Text -> Text) -> (Text -> Text) -> SingletonConstructors -> ReifiedNormalizer Void -> InterpretOptions
- Dhall: RecordType :: Product (Const (Map Text (Expr Src X))) (Compose ((->) (Expr Src X)) (Extractor Src X)) a -> RecordType a
+ Dhall: RecordType :: Product (Const (Map Text (Expr Src Void))) (Compose ((->) (Expr Src Void)) (Extractor Src Void)) a -> RecordType a
- Dhall: Type :: (Expr Src X -> Extractor Src X a) -> Expr Src X -> Type a
+ Dhall: Type :: (Expr Src Void -> Extractor Src Void a) -> Expr Src Void -> Type a
- Dhall: UnionInputType :: Product (Const (Map Text (Expr Src X))) (Op (Text, Expr Src X)) a -> UnionInputType a
+ Dhall: UnionInputType :: Product (Const (Map Text (Expr Src Void))) (Op (Text, Expr Src Void)) a -> UnionInputType a
- Dhall: [declared] :: InputType a -> Expr Src X
+ Dhall: [declared] :: InputType a -> Expr Src Void
- Dhall: [embed] :: InputType a -> a -> Expr Src X
+ Dhall: [embed] :: InputType a -> a -> Expr Src Void
- Dhall: [expected] :: Type a -> Expr Src X
+ Dhall: [expected] :: Type a -> Expr Src Void
- Dhall: [extract] :: Type a -> Expr Src X -> Extractor Src X a
+ Dhall: [extract] :: Type a -> Expr Src Void -> Extractor Src Void a
- Dhall: [inputNormalizer] :: InterpretOptions -> ReifiedNormalizer X
+ Dhall: [inputNormalizer] :: InterpretOptions -> ReifiedNormalizer Void
- Dhall: auto :: Interpret a => Type a
+ Dhall: auto :: FromDhall a => Type a
- Dhall: autoWith :: (Interpret a, Generic a, GenericInterpret (Rep a)) => InterpretOptions -> Type a
+ Dhall: autoWith :: (FromDhall a, Generic a, GenericFromDhall (Rep a)) => InterpretOptions -> Type a
- Dhall: genericAuto :: (Generic a, GenericInterpret (Rep a)) => Type a
+ Dhall: genericAuto :: (Generic a, GenericFromDhall (Rep a)) => Type a
- Dhall: genericAutoWith :: GenericInterpret f => InterpretOptions -> State Int (Type (f a))
+ Dhall: genericAutoWith :: GenericFromDhall f => InterpretOptions -> State Int (Type (f a))
- Dhall: inject :: Inject a => InputType a
+ Dhall: inject :: ToDhall a => InputType a
- Dhall: injectWith :: (Inject a, Generic a, GenericInject (Rep a)) => InterpretOptions -> InputType a
+ Dhall: injectWith :: (ToDhall a, Generic a, GenericToDhall (Rep a)) => InterpretOptions -> InputType a
- Dhall: inputConstructor :: Inject a => Text -> UnionInputType a
+ Dhall: inputConstructor :: ToDhall a => Text -> UnionInputType a
- Dhall: inputExpr :: Text -> IO (Expr Src X)
+ Dhall: inputExpr :: Text -> IO (Expr Src Void)
- Dhall: inputExprWithSettings :: InputSettings -> Text -> IO (Expr Src X)
+ Dhall: inputExprWithSettings :: InputSettings -> Text -> IO (Expr Src Void)
- Dhall: inputField :: Inject a => Text -> RecordInputType a
+ Dhall: inputField :: ToDhall a => Text -> RecordInputType a
- Dhall: normalizer :: (Functor f, HasEvaluateSettings s) => LensLike' f s (Maybe (ReifiedNormalizer X))
+ Dhall: normalizer :: (Functor f, HasEvaluateSettings s) => LensLike' f s (Maybe (ReifiedNormalizer Void))
- Dhall: rawInput :: Alternative f => Type a -> Expr s X -> f a
+ Dhall: rawInput :: Alternative f => Type a -> Expr s Void -> f a
- Dhall: startingContext :: (Functor f, HasEvaluateSettings s) => LensLike' f s (Context (Expr Src X))
+ Dhall: startingContext :: (Functor f, HasEvaluateSettings s) => LensLike' f s (Context (Expr Src Void))
- Dhall.Core: DoubleLit :: Double -> Expr s a
+ Dhall.Core: DoubleLit :: DhallDouble -> Expr s a
- Dhall.Core: alphaNormalize :: () => Expr s a -> Expr s a
+ Dhall.Core: alphaNormalize :: Expr s a -> Expr s a
- Dhall.Import: Status :: NonEmpty Chained -> [Depends] -> Map Chained ImportSemantics -> (URL -> StateT Status IO Text) -> Maybe (ReifiedNormalizer X) -> Context (Expr Src X) -> SemanticCacheMode -> Status
+ Dhall.Import: Status :: NonEmpty Chained -> [Depends] -> Map Chained ImportSemantics -> (URL -> StateT Status IO Text) -> Maybe (ReifiedNormalizer Void) -> Context (Expr Src Void) -> SemanticCacheMode -> Status
- Dhall.Import: [_normalizer] :: Status -> Maybe (ReifiedNormalizer X)
+ Dhall.Import: [_normalizer] :: Status -> Maybe (ReifiedNormalizer Void)
- Dhall.Import: [_startingContext] :: Status -> Context (Expr Src X)
+ Dhall.Import: [_startingContext] :: Status -> Context (Expr Src Void)
- Dhall.Import: assertNoImports :: MonadIO io => Expr Src Import -> io (Expr Src X)
+ Dhall.Import: assertNoImports :: MonadIO io => Expr Src Import -> io (Expr Src Void)
- Dhall.Import: hashExpression :: Expr s X -> SHA256Digest
+ Dhall.Import: hashExpression :: Expr s Void -> SHA256Digest
- Dhall.Import: hashExpressionToCode :: Expr s X -> Text
+ Dhall.Import: hashExpressionToCode :: Expr s Void -> Text
- Dhall.Import: load :: Expr Src Import -> IO (Expr Src X)
+ Dhall.Import: load :: Expr Src Import -> IO (Expr Src Void)
- Dhall.Import: loadRelativeTo :: FilePath -> SemanticCacheMode -> Expr Src Import -> IO (Expr Src X)
+ Dhall.Import: loadRelativeTo :: FilePath -> SemanticCacheMode -> Expr Src Import -> IO (Expr Src Void)
- Dhall.Import: loadWith :: Expr Src Import -> StateT Status IO (Expr Src X)
+ Dhall.Import: loadWith :: Expr Src Import -> StateT Status IO (Expr Src Void)
- Dhall.Import: normalizer :: Functor f => LensLike' f Status (Maybe (ReifiedNormalizer X))
+ Dhall.Import: normalizer :: Functor f => LensLike' f Status (Maybe (ReifiedNormalizer Void))
- Dhall.Import: startingContext :: Functor f => LensLike' f Status (Context (Expr Src X))
+ Dhall.Import: startingContext :: Functor f => LensLike' f Status (Context (Expr Src Void))
- Dhall.Import: writeExpressionToSemanticCache :: Expr Src X -> IO ()
+ Dhall.Import: writeExpressionToSemanticCache :: Expr Src Void -> IO ()
- Dhall.Main: Default :: Input -> Bool -> Bool -> SemanticCacheMode -> Bool -> Mode
+ Dhall.Main: Default :: Input -> Output -> Bool -> Bool -> SemanticCacheMode -> Bool -> Mode
- Dhall.Main: Hash :: Mode
+ Dhall.Main: Hash :: Input -> Mode
- Dhall.Main: Resolve :: Input -> Maybe ResolveMode -> Mode
+ Dhall.Main: Resolve :: Input -> Maybe ResolveMode -> SemanticCacheMode -> Mode
- Dhall.Main: Type :: Input -> Bool -> Mode
+ Dhall.Main: Type :: Input -> Bool -> SemanticCacheMode -> Mode
Files
- CHANGELOG.md +57/−1
- dhall-lang/Prelude/Bool/not +3/−1
- dhall-lang/Prelude/Bool/show +3/−1
- dhall-lang/Prelude/Double/show +3/−1
- dhall-lang/Prelude/Integer/show +3/−1
- dhall-lang/Prelude/Integer/toDouble +3/−1
- dhall-lang/Prelude/JSON/Nesting +5/−1
- dhall-lang/Prelude/JSON/Tagged +1/−2
- dhall-lang/Prelude/JSON/Type +6/−12
- dhall-lang/Prelude/JSON/array +6/−12
- dhall-lang/Prelude/JSON/bool +6/−12
- dhall-lang/Prelude/JSON/keyText +2/−14
- dhall-lang/Prelude/JSON/keyValue +2/−17
- dhall-lang/Prelude/JSON/null +6/−12
- dhall-lang/Prelude/JSON/number +6/−12
- dhall-lang/Prelude/JSON/object +6/−12
- dhall-lang/Prelude/JSON/render +4/−8
- dhall-lang/Prelude/JSON/string +6/−12
- dhall-lang/Prelude/List/default +15/−0
- dhall-lang/Prelude/List/empty +10/−0
- dhall-lang/Prelude/List/head +3/−1
- dhall-lang/Prelude/List/last +3/−1
- dhall-lang/Prelude/List/length +3/−1
- dhall-lang/Prelude/List/package.dhall +6/−0
- dhall-lang/Prelude/List/reverse +3/−1
- dhall-lang/Prelude/List/shifted +3/−6
- dhall-lang/Prelude/Location/Type +3/−6
- dhall-lang/Prelude/Map/empty +15/−0
- dhall-lang/Prelude/Map/keyText +15/−0
- dhall-lang/Prelude/Map/keyValue +17/−0
- dhall-lang/Prelude/Map/package.dhall +9/−0
- dhall-lang/Prelude/Monoid +5/−1
- dhall-lang/Prelude/Natural/even +3/−1
- dhall-lang/Prelude/Natural/isZero +3/−1
- dhall-lang/Prelude/Natural/odd +3/−1
- dhall-lang/Prelude/Natural/show +3/−1
- dhall-lang/Prelude/Natural/subtract +3/−1
- dhall-lang/Prelude/Natural/toInteger +3/−1
- dhall-lang/Prelude/Optional/default +15/−0
- dhall-lang/Prelude/Optional/package.dhall +3/−0
- dhall-lang/Prelude/Text/concatMapSep +1/−2
- dhall-lang/Prelude/Text/concatSep +1/−2
- dhall-lang/Prelude/Text/show +3/−1
- dhall-lang/Prelude/XML/Type +4/−8
- dhall-lang/Prelude/XML/element +10/−20
- dhall-lang/Prelude/XML/leaf +2/−4
- dhall-lang/Prelude/XML/render +4/−8
- dhall-lang/Prelude/XML/text +4/−8
- dhall-lang/Prelude/package.dhall +3/−3
- dhall-lang/tests/binary-decode/failure/unit/OperatorUnknownOpcode.dhallb binary
- dhall-lang/tests/binary-decode/success/unit/CompletionA.dhallb binary
- dhall-lang/tests/binary-decode/success/unit/CompletionB.dhall +1/−0
- dhall-lang/tests/import/data/importBoundary.dhall +3/−0
- dhall-lang/tests/import/failure/customHeadersUsingBoundVariable.dhall +15/−0
- dhall-lang/tests/import/failure/importBoundary.dhall +1/−0
- dhall-lang/tests/normalization/success/simple/completionA.dhall +10/−0
- dhall-lang/tests/normalization/success/simple/completionB.dhall +5/−0
- dhall-lang/tests/normalization/success/unit/CompletionA.dhall +1/−0
- dhall-lang/tests/normalization/success/unit/CompletionB.dhall +1/−0
- dhall-lang/tests/normalization/success/unit/RecursiveRecordTypeMergeDeepA.dhall +1/−0
- dhall-lang/tests/normalization/success/unit/RecursiveRecordTypeMergeDeepB.dhall +1/−0
- dhall-lang/tests/parser/failure/nonBase16Hash.dhall +1/−0
- dhall-lang/tests/parser/success/leadingSeparatorsA.dhall +10/−0
- dhall-lang/tests/parser/success/leadingSeparatorsB.dhallb +1/−0
- dhall-lang/tests/parser/success/lineCommentCRLFA.dhall +3/−0
- dhall-lang/tests/parser/success/lineCommentCRLFB.dhallb +1/−0
- dhall-lang/tests/parser/success/text/dollarSignA.dhall +1/−0
- dhall-lang/tests/parser/success/text/dollarSignB.dhallb +1/−0
- dhall-lang/tests/parser/success/unit/CompletionA.dhall +1/−0
- dhall-lang/tests/parser/success/unit/CompletionB.dhallb binary
- dhall-lang/tests/parser/success/unit/DoubleLitNegZeroA.dhall +1/−0
- dhall-lang/tests/parser/success/unit/DoubleLitNegZeroB.dhallb binary
- dhall-lang/tests/parser/success/unit/import/environmentVariablePosixA.dhall +1/−1
- dhall-lang/tests/parser/success/unit/import/environmentVariablePosixB.dhallb binary
- dhall-lang/tests/parser/success/unit/import/urls/ipv4upperoctetsA.dhall +1/−0
- dhall-lang/tests/parser/success/unit/import/urls/ipv4upperoctetsB.dhallb binary
- dhall-lang/tests/parser/success/unit/import/urls/ipv6verylongA.dhall +1/−0
- dhall-lang/tests/parser/success/unit/import/urls/ipv6verylongB.dhallb binary
- dhall-lang/tests/type-inference/data/importBoundary.dhall +0/−3
- dhall-lang/tests/type-inference/failure/customHeadersUsingBoundVariable.dhall +0/−15
- dhall-lang/tests/type-inference/failure/importBoundary.dhall +0/−1
- dhall-lang/tests/type-inference/success/preludeB.dhall +148/−274
- dhall-lang/tests/type-inference/success/simple/completionA.dhall +25/−0
- dhall-lang/tests/type-inference/success/simple/completionB.dhall +8/−0
- dhall-lang/tests/type-inference/success/simple/orderInsensitivityA.dhall +15/−0
- dhall-lang/tests/type-inference/success/simple/orderInsensitivityB.dhall +1/−0
- dhall-lang/tests/type-inference/success/simple/toMapEmptyNormalizeAnnotationA.dhall +3/−0
- dhall-lang/tests/type-inference/success/simple/toMapEmptyNormalizeAnnotationB.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/CompletionA.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/CompletionB.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/FunctionApplicationNormalizeArgumentA.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/FunctionApplicationNormalizeArgumentB.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/FunctionApplicationNormalizeResultA.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/FunctionApplicationNormalizeResultB.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/FunctionNormalizeTypeAnnotationA.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/FunctionNormalizeTypeAnnotationB.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotation0A.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotation0B.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotation1A.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotation1B.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotationA.dhall +0/−1
- dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotationB.dhall +0/−1
- dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeDeepA.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeDeepB.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/ToMapInferTypeFromRecordA.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/ToMapInferTypeFromRecordB.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/TypeAnnotationFunctionA.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/TypeAnnotationFunctionB.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/TypeAnnotationNormalizeA.dhall +1/−0
- dhall-lang/tests/type-inference/success/unit/TypeAnnotationNormalizeB.dhall +1/−0
- dhall.cabal +16/−3
- doctest/Main.hs +2/−1
- ghc-src/Dhall/Crypto.hs +9/−0
- ghcjs-src/Dhall/Crypto.hs +9/−0
- src/Dhall.hs +597/−213
- src/Dhall/Binary.hs +34/−16
- src/Dhall/Binary.hs-boot +0/−10
- src/Dhall/Core.hs +1383/−2389
- src/Dhall/Core.hs-boot +0/−11
- src/Dhall/Diff.hs +20/−3
- src/Dhall/Eval.hs +53/−44
- src/Dhall/Eval.hs-boot +0/−8
- src/Dhall/Format.hs +4/−4
- src/Dhall/Freeze.hs +5/−5
- src/Dhall/Hash.hs +0/−37
- src/Dhall/Import.hs +18/−17
- src/Dhall/Import/Types.hs +23/−9
- src/Dhall/Lint.hs +17/−2
- src/Dhall/Main.hs +130/−28
- src/Dhall/Parser/Combinators.hs +1/−0
- src/Dhall/Parser/Expression.hs +99/−20
- src/Dhall/Parser/Token.hs +302/−2
- src/Dhall/Pretty/Internal.hs +126/−82
- src/Dhall/Pretty/Internal.hs-boot +1/−6
- src/Dhall/Repl.hs +10/−12
- src/Dhall/Set.hs +15/−4
- src/Dhall/Syntax.hs +1182/−0
- src/Dhall/Syntax.hs-boot +7/−0
- src/Dhall/Tags.hs +279/−0
- src/Dhall/Tutorial.hs +2172/−2983
- src/Dhall/TypeCheck.hs +182/−98
- src/Dhall/Util.hs +9/−5
- src/Dhall/Version.hs +5/−0
- tests/Dhall/Test/Dhall.hs +42/−44
- tests/Dhall/Test/Format.hs +7/−2
- tests/Dhall/Test/Lint.hs +3/−3
- tests/Dhall/Test/Main.hs +4/−0
- tests/Dhall/Test/Normalization.hs +7/−7
- tests/Dhall/Test/Parser.hs +12/−11
- tests/Dhall/Test/QuickCheck.hs +93/−7
- tests/Dhall/Test/Regression.hs +13/−4
- tests/Dhall/Test/Tags.hs +63/−0
- tests/Dhall/Test/Tutorial.hs +2/−2
- tests/Dhall/Test/TypeInference.hs +9/−21
- tests/Dhall/Test/Util.hs +16/−16
- tests/format/applicationMultilineA.dhall +30/−0
- tests/format/applicationMultilineB.dhall +30/−0
- tests/format/asciiB.dhall +0/−0
- tests/format/concatSepA.dhall +30/−0
- tests/format/concatSepB.dhall +33/−0
- tests/format/doubleRoundB.dhall +0/−0
- tests/format/emptyRecordB.dhall +0/−0
- tests/format/escapeNumericLabelB.dhall +0/−0
- tests/format/escapeSingleQuotedOpenInterpolationB.dhall +0/−0
- tests/format/fieldOrderB.dhall +0/−0
- tests/format/functionLikeA.dhall +6/−0
- tests/format/functionLikeB.dhall +6/−0
- tests/format/headerB.dhall +0/−0
- tests/format/ifThenElseB.dhall +0/−0
- tests/format/importAccessB.dhall +0/−0
- tests/format/importLines2A.dhall +0/−0
- tests/format/importLines2B.dhall +0/−0
- tests/format/importLinesB.dhall +0/−0
- tests/format/importSuffixB.dhall +0/−0
- tests/format/innerMultilineB.dhall +0/−0
- tests/format/issue1301B.dhall +0/−0
- tests/format/issue1413A.dhall +6/−0
- tests/format/issue1413B.dhall +6/−0
- tests/format/largeExponentB.dhall +0/−0
- tests/format/largeRecordB.dhall +9/−9
- tests/format/letB.dhall +0/−0
- tests/format/letCommentsB.dhall +0/−0
- tests/format/letLineCommentsAfterVariableA.dhall +10/−0
- tests/format/letLineCommentsAfterVariableB.dhall +12/−0
- tests/format/letLongCommentsB.dhall +0/−0
- tests/format/letNewlineCommentsB.dhall +0/−0
- tests/format/letShortCommentsB.dhall +7/−1
- tests/format/multilineB.dhall +0/−0
- tests/format/parentB.dhall +0/−0
- tests/format/projectionOrderB.dhall +0/−0
- tests/format/recordCompletionA.dhall +25/−0
- tests/format/recordCompletionB.dhall +25/−0
- tests/format/sha256PrintingB.dhall +0/−0
- tests/format/unicodeB.dhall +0/−0
- tests/format/urlsB.dhall +0/−0
- tests/lint/success/fixAssertA.dhall +1/−0
- tests/lint/success/fixAssertB.dhall +1/−0
- tests/tags/all.tags +61/−0
- tests/tags/let.dhall +4/−0
- tests/tags/let.tags +6/−0
- tests/tags/nonunicode.dhall +3/−0
- tests/tags/nonunicode.tags +5/−0
- tests/tags/record.dhall +35/−0
- tests/tags/record.tags +13/−0
- tests/tags/recordlit.dhall +14/−0
- tests/tags/recordlit.tags +10/−0
- tests/tags/simple.dhall +39/−0
- tests/tags/simple.tags +11/−0
- tests/tags/unicode.dhall +3/−0
- tests/tags/unicode.tags +5/−0
- tests/tags/union.dhall +4/−0
- tests/tags/union.tags +11/−0
CHANGELOG.md view
@@ -1,3 +1,55 @@+1.27.0++* [Supports version 11.0.0 of the standard](https://github.com/dhall-lang/dhall-lang/releases/tag/v11.0.0)+* BREAKING CHANGE: Rename `Inject`/`Interpret` to `ToDhall`/`FromDhall`+ * This change `ConstraintKinds` to minimize disruption by keeping around+ `Inject`/`Interpret` as synonyms for `ToDhall`/`FromDhall`+ * In other words, constraints and derived instances using `Inject` or+ `Interpret` will still work+ * However, manual instances using `Inject` or `Interpret` won't work+ unless you rename them to `ToDhall`/`FromDhall` or enable the+ `TypeSynonymInstances` extension+* BREAKING CHANGE: Fix `Eq` instance for `Expr`s with special `Double`s+ * This fixes the `Eq` instance for `Expr`s to match the standard regarding+ `Double` comparisons+ * Specifically: `NaN == NaN` and `-0.0 /= 0.0`+ * This is a breaking change because the `DoubleLit` constructor of `Expr`+ now stores a `DhallDouble` instead of a `Double`+* BREAKING CHANGE: [Add `--file` option for `dhall hash`](https://github.com/dhall-lang/dhall-haskell/pull/1445)+ * This is a breaking change because it also removes the `Dhall.Hash` module,+ which wasn't really carrying its own weight+* [Add support for leading separators](https://github.com/dhall-lang/dhall-haskell/pull/1355)+ * See the [changelog for standard version 11.0.0](https://github.com/dhall-lang/dhall-lang/releases/tag/v11.0.0) for more details+* [Add record completion operator](https://github.com/dhall-lang/dhall-haskell/pull/1375)+ * See the [changelog for standard version 11.0.0](https://github.com/dhall-lang/dhall-lang/releases/tag/v11.0.0) for more details+* [Add `dhall tags` subcommand](https://github.com/dhall-lang/dhall-haskell/pull/1398)+ * This generates an ETags file from an input file or directory+* [Add `dhall lint` support for fixing malformed assertions](https://github.com/dhall-lang/dhall-haskell/pull/1353)+ * `dhall lint` now fixes `let example = foo === bar` to be+ `let example = assert : foo === bar`+* Normalize inferred types [#1337](https://github.com/dhall-lang/dhall-haskell/pull/1337)+* New `FromDhall`/`ToDhall` (previously `Interpret`/`Inject`) instances for:+ * [`Void`](https://github.com/dhall-lang/dhall-haskell/pull/1382)+ * `Map` [#1391](https://github.com/dhall-lang/dhall-haskell/pull/1391) / [#1412](https://github.com/dhall-lang/dhall-haskell/pull/1412)+ * [`HashMap`](https://github.com/dhall-lang/dhall-haskell/pull/1426)+ * [`Set` and `HashSet`](https://github.com/dhall-lang/dhall-haskell/pull/1405)+ * [`()`](https://github.com/dhall-lang/dhall-haskell/pull/1388)+* [Add `--output` option to `dhall`](https://github.com/dhall-lang/dhall-haskell/pull/1399)+* [Move syntax things from `Dhall.Core` to a new `Dhall.Syntax` module](https://github.com/dhall-lang/dhall-haskell/pull/1440)+ * This is not a breaking change. `Dhall.Core` still re-exports the same+ API as before+* Performance improvements+ * [#1362](https://github.com/dhall-lang/dhall-haskell/pull/1362)+ * [#1366](https://github.com/dhall-lang/dhall-haskell/pull/1366)+ * [#1368](https://github.com/dhall-lang/dhall-haskell/pull/1368)+ * [#1397](https://github.com/dhall-lang/dhall-haskell/pull/1397)+ * [#1401](https://github.com/dhall-lang/dhall-haskell/pull/1401)+* Fixes and improvements to code formatting+ * [#1360](https://github.com/dhall-lang/dhall-haskell/pull/1360)+ * [#1372](https://github.com/dhall-lang/dhall-haskell/pull/1372)+ * [#1380](https://github.com/dhall-lang/dhall-haskell/pull/1380)+ * [#1415](https://github.com/dhall-lang/dhall-haskell/pull/1415)+ 1.26.1 * TECHNICALLY BREAKING CHANGES: [Simplify `⫽` within projection](https://github.com/dhall-lang/dhall-haskell/pull/1283) / [Simplify nested record projections](https://github.com/dhall-lang/dhall-haskell/pull/1307)@@ -40,6 +92,10 @@ * This fixes `dhall type` to more accurately follow the standard * [Initial changes for GHC 8.8 support](https://github.com/dhall-lang/dhall-haskell/pull/1324) * [Fix Haddock formatting for `dhallFromJSON`](https://github.com/dhall-lang/dhall-haskell/pull/1316)+* [Improved Windows caching support](https://github.com/dhall-lang/dhall-haskell/pull/1272)+ * `dhall` will now prefer the `%LOCALAPPDATA%` directory for caching if it is+ available+* [Warn about missing cache directories](https://github.com/dhall-lang/dhall-haskell/pull/1320) 1.26.0 @@ -104,7 +160,7 @@ newly-added utilities * See also: [#1298](https://github.com/dhall-lang/dhall-haskell/pull/1298) * [New `:help` command for `dhall repl`](https://github.com/dhall-lang/dhall-haskell/pull/1237)-* [New `--no-cache` flag](https://github.com/dhall-lang/dhall-haskell/pull/1290)+* New `--no-cache` flag [#1290](https://github.com/dhall-lang/dhall-haskell/pull/1290) / [#1434](https://github.com/dhall-lang/dhall-haskell/pull/1434) / [#1436](https://github.com/dhall-lang/dhall-haskell/pull/1436) * You can now disable use of the cache with this flag * This comes in handy if you want to disable α-normalization for imports protected by a semantic integrity check
dhall-lang/Prelude/Bool/not view
@@ -1,7 +1,9 @@ {- Flip the value of a `Bool` -}-let not : Bool → Bool = λ(b : Bool) → b == False+let not+ : Bool → Bool+ = λ(b : Bool) → b == False let example0 = assert : not True ≡ False
dhall-lang/Prelude/Bool/show view
@@ -2,7 +2,9 @@ Render a `Bool` as `Text` using the same representation as Dhall source code (i.e. beginning with a capital letter) -}-let show : Bool → Text = λ(b : Bool) → if b then "True" else "False"+let show+ : Bool → Text+ = λ(b : Bool) → if b then "True" else "False" let example0 = assert : show True ≡ "True"
dhall-lang/Prelude/Double/show view
@@ -2,7 +2,9 @@ Render a `Double` as `Text` using the same representation as Dhall source code (i.e. a decimal floating point number with a leading `-` sign if negative) -}-let show : Double → Text = Double/show+let show+ : Double → Text+ = Double/show let example0 = assert : show -3.1 ≡ "-3.1"
dhall-lang/Prelude/Integer/show view
@@ -3,7 +3,9 @@ code (i.e. a decimal number with a leading `-` sign if negative and a leading `+` sign if non-negative) -}-let show : Integer → Text = Integer/show+let show+ : Integer → Text+ = Integer/show let example0 = assert : show -3 ≡ "-3"
dhall-lang/Prelude/Integer/toDouble view
@@ -1,7 +1,9 @@ {- Convert an `Integer` to the corresponding `Double` -}-let toDouble : Integer → Double = Integer/toDouble+let toDouble+ : Integer → Double+ = Integer/toDouble let example0 = assert : toDouble -3 ≡ -3.0
dhall-lang/Prelude/JSON/Nesting view
@@ -28,4 +28,8 @@ ``` -}-let Nesting : Type = < Inline | Nested : Text > in Nesting+let Nesting+ : Type+ = < Inline | Nested : Text >++in Nesting
dhall-lang/Prelude/JSON/Tagged view
@@ -70,8 +70,7 @@ , nesting : ./Nesting sha256:6284802edd41d5d725aa1ec7687e614e21ad1be7e14dd10996bfa9625105c335 ? ./Nesting- , contents :- a+ , contents : a } in Tagged
dhall-lang/Prelude/JSON/Type view
@@ -57,18 +57,12 @@ : Type = ∀(JSON : Type) → ∀ ( json- : { string :- Text → JSON- , number :- Double → JSON- , object :- List { mapKey : Text, mapValue : JSON } → JSON- , array :- List JSON → JSON- , bool :- Bool → JSON- , null :- JSON+ : { string : Text → JSON+ , number : Double → JSON+ , object : List { mapKey : Text, mapValue : JSON } → JSON+ , array : List JSON → JSON+ , bool : Bool → JSON+ , null : JSON } ) → JSON
dhall-lang/Prelude/JSON/array view
@@ -24,18 +24,12 @@ = λ(x : List JSON) → λ(JSON : Type) → λ ( json- : { string :- Text → JSON- , number :- Double → JSON- , object :- List { mapKey : Text, mapValue : JSON } → JSON- , array :- List JSON → JSON- , bool :- Bool → JSON- , null :- JSON+ : { string : Text → JSON+ , number : Double → JSON+ , object : List { mapKey : Text, mapValue : JSON } → JSON+ , array : List JSON → JSON+ , bool : Bool → JSON+ , null : JSON } ) → json.array (List/map JSON@1 JSON (λ(j : JSON@1) → j JSON json) x)
dhall-lang/Prelude/JSON/bool view
@@ -19,18 +19,12 @@ = λ(x : Bool) → λ(JSON : Type) → λ ( json- : { string :- Text → JSON- , number :- Double → JSON- , object :- List { mapKey : Text, mapValue : JSON } → JSON- , array :- List JSON → JSON- , bool :- Bool → JSON- , null :- JSON+ : { string : Text → JSON+ , number : Double → JSON+ , object : List { mapKey : Text, mapValue : JSON } → JSON+ , array : List JSON → JSON+ , bool : Bool → JSON+ , null : JSON } ) → json.bool x
dhall-lang/Prelude/JSON/keyText view
@@ -1,14 +1,2 @@-{--Builds a key-value record such that a List of them will be converted to a-homogeneous record by dhall-to-json and dhall-to-yaml.--Both key and value are fixed to Text.-Take a look at `Record/keyValue` for a polymorphic version.--}-let keyText =- λ(key : Text) → λ(value : Text) → { mapKey = key, mapValue = value }--let example0 =- assert : keyText "foo" "bar" ≡ { mapKey = "foo", mapValue = "bar" }--in keyText+ ../Map/keyText sha256:f7b6c802ca5764d03d5e9a6e48d9cb167c01392f775d9c2c87b83cdaa60ea0cc+? ../Map/keyText
dhall-lang/Prelude/JSON/keyValue view
@@ -1,17 +1,2 @@-{--Builds a key-value record such that a List of them will be converted to a-homogeneous record by dhall-to-json and dhall-to-yaml.--}-let keyValue =- λ(v : Type)- → λ(key : Text)- → λ(value : v)- → { mapKey = key, mapValue = value }--let example0 =- assert : keyValue Natural "foo" 2 ≡ { mapKey = "foo", mapValue = 2 }--let example1 =- assert : keyValue Text "bar" "baz" ≡ { mapKey = "bar", mapValue = "baz" }--in keyValue+ ../Map/keyValue sha256:a0a97199d280c4cce72ffcbbf93b7ceda0a569cf4d173ac98e0aaaa78034b98c+? ../Map/keyValue
dhall-lang/Prelude/JSON/null view
@@ -14,18 +14,12 @@ : JSON = λ(JSON : Type) → λ ( json- : { string :- Text → JSON- , number :- Double → JSON- , object :- List { mapKey : Text, mapValue : JSON } → JSON- , array :- List JSON → JSON- , bool :- Bool → JSON- , null :- JSON+ : { string : Text → JSON+ , number : Double → JSON+ , object : List { mapKey : Text, mapValue : JSON } → JSON+ , array : List JSON → JSON+ , bool : Bool → JSON+ , null : JSON } ) → json.null
dhall-lang/Prelude/JSON/number view
@@ -19,18 +19,12 @@ = λ(x : Double) → λ(JSON : Type) → λ ( json- : { string :- Text → JSON- , number :- Double → JSON- , object :- List { mapKey : Text, mapValue : JSON } → JSON- , array :- List JSON → JSON- , bool :- Bool → JSON- , null :- JSON+ : { string : Text → JSON+ , number : Double → JSON+ , object : List { mapKey : Text, mapValue : JSON } → JSON+ , array : List JSON → JSON+ , bool : Bool → JSON+ , null : JSON } ) → json.number x
dhall-lang/Prelude/JSON/object view
@@ -30,18 +30,12 @@ = λ(x : List { mapKey : Text, mapValue : JSON }) → λ(JSON : Type) → λ ( json- : { string :- Text → JSON- , number :- Double → JSON- , object :- List { mapKey : Text, mapValue : JSON } → JSON- , array :- List JSON → JSON- , bool :- Bool → JSON- , null :- JSON+ : { string : Text → JSON+ , number : Double → JSON+ , object : List { mapKey : Text, mapValue : JSON } → JSON+ , array : List JSON → JSON+ , bool : Bool → JSON+ , null : JSON } ) → json.object
dhall-lang/Prelude/JSON/render view
@@ -32,10 +32,8 @@ = λ(j : JSON) → j Text- { string =- λ(x : Text) → Text/show x- , number =- λ(x : Double) → Double/show x+ { string = λ(x : Text) → Text/show x+ , number = λ(x : Double) → Double/show x , object = λ(x : List { mapKey : Text, mapValue : Text }) → let body =@@ -53,10 +51,8 @@ → let body = Text/concatMapSep "," Text (λ(y : Text) → " ${y}") x in "[${body} ]"- , bool =- λ(x : Bool) → if x then "true" else "false"- , null =- "null"+ , bool = λ(x : Bool) → if x then "true" else "false"+ , null = "null" } in render
dhall-lang/Prelude/JSON/string view
@@ -19,18 +19,12 @@ = λ(x : Text) → λ(JSON : Type) → λ ( json- : { string :- Text → JSON- , number :- Double → JSON- , object :- List { mapKey : Text, mapValue : JSON } → JSON- , array :- List JSON → JSON- , bool :- Bool → JSON- , null :- JSON+ : { string : Text → JSON+ , number : Double → JSON+ , object : List { mapKey : Text, mapValue : JSON } → JSON+ , array : List JSON → JSON+ , bool : Bool → JSON+ , null : JSON } ) → json.string x
+ dhall-lang/Prelude/List/default view
@@ -0,0 +1,15 @@+{-+Unpack an `Optional` containing a `List`, defaulting to an empty list when the+`Optional` is `None`+-}+let default+ : ∀(a : Type) → Optional (List a) → List a+ = λ(a : Type)+ → λ(o : Optional (List a))+ → Optional/fold (List a) o (List a) (λ(l : List a) → l) ([] : List a)++let example0 = assert : default Bool (None (List Bool)) ≡ ([] : List Bool)++let example1 = assert : default Bool (Some [ True ]) ≡ [ True ]++in default
+ dhall-lang/Prelude/List/empty view
@@ -0,0 +1,10 @@+{-+An empty list of the given type+-}+let empty+ : ∀(a : Type) → List a+ = λ(a : Type) → [] : List a++let example0 = assert : empty Bool ≡ ([] : List Bool)++in empty
dhall-lang/Prelude/List/head view
@@ -1,7 +1,9 @@ {- Retrieve the first element of the list -}-let head : ∀(a : Type) → List a → Optional a = List/head+let head+ : ∀(a : Type) → List a → Optional a+ = List/head let example0 = assert : head Natural [ 0, 1, 2 ] ≡ Some 0
dhall-lang/Prelude/List/last view
@@ -1,7 +1,9 @@ {- Retrieve the last element of the list -}-let last : ∀(a : Type) → List a → Optional a = List/last+let last+ : ∀(a : Type) → List a → Optional a+ = List/last let example0 = assert : last Natural [ 0, 1, 2 ] ≡ Some 2
dhall-lang/Prelude/List/length view
@@ -1,7 +1,9 @@ {- Returns the number of elements in a list -}-let length : ∀(a : Type) → List a → Natural = List/length+let length+ : ∀(a : Type) → List a → Natural+ = List/length let example0 = assert : length Natural [ 0, 1, 2 ] ≡ 3
dhall-lang/Prelude/List/package.dhall view
@@ -13,6 +13,12 @@ , concatMap = ./concatMap sha256:3b2167061d11fda1e4f6de0522cbe83e0d5ac4ef5ddf6bb0b2064470c5d3fb64 ? ./concatMap+, default =+ ./default sha256:0ed2a04df6c1d55c08bcdbad82b30980af9ac40d2df7a1628f3663e3e8b4fe01+ ? ./default+, empty =+ ./empty sha256:b2f561f35098c457353723c93a22bd5de28d26ecc5370814bef9dfda421e0147+ ? ./empty , filter = ./filter sha256:8ebfede5bbfe09675f246c33eb83964880ac615c4b1be8d856076fdbc4b26ba6 ? ./filter
dhall-lang/Prelude/List/reverse view
@@ -1,7 +1,9 @@ {- Reverse a list -}-let reverse : ∀(a : Type) → List a → List a = List/reverse+let reverse+ : ∀(a : Type) → List a → List a+ = List/reverse let example0 = assert : reverse Natural [ 0, 1, 2 ] ≡ [ 2, 1, 0 ]
dhall-lang/Prelude/List/shifted view
@@ -23,8 +23,7 @@ → let length = List/length { index : Natural, value : a } kvs - in { count =- y.count + length+ in { count = y.count + length , diff = λ(n : Natural) → List/fold@@ -36,10 +35,8 @@ ) → λ(z : list) → let kvNew =- { index =- kvOld.index + n- , value =- kvOld.value+ { index = kvOld.index + n+ , value = kvOld.value } in cons kvNew z
dhall-lang/Prelude/Location/Type view
@@ -9,13 +9,10 @@ : ( missing sha256:f428188ff9d77ea15bc2bcd0da3f8ed81b304e175b07ade42a3b0fb02941b2aa as Location ? missing as Location )- ≡ < Environment :- Text- | Local :- Text+ ≡ < Environment : Text+ | Local : Text | Missing- | Remote :- Text+ | Remote : Text >.Missing in Location
+ dhall-lang/Prelude/Map/empty view
@@ -0,0 +1,15 @@+{-+An empty `Map` of the given key and value types+-}+let Map =+ ./Type sha256:210c7a9eba71efbb0f7a66b3dcf8b9d3976ffc2bc0e907aadfb6aa29c333e8ed+ ? ./Type++let empty+ : ∀(k : Type) → ∀(v : Type) → Map k v+ = λ(k : Type) → λ(v : Type) → [] : Map k v++let example0 =+ assert : empty Text Bool ≡ ([] : List { mapKey : Text, mapValue : Bool })++in empty
+ dhall-lang/Prelude/Map/keyText view
@@ -0,0 +1,15 @@+{-+Builds a key-value record such that a `List` of them will be converted to a+homogeneous record by dhall-to-json and dhall-to-yaml.++Both key and value are fixed to `Text`.++Take a look at `./keyValue` for a polymorphic version.+-}+let keyText =+ λ(key : Text) → λ(value : Text) → { mapKey = key, mapValue = value }++let example0 =+ assert : keyText "foo" "bar" ≡ { mapKey = "foo", mapValue = "bar" }++in keyText
+ dhall-lang/Prelude/Map/keyValue view
@@ -0,0 +1,17 @@+{-+Builds a key-value record such that a List of them will be converted to a+homogeneous record by dhall-to-json and dhall-to-yaml.+-}+let keyValue =+ λ(v : Type)+ → λ(key : Text)+ → λ(value : v)+ → { mapKey = key, mapValue = value }++let example0 =+ assert : keyValue Natural "foo" 2 ≡ { mapKey = "foo", mapValue = 2 }++let example1 =+ assert : keyValue Text "bar" "baz" ≡ { mapKey = "bar", mapValue = "baz" }++in keyValue
dhall-lang/Prelude/Map/package.dhall view
@@ -4,6 +4,15 @@ , Entry = ./Entry sha256:f334283bdd9cd88e6ea510ca914bc221fc2dab5fb424d24514b2e0df600d5346 ? ./Entry+, empty =+ ./empty sha256:4c612558b8bbe8f955550ed3fb295d57b1b864c85cd52615b52d0ee0e9682e52+ ? ./empty+, keyText =+ ./keyText sha256:f7b6c802ca5764d03d5e9a6e48d9cb167c01392f775d9c2c87b83cdaa60ea0cc+ ? ./keyText+, keyValue =+ ./keyValue sha256:a0a97199d280c4cce72ffcbbf93b7ceda0a569cf4d173ac98e0aaaa78034b98c+ ? ./keyValue , keys = ./keys sha256:d13ec34e6acf7c349d82272ef09a37c7bdf37f0dab489e9df47a1ff215d9f5e7 ? ./keys
dhall-lang/Prelude/Monoid view
@@ -36,4 +36,8 @@ : ./Monoid Text ``` -}-let Monoid : ∀(m : Type) → Type = λ(m : Type) → List m → m in Monoid+let Monoid+ : ∀(m : Type) → Type+ = λ(m : Type) → List m → m++in Monoid
dhall-lang/Prelude/Natural/even view
@@ -1,7 +1,9 @@ {- Returns `True` if a number if even and returns `False` otherwise -}-let even : Natural → Bool = Natural/even+let even+ : Natural → Bool+ = Natural/even let example0 = assert : even 3 ≡ False
dhall-lang/Prelude/Natural/isZero view
@@ -1,7 +1,9 @@ {- Returns `True` if a number is `0` and returns `False` otherwise -}-let isZero : Natural → Bool = Natural/isZero+let isZero+ : Natural → Bool+ = Natural/isZero let example0 = assert : isZero 2 ≡ False
dhall-lang/Prelude/Natural/odd view
@@ -1,7 +1,9 @@ {- Returns `True` if a number is odd and returns `False` otherwise -}-let odd : Natural → Bool = Natural/odd+let odd+ : Natural → Bool+ = Natural/odd let example0 = assert : odd 3 ≡ True
dhall-lang/Prelude/Natural/show view
@@ -2,7 +2,9 @@ Render a `Natural` number as `Text` using the same representation as Dhall source code (i.e. a decimal number) -}-let show : Natural → Text = Natural/show+let show+ : Natural → Text+ = Natural/show let example0 = assert : show 3 ≡ "3"
dhall-lang/Prelude/Natural/subtract view
@@ -1,7 +1,9 @@ {- `subtract m n` computes `n - m`, truncating to `0` if `m > n` -}-let subtract : Natural → Natural → Natural = Natural/subtract+let subtract+ : Natural → Natural → Natural+ = Natural/subtract let example0 = assert : subtract 1 2 ≡ 1
dhall-lang/Prelude/Natural/toInteger view
@@ -1,7 +1,9 @@ {- Convert a `Natural` number to the corresponding `Integer` -}-let toInteger : Natural → Integer = Natural/toInteger+let toInteger+ : Natural → Integer+ = Natural/toInteger let example0 = assert : toInteger 3 ≡ +3
+ dhall-lang/Prelude/Optional/default view
@@ -0,0 +1,15 @@+{-+Unpack an `Optional`, returning the default when it's `None`.+-}+let default+ : ∀(a : Type) → a → Optional a → a+ = λ(a : Type)+ → λ(default : a)+ → λ(o : Optional a)+ → Optional/fold a o a (λ(x : a) → x) default++let example0 = assert : default Bool False (None Bool) ≡ False++let example1 = assert : default Bool False (Some True) ≡ True++in default
dhall-lang/Prelude/Optional/package.dhall view
@@ -10,6 +10,9 @@ , concat = ./concat sha256:b49a3b7dc49eb83d150977caa5ae347be1cbbe14e3b6d0e07349bd2e5f707d69 ? ./concat+, default =+ ./default sha256:8f802473931b605422b545d7b81de20dbecb38f2ae63950c13f5381865a7f012+ ? ./default , filter = ./filter sha256:b3d5e19a6cec592a76c12167a9e5e1e76649e776229d70a11c77b76cd29f617e ? ./filter
dhall-lang/Prelude/Text/concatMapSep view
@@ -18,8 +18,7 @@ ( λ(x : a) → λ(status : Status) → merge- { Empty =- Status.NonEmpty (f x)+ { Empty = Status.NonEmpty (f x) , NonEmpty = λ(result : Text) → Status.NonEmpty (f x ++ separator ++ result)
dhall-lang/Prelude/Text/concatSep view
@@ -15,8 +15,7 @@ ( λ(element : Text) → λ(status : Status) → merge- { Empty =- Status.NonEmpty element+ { Empty = Status.NonEmpty element , NonEmpty = λ(result : Text) → Status.NonEmpty (element ++ separator ++ result)
dhall-lang/Prelude/Text/show view
@@ -2,7 +2,9 @@ Render a `Text` literal as its own representation as Dhall source code (i.e. a double-quoted string literal) -}-let show : Text → Text = Text/show+let show+ : Text → Text+ = Text/show let example0 = assert : show "ABC" ≡ "\"ABC\""
dhall-lang/Prelude/XML/Type view
@@ -48,15 +48,11 @@ : Type = ∀(XML : Type) → ∀ ( xml- : { text :- Text → XML+ : { text : Text → XML , element :- { attributes :- List { mapKey : Text, mapValue : Text }- , content :- List XML- , name :- Text+ { attributes : List { mapKey : Text, mapValue : Text }+ , content : List XML+ , name : Text } → XML }
dhall-lang/Prelude/XML/element view
@@ -28,12 +28,9 @@ ? ../List/map let Args =- { attributes :- List { mapKey : Text, mapValue : Text }- , name :- Text- , content :- List XML+ { attributes : List { mapKey : Text, mapValue : Text }+ , name : Text+ , content : List XML } : Type @@ -42,26 +39,19 @@ = λ(elem : Args) → λ(XML : Type) → λ ( xml- : { text :- Text → XML+ : { text : Text → XML , element :- { attributes :- List { mapKey : Text, mapValue : Text }- , content :- List XML- , name :- Text+ { attributes : List { mapKey : Text, mapValue : Text }+ , content : List XML+ , name : Text } → XML } ) → xml.element- { attributes =- elem.attributes- , name =- elem.name- , content =- List/map XML@1 XML (λ(x : XML@1) → x XML xml) elem.content+ { attributes = elem.attributes+ , name = elem.name+ , content = List/map XML@1 XML (λ(x : XML@1) → x XML xml) elem.content } in element
dhall-lang/Prelude/XML/leaf view
@@ -21,10 +21,8 @@ : { attributes : List { mapKey : Text, mapValue : Text }, name : Text } → XML = λ ( elem- : { attributes :- List { mapKey : Text, mapValue : Text }- , name :- Text+ : { attributes : List { mapKey : Text, mapValue : Text }+ , name : Text } ) → element (elem ⫽ { content = [] : List XML })
dhall-lang/Prelude/XML/render view
@@ -43,16 +43,12 @@ = λ(x : XML) → x Text- { text =- λ(d : Text) → d+ { text = λ(d : Text) → d , element = λ ( elem- : { attributes :- List { mapKey : Text, mapValue : Text }- , content :- List Text- , name :- Text+ : { attributes : List { mapKey : Text, mapValue : Text }+ , content : List Text+ , name : Text } ) → let attribs = Text/concatMap Attr renderAttr elem.attributes
dhall-lang/Prelude/XML/text view
@@ -23,15 +23,11 @@ = λ(d : Text) → λ(XML : Type) → λ ( xml- : { text :- Text → XML+ : { text : Text → XML , element :- { attributes :- List { mapKey : Text, mapValue : Text }- , content :- List XML- , name :- Text+ { attributes : List { mapKey : Text, mapValue : Text }+ , content : List XML+ , name : Text } → XML }
dhall-lang/Prelude/package.dhall view
@@ -11,13 +11,13 @@ ./Integer/package.dhall sha256:eb464566d3192dd16ce915a9bd874aaaad612d5c69beb356e5b7d2e0c4949dcf ? ./Integer/package.dhall , List =- ./List/package.dhall sha256:108be3af5ebd465f7091039f2216c433e65ae5d25556a9a71786dd84d33ef49a+ ./List/package.dhall sha256:88ae09f29981480e62a39c925011b3246d716a80871440f071cf981efea1ad5e ? ./List/package.dhall , Location = ./Location/package.dhall sha256:0eb4e4a60814018009c720f6820aaa13cf9491eb1b09afb7b832039c6ee4d470 ? ./Location/package.dhall , Map =- ./Map/package.dhall sha256:5e378b6da12f26b64f796f9c1b61d89bcc5a591f3a3dedcf85cb4637bbb8b618+ ./Map/package.dhall sha256:598e9c76103b2686fbbda6cc30078f9e60dd846d9eaf155d0149cf0ae06c21c5 ? ./Map/package.dhall , Monoid = ./Monoid sha256:26fafa098600ef7a54ef9dba5ada416bbbdd21df1af306c052420c61553ad4af@@ -26,7 +26,7 @@ ./Natural/package.dhall sha256:4574df29e6bb82fd894c311f6c2ad9a22574756eb62f06d475aa8306d25006fc ? ./Natural/package.dhall , Optional =- ./Optional/package.dhall sha256:36a366af67a3c26cd5d196e095d3023f18953c5b5db3a03956fa554609e5442a+ ./Optional/package.dhall sha256:7608f2d38dabee8bfe6865b4adc11289059984220f422d2b023b15b3908f7a4c ? ./Optional/package.dhall , JSON = ./JSON/package.dhall sha256:0c3c40a63108f2e6ad59f23b789c18eb484d0e9aebc9416c5a4f338c6753084b
dhall-lang/tests/binary-decode/failure/unit/OperatorUnknownOpcode.dhallb view
binary file changed (5 → 6 bytes)
+ dhall-lang/tests/binary-decode/success/unit/CompletionA.dhallb view
binary file changed (absent → 11 bytes)
+ dhall-lang/tests/binary-decode/success/unit/CompletionB.dhall view
@@ -0,0 +1,1 @@+T::r
+ dhall-lang/tests/import/data/importBoundary.dhall view
@@ -0,0 +1,3 @@+-- This file contains a free variable. This should be a type error, even if the variable+-- is in scope in the importing expression+x
+ dhall-lang/tests/import/failure/customHeadersUsingBoundVariable.dhall view
@@ -0,0 +1,15 @@+{- This is a type error because the custom headers are type-checked with an+ empty context. This is necessary because:++ * import resolution precedes β-normalization+ * we also don't want custom headers to leak program state anyway++ This should fail due to the `x` within the custom header being an unbound+ variable+-}++let x = "Bar"++in https://httpbin.org/headers+ using [ { mapKey = "Foo", mapValue = x } ]+ as Text
+ dhall-lang/tests/import/failure/importBoundary.dhall view
@@ -0,0 +1,1 @@+\(x: Bool) -> ../data/importBoundary.dhall
+ dhall-lang/tests/normalization/success/simple/completionA.dhall view
@@ -0,0 +1,10 @@+let Example =+ { Type = { name : Text, id : Optional Natural }+ , default = { name = "", id = None Natural }+ }++in { example0 = Example::{=}+ , example1 = Example::{ name = "John Doe" }+ , example2 = Example::{ id = Some 0 }+ , example3 = Example::{ name = "Mary Jane", id = Some 0 }+ }
+ dhall-lang/tests/normalization/success/simple/completionB.dhall view
@@ -0,0 +1,5 @@+{ example0 = { id = None Natural, name = "" }+, example1 = { id = None Natural, name = "John Doe" }+, example2 = { id = Some 0, name = "" }+, example3 = { id = Some 0, name = "Mary Jane" }+}
+ dhall-lang/tests/normalization/success/unit/CompletionA.dhall view
@@ -0,0 +1,1 @@+{ default = {=}, Type = {} }::{=}
+ dhall-lang/tests/normalization/success/unit/CompletionB.dhall view
@@ -0,0 +1,1 @@+{=}
+ dhall-lang/tests/normalization/success/unit/RecursiveRecordTypeMergeDeepA.dhall view
@@ -0,0 +1,1 @@+{ a : { b : { x : Bool } } } ⩓ { a : { b : { y : Bool } } }
+ dhall-lang/tests/normalization/success/unit/RecursiveRecordTypeMergeDeepB.dhall view
@@ -0,0 +1,1 @@+{ a : { b : { x : Bool, y : Bool } } }
+ dhall-lang/tests/parser/failure/nonBase16Hash.dhall view
@@ -0,0 +1,1 @@+./foo sha256:d60d8415e36e86dae7f42933d3b0c4fe3ca238f057fba206c7e9fbf5d784fe1g
+ dhall-lang/tests/parser/success/leadingSeparatorsA.dhall view
@@ -0,0 +1,10 @@+{ emptyRecordLiteral = { , = }+, emptyRecordType = { , }+, emptyList = [,] : List Natural+, emptyUnionType = < | >++, recordLiteral = { , x = 1, y = True }+, recordType = { , x : Natural, y : Bool }+, list = [ , 1, 2 ]+, unionType = < | x : Natural | y : Bool >+}
+ dhall-lang/tests/parser/success/leadingSeparatorsB.dhallb view
@@ -0,0 +1,1 @@+¨iemptyListgNaturalremptyRecordLiteral oemptyRecordType nemptyUnionType dlistömrecordLiteral¢axayõjrecordType¢axgNaturalaydBooliunionType¢axgNaturalaydBool
+ dhall-lang/tests/parser/success/lineCommentCRLFA.dhall view
@@ -0,0 +1,3 @@+-- foo + +1
+ dhall-lang/tests/parser/success/lineCommentCRLFB.dhallb view
@@ -0,0 +1,1 @@+
+ dhall-lang/tests/parser/success/text/dollarSignA.dhall view
@@ -0,0 +1,1 @@+"$"
+ dhall-lang/tests/parser/success/text/dollarSignB.dhallb view
@@ -0,0 +1,1 @@+a$
+ dhall-lang/tests/parser/success/unit/CompletionA.dhall view
@@ -0,0 +1,1 @@+T::r
+ dhall-lang/tests/parser/success/unit/CompletionB.dhallb view
binary file changed (absent → 11 bytes)
+ dhall-lang/tests/parser/success/unit/DoubleLitNegZeroA.dhall view
@@ -0,0 +1,1 @@+-0.0
+ dhall-lang/tests/parser/success/unit/DoubleLitNegZeroB.dhallb view
binary file changed (absent → 3 bytes)
dhall-lang/tests/parser/success/unit/import/environmentVariablePosixA.dhall view
@@ -1,2 +1,2 @@ -- Yes, this is legal-env:"\"\\\a\b\f\n\r\t\v"+env:"\"\\\a\b\f\n\r\t\v!<[~"
dhall-lang/tests/parser/success/unit/import/environmentVariablePosixB.dhallb view
binary file changed (16 → 20 bytes)
+ dhall-lang/tests/parser/success/unit/import/urls/ipv4upperoctetsA.dhall view
@@ -0,0 +1,1 @@+https://200.249.250.255/index.dhall
+ dhall-lang/tests/parser/success/unit/import/urls/ipv4upperoctetsB.dhallb view
binary file changed (absent → 36 bytes)
+ dhall-lang/tests/parser/success/unit/import/urls/ipv6verylongA.dhall view
@@ -0,0 +1,1 @@+http://[AAAA:BBBB:CCCC:DDDD:EEEE:FFFF:0000::]/tutorial.dhall
+ dhall-lang/tests/parser/success/unit/import/urls/ipv6verylongB.dhallb view
binary file changed (absent → 63 bytes)
− dhall-lang/tests/type-inference/data/importBoundary.dhall
@@ -1,3 +0,0 @@--- This file contains a free variable. This should be a type error, even if the variable--- is in scope in the importing expression-x
− dhall-lang/tests/type-inference/failure/customHeadersUsingBoundVariable.dhall
@@ -1,15 +0,0 @@-{- This is a type error because the custom headers are type-checked with an- empty context. This is necessary because:-- * import resolution precedes β-normalization- * we also don't want custom headers to leak program state anyway-- This should fail due to the `x` within the custom header being an unbound- variable--}--let x = "Bar"--in https://httpbin.org/headers- using [ { mapKey = "Foo", mapValue = x } ]- as Text
− dhall-lang/tests/type-inference/failure/importBoundary.dhall
@@ -1,1 +0,0 @@-\(x: Bool) -> ../data/importBoundary.dhall
dhall-lang/tests/type-inference/success/preludeB.dhall view
@@ -1,23 +1,16 @@ { Bool :- { and :- ∀(xs : List Bool) → Bool+ { and : ∀(xs : List Bool) → Bool , build : ∀(f : ∀(bool : Type) → ∀(true : bool) → ∀(false : bool) → bool) → Bool- , even :- ∀(xs : List Bool) → Bool+ , even : ∀(xs : List Bool) → Bool , fold : ∀(b : Bool) → ∀(bool : Type) → ∀(true : bool) → ∀(false : bool) → bool- , not :- ∀(b : Bool) → Bool- , odd :- ∀(xs : List Bool) → Bool- , or :- ∀(xs : List Bool) → Bool- , show :- ∀(b : Bool) → Text+ , not : ∀(b : Bool) → Bool+ , odd : ∀(xs : List Bool) → Bool+ , or : ∀(xs : List Bool) → Bool+ , show : ∀(b : Bool) → Text }-, Double :- { show : Double → Text }+, Double : { show : Double → Text } , Function : { compose : ∀(A : Type)@@ -28,32 +21,23 @@ → ∀(x : A) → C }-, Integer :- { show : Integer → Text, toDouble : Integer → Double }+, Integer : { show : Integer → Text, toDouble : Integer → Double } , JSON :- { Nesting :- Type- , Tagged :- ∀(a : Type) → Type- , Type :- Type+ { Nesting : Type+ , Tagged : ∀(a : Type) → Type+ , Type : Type , array : ∀ ( x : List ( ∀(JSON : Type) → ∀ ( json- : { array :- List JSON → JSON- , bool :- Bool → JSON- , null :- JSON- , number :- Double → JSON+ : { array : List JSON → JSON+ , bool : Bool → JSON+ , null : JSON+ , number : Double → JSON , object : List { mapKey : Text, mapValue : JSON } → JSON- , string :- Text → JSON+ , string : Text → JSON } ) → JSON@@ -61,18 +45,12 @@ ) → ∀(JSON : Type) → ∀ ( json- : { array :- List JSON → JSON- , bool :- Bool → JSON- , null :- JSON- , number :- Double → JSON- , object :- List { mapKey : Text, mapValue : JSON } → JSON- , string :- Text → JSON+ : { array : List JSON → JSON+ , bool : Bool → JSON+ , null : JSON+ , number : Double → JSON+ , object : List { mapKey : Text, mapValue : JSON } → JSON+ , string : Text → JSON } ) → JSON@@ -80,18 +58,12 @@ ∀(x : Bool) → ∀(JSON : Type) → ∀ ( json- : { array :- List JSON → JSON- , bool :- Bool → JSON- , null :- JSON- , number :- Double → JSON- , object :- List { mapKey : Text, mapValue : JSON } → JSON- , string :- Text → JSON+ : { array : List JSON → JSON+ , bool : Bool → JSON+ , null : JSON+ , number : Double → JSON+ , object : List { mapKey : Text, mapValue : JSON } → JSON+ , string : Text → JSON } ) → JSON@@ -105,18 +77,12 @@ , null : ∀(JSON : Type) → ∀ ( json- : { array :- List JSON → JSON- , bool :- Bool → JSON- , null :- JSON- , number :- Double → JSON- , object :- List { mapKey : Text, mapValue : JSON } → JSON- , string :- Text → JSON+ : { array : List JSON → JSON+ , bool : Bool → JSON+ , null : JSON+ , number : Double → JSON+ , object : List { mapKey : Text, mapValue : JSON } → JSON+ , string : Text → JSON } ) → JSON@@ -124,41 +90,29 @@ ∀(x : Double) → ∀(JSON : Type) → ∀ ( json- : { array :- List JSON → JSON- , bool :- Bool → JSON- , null :- JSON- , number :- Double → JSON- , object :- List { mapKey : Text, mapValue : JSON } → JSON- , string :- Text → JSON+ : { array : List JSON → JSON+ , bool : Bool → JSON+ , null : JSON+ , number : Double → JSON+ , object : List { mapKey : Text, mapValue : JSON } → JSON+ , string : Text → JSON } ) → JSON , object : ∀ ( x : List- { mapKey :- Text+ { mapKey : Text , mapValue : ∀(JSON : Type) → ∀ ( json- : { array :- List JSON → JSON- , bool :- Bool → JSON- , null :- JSON- , number :- Double → JSON+ : { array : List JSON → JSON+ , bool : Bool → JSON+ , null : JSON+ , number : Double → JSON , object : List { mapKey : Text, mapValue : JSON } → JSON- , string :- Text → JSON+ , string : Text → JSON } ) → JSON@@ -166,18 +120,12 @@ ) → ∀(JSON : Type) → ∀ ( json- : { array :- List JSON → JSON- , bool :- Bool → JSON- , null :- JSON- , number :- Double → JSON- , object :- List { mapKey : Text, mapValue : JSON } → JSON- , string :- Text → JSON+ : { array : List JSON → JSON+ , bool : Bool → JSON+ , null : JSON+ , number : Double → JSON+ , object : List { mapKey : Text, mapValue : JSON } → JSON+ , string : Text → JSON } ) → JSON@@ -185,18 +133,12 @@ ∀ ( j : ∀(JSON : Type) → ∀ ( json- : { array :- List JSON → JSON- , bool :- Bool → JSON- , null :- JSON- , number :- Double → JSON- , object :- List { mapKey : Text, mapValue : JSON } → JSON- , string :- Text → JSON+ : { array : List JSON → JSON+ , bool : Bool → JSON+ , null : JSON+ , number : Double → JSON+ , object : List { mapKey : Text, mapValue : JSON } → JSON+ , string : Text → JSON } ) → JSON@@ -206,37 +148,29 @@ ∀(x : Text) → ∀(JSON : Type) → ∀ ( json- : { array :- List JSON → JSON- , bool :- Bool → JSON- , null :- JSON- , number :- Double → JSON- , object :- List { mapKey : Text, mapValue : JSON } → JSON- , string :- Text → JSON+ : { array : List JSON → JSON+ , bool : Bool → JSON+ , null : JSON+ , number : Double → JSON+ , object : List { mapKey : Text, mapValue : JSON } → JSON+ , string : Text → JSON } ) → JSON } , List :- { all :- ∀(a : Type) → ∀(f : a → Bool) → ∀(xs : List a) → Bool- , any :- ∀(a : Type) → ∀(f : a → Bool) → ∀(xs : List a) → Bool+ { all : ∀(a : Type) → ∀(f : a → Bool) → ∀(xs : List a) → Bool+ , any : ∀(a : Type) → ∀(f : a → Bool) → ∀(xs : List a) → Bool , build : ∀(a : Type) → (∀(list : Type) → ∀(cons : a → list → list) → ∀(nil : list) → list) → List a- , concat :- ∀(a : Type) → ∀(xss : List (List a)) → List a+ , concat : ∀(a : Type) → ∀(xss : List (List a)) → List a , concatMap : ∀(a : Type) → ∀(b : Type) → ∀(f : a → List b) → ∀(xs : List a) → List b- , filter :- ∀(a : Type) → ∀(f : a → Bool) → ∀(xs : List a) → List a+ , default : ∀(a : Type) → ∀(o : Optional (List a)) → List a+ , empty : ∀(a : Type) → List a+ , filter : ∀(a : Type) → ∀(f : a → Bool) → ∀(xs : List a) → List a , fold : ∀(a : Type) → List a@@ -244,26 +178,16 @@ → ∀(cons : a → list → list) → ∀(nil : list) → list- , generate :- ∀(n : Natural) → ∀(a : Type) → ∀(f : Natural → a) → List a- , head :- ∀(a : Type) → List a → Optional a- , indexed :- ∀(a : Type) → List a → List { index : Natural, value : a }- , iterate :- ∀(n : Natural) → ∀(a : Type) → ∀(f : a → a) → ∀(x : a) → List a- , last :- ∀(a : Type) → List a → Optional a- , length :- ∀(a : Type) → List a → Natural- , map :- ∀(a : Type) → ∀(b : Type) → ∀(f : a → b) → ∀(xs : List a) → List b- , null :- ∀(a : Type) → ∀(xs : List a) → Bool- , replicate :- ∀(n : Natural) → ∀(a : Type) → ∀(x : a) → List a- , reverse :- ∀(a : Type) → List a → List a+ , generate : ∀(n : Natural) → ∀(a : Type) → ∀(f : Natural → a) → List a+ , head : ∀(a : Type) → List a → Optional a+ , indexed : ∀(a : Type) → List a → List { index : Natural, value : a }+ , iterate : ∀(n : Natural) → ∀(a : Type) → ∀(f : a → a) → ∀(x : a) → List a+ , last : ∀(a : Type) → List a → Optional a+ , length : ∀(a : Type) → List a → Natural+ , map : ∀(a : Type) → ∀(b : Type) → ∀(f : a → b) → ∀(xs : List a) → List b+ , null : ∀(a : Type) → ∀(xs : List a) → Bool+ , replicate : ∀(n : Natural) → ∀(a : Type) → ∀(x : a) → List a+ , reverse : ∀(a : Type) → List a → List a , shifted : ∀(a : Type) → ∀(kvss : List (List { index : Natural, value : a }))@@ -274,13 +198,18 @@ → ∀(xs : List { _1 : a, _2 : b }) → { _1 : List a, _2 : List b } }-, Location :- { Type : Type }+, Location : { Type : Type } , Map :- { Entry :- ∀(k : Type) → ∀(v : Type) → Type- , Type :- ∀(k : Type) → ∀(v : Type) → Type+ { Entry : ∀(k : Type) → ∀(v : Type) → Type+ , Type : ∀(k : Type) → ∀(v : Type) → Type+ , empty : ∀(k : Type) → ∀(v : Type) → List { mapKey : k, mapValue : v }+ , keyText :+ ∀(key : Text) → ∀(value : Text) → { mapKey : Text, mapValue : Text }+ , keyValue :+ ∀(v : Type)+ → ∀(key : Text)+ → ∀(value : v)+ → { mapKey : Text, mapValue : v } , keys : ∀(k : Type) → ∀(v : Type)@@ -299,8 +228,7 @@ → ∀(xs : List { mapKey : k, mapValue : v }) → List v }-, Monoid :- ∀(m : Type) → Type+, Monoid : ∀(m : Type) → Type , Natural : { build : ( ∀(natural : Type)@@ -309,48 +237,31 @@ → natural ) → Natural- , enumerate :- ∀(n : Natural) → List Natural- , equal :- ∀(a : Natural) → ∀(b : Natural) → Bool- , even :- Natural → Bool+ , enumerate : ∀(n : Natural) → List Natural+ , equal : ∀(a : Natural) → ∀(b : Natural) → Bool+ , even : Natural → Bool , fold : Natural → ∀(natural : Type) → ∀(succ : natural → natural) → ∀(zero : natural) → natural- , greaterThan :- ∀(x : Natural) → ∀(y : Natural) → Bool- , greaterThanEqual :- ∀(x : Natural) → ∀(y : Natural) → Bool- , isZero :- Natural → Bool- , lessThan :- ∀(x : Natural) → ∀(y : Natural) → Bool- , lessThanEqual :- ∀(x : Natural) → ∀(y : Natural) → Bool- , odd :- Natural → Bool- , product :- ∀(xs : List Natural) → Natural- , show :- Natural → Text- , subtract :- Natural → Natural → Natural- , sum :- ∀(xs : List Natural) → Natural- , toDouble :- ∀(n : Natural) → Double- , toInteger :- Natural → Integer+ , greaterThan : ∀(x : Natural) → ∀(y : Natural) → Bool+ , greaterThanEqual : ∀(x : Natural) → ∀(y : Natural) → Bool+ , isZero : Natural → Bool+ , lessThan : ∀(x : Natural) → ∀(y : Natural) → Bool+ , lessThanEqual : ∀(x : Natural) → ∀(y : Natural) → Bool+ , odd : Natural → Bool+ , product : ∀(xs : List Natural) → Natural+ , show : Natural → Text+ , subtract : Natural → Natural → Natural+ , sum : ∀(xs : List Natural) → Natural+ , toDouble : ∀(n : Natural) → Double+ , toInteger : Natural → Integer } , Optional :- { all :- ∀(a : Type) → ∀(f : a → Bool) → ∀(xs : Optional a) → Bool- , any :- ∀(a : Type) → ∀(f : a → Bool) → ∀(xs : Optional a) → Bool+ { all : ∀(a : Type) → ∀(f : a → Bool) → ∀(xs : Optional a) → Bool+ , any : ∀(a : Type) → ∀(f : a → Bool) → ∀(xs : Optional a) → Bool , build : ∀(a : Type) → ( ∀(optional : Type)@@ -359,10 +270,9 @@ → optional ) → Optional a- , concat :- ∀(a : Type) → ∀(x : Optional (Optional a)) → Optional a- , filter :- ∀(a : Type) → ∀(f : a → Bool) → ∀(xs : Optional a) → Optional a+ , concat : ∀(a : Type) → ∀(x : Optional (Optional a)) → Optional a+ , default : ∀(a : Type) → ∀(default : a) → ∀(o : Optional a) → a+ , filter : ∀(a : Type) → ∀(f : a → Bool) → ∀(xs : Optional a) → Optional a , fold : ∀(a : Type) → Optional a@@ -370,22 +280,17 @@ → ∀(just : a → optional) → ∀(nothing : optional) → optional- , head :- ∀(a : Type) → ∀(xs : List (Optional a)) → Optional a- , last :- ∀(a : Type) → ∀(xs : List (Optional a)) → Optional a- , length :- ∀(a : Type) → ∀(xs : Optional a) → Natural+ , head : ∀(a : Type) → ∀(xs : List (Optional a)) → Optional a+ , last : ∀(a : Type) → ∀(xs : List (Optional a)) → Optional a+ , length : ∀(a : Type) → ∀(xs : Optional a) → Natural , map : ∀(a : Type) → ∀(b : Type) → ∀(f : a → b) → ∀(o : Optional a) → Optional b- , null :- ∀(a : Type) → ∀(xs : Optional a) → Bool- , toList :- ∀(a : Type) → ∀(o : Optional a) → List a+ , null : ∀(a : Type) → ∀(xs : Optional a) → Bool+ , toList : ∀(a : Type) → ∀(o : Optional a) → List a , unzip : ∀(a : Type) → ∀(b : Type)@@ -393,34 +298,26 @@ → { _1 : Optional a, _2 : Optional b } } , Text :- { concat :- ∀(xs : List Text) → Text- , concatMap :- ∀(a : Type) → ∀(f : a → Text) → ∀(xs : List a) → Text+ { concat : ∀(xs : List Text) → Text+ , concatMap : ∀(a : Type) → ∀(f : a → Text) → ∀(xs : List a) → Text , concatMapSep : ∀(separator : Text) → ∀(a : Type) → ∀(f : a → Text) → ∀(elements : List a) → Text- , concatSep :- ∀(separator : Text) → ∀(elements : List Text) → Text- , default :- ∀(o : Optional Text) → Text- , defaultMap :- ∀(a : Type) → ∀(f : a → Text) → ∀(o : Optional a) → Text- , show :- Text → Text+ , concatSep : ∀(separator : Text) → ∀(elements : List Text) → Text+ , default : ∀(o : Optional Text) → Text+ , defaultMap : ∀(a : Type) → ∀(f : a → Text) → ∀(o : Optional a) → Text+ , show : Text → Text } , XML :- { Type :- Type+ { Type : Type , attribute : ∀(key : Text) → ∀(value : Text) → { mapKey : Text, mapValue : Text } , element : ∀ ( elem- : { attributes :- List { mapKey : Text, mapValue : Text }+ : { attributes : List { mapKey : Text, mapValue : Text } , content : List ( ∀(XML : Type)@@ -428,61 +325,46 @@ : { element : { attributes : List { mapKey : Text, mapValue : Text }- , content :- List XML- , name :- Text+ , content : List XML+ , name : Text } → XML- , text :- Text → XML+ , text : Text → XML } ) → XML )- , name :- Text+ , name : Text } ) → ∀(XML : Type) → ∀ ( xml : { element :- { attributes :- List { mapKey : Text, mapValue : Text }- , content :- List XML- , name :- Text+ { attributes : List { mapKey : Text, mapValue : Text }+ , content : List XML+ , name : Text } → XML- , text :- Text → XML+ , text : Text → XML } ) → XML- , emptyAttributes :- List { mapKey : Text, mapValue : Text }+ , emptyAttributes : List { mapKey : Text, mapValue : Text } , leaf : ∀ ( elem- : { attributes :- List { mapKey : Text, mapValue : Text }- , name :- Text+ : { attributes : List { mapKey : Text, mapValue : Text }+ , name : Text } ) → ∀(XML : Type) → ∀ ( xml : { element :- { attributes :- List { mapKey : Text, mapValue : Text }- , content :- List XML- , name :- Text+ { attributes : List { mapKey : Text, mapValue : Text }+ , content : List XML+ , name : Text } → XML- , text :- Text → XML+ , text : Text → XML } ) → XML@@ -491,16 +373,12 @@ : ∀(XML : Type) → ∀ ( xml : { element :- { attributes :- List { mapKey : Text, mapValue : Text }- , content :- List XML- , name :- Text+ { attributes : List { mapKey : Text, mapValue : Text }+ , content : List XML+ , name : Text } → XML- , text :- Text → XML+ , text : Text → XML } ) → XML@@ -511,16 +389,12 @@ → ∀(XML : Type) → ∀ ( xml : { element :- { attributes :- List { mapKey : Text, mapValue : Text }- , content :- List XML- , name :- Text+ { attributes : List { mapKey : Text, mapValue : Text }+ , content : List XML+ , name : Text } → XML- , text :- Text → XML+ , text : Text → XML } ) → XML
+ dhall-lang/tests/type-inference/success/simple/completionA.dhall view
@@ -0,0 +1,25 @@+let Example =+ { Type = { name : Text, id : Optional Natural }+ , default = { name = "", id = None Natural }+ }++let ExtraFields = Example ∧ { extra = {=} }++let RequiredFields =+ { Type = { name : Text, id : Optional Natural }+ , default = { id = None Natural }+ }++let WrongDefaultType =+ { Type = { name : Text, id : Optional Natural }+ , default = { id = True }+ }++in { example0 = Example::{=}+ , example1 = Example::{ name = "John Doe" }+ , example2 = Example::{ id = Some 0 }+ , example3 = Example::{ name = "Mary Jane", id = Some 0 }+ , example4 = ExtraFields::{=}+ , example5 = RequiredFields::{ name = "Jane Smith" }+ , example6 = WrongDefaultType::{ name = "Bob Roberts", id = Some 0 }+ }
+ dhall-lang/tests/type-inference/success/simple/completionB.dhall view
@@ -0,0 +1,8 @@+{ example0 : { id : Optional Natural, name : Text }+, example1 : { id : Optional Natural, name : Text }+, example2 : { id : Optional Natural, name : Text }+, example3 : { id : Optional Natural, name : Text }+, example4 : { id : Optional Natural, name : Text }+, example5 : { id : Optional Natural, name : Text }+, example6 : { id : Optional Natural, name : Text }+}
+ dhall-lang/tests/type-inference/success/simple/orderInsensitivityA.dhall view
@@ -0,0 +1,15 @@+{- This test caught a regression in the Haskell implementation's typechecker.++ The typechecker was not sorting the fields of a record type inferred from a+ projection, meaning that it inferred that the fully normalized type of+ the subexpression `{ bar = "", foo = "" }.{ foo, bar }` was+ `{ foo : Text, bar : Text }` when according to the standard the inferred type+ should have been `{ bar : Text, foo : Text }`. On top of that the Haskell+ implementation was sensitive to field order, leading to a type mismatch+ when applying a function whose input type had the fields sorted.++ This test ensures that implementations correctly handle order-insensitivity+ when projecting fields and/or comparing types, just in case they are+ order-sensitive like the Haskell implementation.+-}+λ(f : { bar : Text, foo : Text } → Bool) → f { bar = "", foo = "" }.{ foo, bar }
+ dhall-lang/tests/type-inference/success/simple/orderInsensitivityB.dhall view
@@ -0,0 +1,1 @@+∀(f : { bar : Text, foo : Text } → Bool) → Bool
+ dhall-lang/tests/type-inference/success/simple/toMapEmptyNormalizeAnnotationA.dhall view
@@ -0,0 +1,3 @@+toMap+ { x = Bool }.{}+: List { mapKey : { t = Text }.t, mapValue : { t = Double }.t }
+ dhall-lang/tests/type-inference/success/simple/toMapEmptyNormalizeAnnotationB.dhall view
@@ -0,0 +1,1 @@+List { mapKey : Text, mapValue : Double }
+ dhall-lang/tests/type-inference/success/unit/CompletionA.dhall view
@@ -0,0 +1,1 @@+{ default = {=}, Type = {} }::{=}
+ dhall-lang/tests/type-inference/success/unit/CompletionB.dhall view
@@ -0,0 +1,1 @@+{}
+ dhall-lang/tests/type-inference/success/unit/FunctionApplicationNormalizeArgumentA.dhall view
@@ -0,0 +1,1 @@+List/length (let x = 0 in Bool)
+ dhall-lang/tests/type-inference/success/unit/FunctionApplicationNormalizeArgumentB.dhall view
@@ -0,0 +1,1 @@+List Bool → Natural
+ dhall-lang/tests/type-inference/success/unit/FunctionApplicationNormalizeResultA.dhall view
@@ -0,0 +1,1 @@+(λ(g : Natural → Bool) → assert : g 0 ≡ g 0) Natural/even
+ dhall-lang/tests/type-inference/success/unit/FunctionApplicationNormalizeResultB.dhall view
@@ -0,0 +1,1 @@+True ≡ True
+ dhall-lang/tests/type-inference/success/unit/FunctionNormalizeTypeAnnotationA.dhall view
@@ -0,0 +1,1 @@+λ(x : { t = Bool }.t) → x
+ dhall-lang/tests/type-inference/success/unit/FunctionNormalizeTypeAnnotationB.dhall view
@@ -0,0 +1,1 @@+∀(x : Bool) → Bool
+ dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotation0A.dhall view
@@ -0,0 +1,1 @@+merge { x = λ(_ : Bool) → _ } (< x : Bool >.x True) : Bool
+ dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotation0B.dhall view
@@ -0,0 +1,1 @@+Bool
+ dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotation1A.dhall view
@@ -0,0 +1,1 @@+merge { x = λ(y : Bool) → y } < x >.x : (Bool → Bool)
+ dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotation1B.dhall view
@@ -0,0 +1,1 @@+∀(y : Bool) → Bool
− dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotationA.dhall
@@ -1,1 +0,0 @@-merge { x = λ(_ : Bool) → _ } (< x : Bool >.x True) : Bool
− dhall-lang/tests/type-inference/success/unit/MergeOneWithAnnotationB.dhall
@@ -1,1 +0,0 @@-Bool
+ dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeDeepA.dhall view
@@ -0,0 +1,1 @@+{ a : { b : { x : Bool } } } ⩓ { a : { b : { y : Bool } } }
+ dhall-lang/tests/type-inference/success/unit/RecursiveRecordTypeMergeDeepB.dhall view
@@ -0,0 +1,1 @@+Type
+ dhall-lang/tests/type-inference/success/unit/ToMapInferTypeFromRecordA.dhall view
@@ -0,0 +1,1 @@+toMap { x = λ(x : Bool) → x } : List { mapKey : Text, mapValue : Bool → Bool }
+ dhall-lang/tests/type-inference/success/unit/ToMapInferTypeFromRecordB.dhall view
@@ -0,0 +1,1 @@+List { mapKey : Text, mapValue : ∀(x : Bool) → Bool }
+ dhall-lang/tests/type-inference/success/unit/TypeAnnotationFunctionA.dhall view
@@ -0,0 +1,1 @@+(λ(x : Bool) → x) : Bool → Bool
+ dhall-lang/tests/type-inference/success/unit/TypeAnnotationFunctionB.dhall view
@@ -0,0 +1,1 @@+∀(x : Bool) → Bool
+ dhall-lang/tests/type-inference/success/unit/TypeAnnotationNormalizeA.dhall view
@@ -0,0 +1,1 @@+True : { x = Bool }.x
+ dhall-lang/tests/type-inference/success/unit/TypeAnnotationNormalizeB.dhall view
@@ -0,0 +1,1 @@+Bool
dhall.cabal view
@@ -1,5 +1,5 @@ Name: dhall-Version: 1.26.1+Version: 1.27.0 Cabal-Version: >=1.10 Build-Type: Simple Tested-With: GHC == 7.10.3, GHC == 8.4.3, GHC == 8.6.1@@ -61,6 +61,8 @@ dhall-lang/Prelude/List/build dhall-lang/Prelude/List/concat dhall-lang/Prelude/List/concatMap+ dhall-lang/Prelude/List/default+ dhall-lang/Prelude/List/empty dhall-lang/Prelude/List/filter dhall-lang/Prelude/List/fold dhall-lang/Prelude/List/generate@@ -79,7 +81,10 @@ dhall-lang/Prelude/Location/package.dhall dhall-lang/Prelude/Location/Type dhall-lang/Prelude/Map/Entry+ dhall-lang/Prelude/Map/empty dhall-lang/Prelude/Map/keys+ dhall-lang/Prelude/Map/keyText+ dhall-lang/Prelude/Map/keyValue dhall-lang/Prelude/Map/map dhall-lang/Prelude/Map/package.dhall dhall-lang/Prelude/Map/Type@@ -107,6 +112,7 @@ dhall-lang/Prelude/Optional/any dhall-lang/Prelude/Optional/build dhall-lang/Prelude/Optional/concat+ dhall-lang/Prelude/Optional/default dhall-lang/Prelude/Optional/filter dhall-lang/Prelude/Optional/fold dhall-lang/Prelude/Optional/head@@ -295,7 +301,6 @@ dhall-lang/tests/semantic-hash/success/simple/*.hash dhall-lang/tests/semantic-hash/success/simplifications/*.dhall dhall-lang/tests/semantic-hash/success/simplifications/*.hash- dhall-lang/tests/type-inference/data/*.dhall dhall-lang/tests/type-inference/failure/*.dhall dhall-lang/tests/type-inference/success/*.dhall dhall-lang/tests/type-inference/success/prelude/Bool/and/*.dhall@@ -366,6 +371,8 @@ tests/diff/*.txt tests/recursive/*.dhall tests/regression/*.dhall+ tests/tags/*.dhall+ tests/tags/*.tags tests/tutorial/*.dhall Source-Repository head@@ -404,6 +411,7 @@ exceptions >= 0.8.3 && < 0.11, filepath >= 1.4 && < 1.5 , haskeline >= 0.7.2.1 && < 0.8 ,+ hashable >= 1.2 && < 1.3 , lens-family-core >= 1.0.0 && < 2.1 , megaparsec >= 6.5.0 && < 7.1 , memory >= 0.14 && < 0.16,@@ -482,9 +490,9 @@ Dhall.Core, Dhall.Crypto, Dhall.Diff,+ Dhall.Tags, Dhall.Format, Dhall.Freeze,- Dhall.Hash, Dhall.Import, Dhall.Lint, Dhall.Main,@@ -509,6 +517,7 @@ Dhall.Parser.Combinators, Dhall.URL, Dhall.Import.Types,+ Dhall.Syntax, Dhall.Eval, Paths_dhall Autogen-Modules:@@ -535,6 +544,7 @@ Other-Modules: Dhall.Test.Dhall Dhall.Test.Diff+ Dhall.Test.Tags Dhall.Test.Format Dhall.Test.Import Dhall.Test.Lint@@ -555,6 +565,7 @@ deepseq >= 1.2.0.1 && < 1.5 , dhall , directory ,+ either , filepath , foldl < 1.5 , generic-random >= 1.3.0.0 && < 1.4 ,@@ -566,6 +577,7 @@ scientific , semigroups , serialise ,+ special-values < 0.2 , spoon < 0.4 , tasty >= 0.11.2 && < 1.3 , tasty-expected-failure < 0.12,@@ -574,6 +586,7 @@ text >= 0.11.1.0 && < 1.3 , transformers , turtle < 1.6 ,+ unordered-containers , vector >= 0.11.0.0 && < 0.13 if flag(with-http) CPP-Options:
doctest/Main.hs view
@@ -17,7 +17,7 @@ prefix <- System.Directory.makeAbsolute pwd Test.Mockery.Directory.inTempDirectory $ do- writeFile "makeBools" "λ(n : Bool) → [ n && True, n && False, n || True, n || False ]"+ writeFile "makeBools.dhall" "λ(n : Bool) → [ n && True, n && False, n || True, n || False ]" writeFile "bool1" "True" writeFile "bool2" "False" writeFile "both" "./bool1 && ./bool2"@@ -32,4 +32,5 @@ , prefix </> "src/Dhall.hs" , prefix </> "src/Dhall/Import.hs" , prefix </> "src/Dhall/Tutorial.hs"+ , prefix </> "src/Dhall/Tags.hs" ]
ghc-src/Dhall/Crypto.hs view
@@ -1,6 +1,10 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-| This module provides implementations of cryptographic utilities that only+ work for GHC (as opposed to GHCJS)+-}+ module Dhall.Crypto ( SHA256Digest(..) , sha256DigestFromByteString@@ -17,17 +21,22 @@ import qualified Crypto.Hash import qualified Data.ByteString.Char8 as ByteString.Char8 +-- | A SHA256 digest newtype SHA256Digest = SHA256Digest { unSHA256Digest :: ByteString } deriving (Eq, Generic, Ord, NFData, ByteArrayAccess) instance Show SHA256Digest where show (SHA256Digest bytes) = ByteString.Char8.unpack $ convertToBase Base16 bytes +{-| Attempt to interpret a `ByteString` as a `SHA256Digest`, returning+ `Nothing` if the conversion fails+-} sha256DigestFromByteString :: ByteString -> Maybe SHA256Digest sha256DigestFromByteString bytes = SHA256Digest . convert <$> mh where mh = Crypto.Hash.digestFromByteString bytes :: Maybe (Crypto.Hash.Digest SHA256) +-- | Hash a `ByteString` and return the hash as a `SHA256Digest` sha256Hash :: ByteString -> SHA256Digest sha256Hash bytes = SHA256Digest $ convert h where
ghcjs-src/Dhall/Crypto.hs view
@@ -2,6 +2,10 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE JavaScriptFFI #-} +{-| This module provides implementations of cryptographic utilities that only+ work for GHCJS+-}+ module Dhall.Crypto ( SHA256Digest(..) , sha256DigestFromByteString@@ -20,12 +24,16 @@ import qualified Data.ByteString.Char8 as ByteString.Char8 import qualified GHCJS.Buffer as Buffer +-- | A SHA256 digest newtype SHA256Digest = SHA256Digest { unSHA256Digest :: ByteString } deriving (Eq, Generic, Ord, NFData, ByteArrayAccess) instance Show SHA256Digest where show (SHA256Digest bytes) = ByteString.Char8.unpack $ convertToBase Base16 bytes +{-| Attempt to interpret a `ByteString` as a `SHA256Digest`, returning+ `Nothing` if the conversion fails+-} sha256DigestFromByteString :: ByteString -> Maybe SHA256Digest sha256DigestFromByteString bytes | ByteString.length bytes == 32 = Just $ SHA256Digest bytes@@ -55,6 +63,7 @@ arrayBufferToByteString = Buffer.toByteString 0 Nothing . Buffer.createFromArrayBuffer +-- | Hash a `ByteString` and return the hash as a `SHA256Digest` sha256Hash :: ByteString -> SHA256Digest sha256Hash bytes | ByteString.length out == 32 = SHA256Digest out
src/Dhall.hs view
@@ -46,7 +46,8 @@ , RecordType(..) , UnionType(..) , InputType(..)- , Interpret(..)+ , FromDhall(..)+ , Interpret , InvalidType(..) , ExtractErrors(..) , Extractor@@ -71,19 +72,28 @@ , sequence , list , vector+ , setFromDistinctList+ , setIgnoringDuplicates+ , hashSetFromDistinctList+ , hashSetIgnoringDuplicates+ , Dhall.map+ , hashMap+ , pairFromMapEntry , unit+ , void , string , pair , record , field , union , constructor- , GenericInterpret(..)- , GenericInject(..)+ , GenericFromDhall(..)+ , GenericToDhall(..) - , Inject(..)+ , ToDhall(..)+ , Inject , inject- , genericInject+ , genericToDhall , RecordInputType(..) , inputFieldWith , inputField@@ -116,7 +126,10 @@ import Data.Fix (Fix(..)) import Data.Functor.Contravariant (Contravariant(..), (>$<), Op(..)) import Data.Functor.Contravariant.Divisible (Divisible(..), divided)+import Data.Hashable (Hashable) import Data.List.NonEmpty (NonEmpty (..))+import Data.HashMap.Strict (HashMap)+import Data.Map (Map) import Data.Monoid ((<>)) import Data.Scientific (Scientific) import Data.Semigroup (Semigroup)@@ -125,13 +138,14 @@ import Data.Text.Prettyprint.Doc (Pretty) import Data.Typeable (Typeable) import Data.Vector (Vector)+import Data.Void (Void) import Data.Word (Word8, Word16, Word32, Word64)-import Dhall.Core (Expr(..), Chunks(..))+import Dhall.Core (Expr(..), Chunks(..), DhallDouble(..)) import Dhall.Import (Imported(..)) import Dhall.Parser (Src(..))-import Dhall.TypeCheck (DetailedTypeError(..), TypeError, X)+import Dhall.TypeCheck (DetailedTypeError(..), TypeError) import GHC.Generics-import Lens.Family (LensLike', set, view)+import Lens.Family (LensLike', view) import Numeric.Natural (Natural) import Prelude hiding (maybe, sequence) import System.FilePath (takeDirectory)@@ -142,16 +156,21 @@ import qualified Data.Foldable import qualified Data.Functor.Compose import qualified Data.Functor.Product+import qualified Data.HashMap.Strict as HashMap+import qualified Data.Map import qualified Data.Maybe+import qualified Data.List import qualified Data.List.NonEmpty import qualified Data.Semigroup import qualified Data.Scientific import qualified Data.Sequence import qualified Data.Set+import qualified Data.HashSet import qualified Data.Text import qualified Data.Text.IO import qualified Data.Text.Lazy import qualified Data.Vector+import qualified Data.Void import qualified Dhall.Context import qualified Dhall.Core import qualified Dhall.Import@@ -160,18 +179,32 @@ import qualified Dhall.Pretty.Internal import qualified Dhall.TypeCheck import qualified Dhall.Util+import qualified Lens.Family -- $setup -- >>> :set -XOverloadedStrings -- >>> :set -XRecordWildCards+-- >>> import Data.Word (Word8, Word16, Word32, Word64)+-- >>> import Dhall.Pretty.Internal (prettyExpr) +{-| Useful synonym for the `Validation` type used when marshalling Dhall+ expressions+-} type Extractor s a = Validation (ExtractErrors s a)-type MonadicExtractor s a = Either (ExtractErrors s a) +{-| Useful synonym for the equivalent `Either` type used when marshalling Dhall+ code+-}+type MonadicExtractor s a = Either (ExtractErrors s a) +{-| Generate a type error during extraction by specifying the expected type+ and the actual type+-} typeError :: Expr s a -> Expr s a -> Extractor s a b-typeError expected actual = Failure . ExtractErrors . pure . TypeMismatch $ InvalidType expected actual+typeError expected actual =+ Failure . ExtractErrors . pure . TypeMismatch $ InvalidType expected actual +-- | Turn a `Text` message into an extraction failure extractError :: Text -> Extractor s a b extractError = Failure . ExtractErrors . pure . ExtractError @@ -185,6 +218,9 @@ fromMonadic :: MonadicExtractor s a b -> Extractor s a b fromMonadic = eitherToValidation +{-| One or more errors returned from extracting a Dhall expression to a+ Haskell expression+-} newtype ExtractErrors s a = ExtractErrors { getErrors :: NonEmpty (ExtractError s a) } deriving Semigroup@@ -298,8 +334,8 @@ -- | @since 1.16 data EvaluateSettings = EvaluateSettings- { _startingContext :: Dhall.Context.Context (Expr Src X)- , _normalizer :: Maybe (Dhall.Core.ReifiedNormalizer X)+ { _startingContext :: Dhall.Context.Context (Expr Src Void)+ , _normalizer :: Maybe (Dhall.Core.ReifiedNormalizer Void) } -- | Default evaluation settings: no extra entries in the initial@@ -317,11 +353,11 @@ -- @since 1.16 startingContext :: (Functor f, HasEvaluateSettings s)- => LensLike' f s (Dhall.Context.Context (Expr Src X))+ => LensLike' f s (Dhall.Context.Context (Expr Src Void)) startingContext = evaluateSettings . l where l :: (Functor f)- => LensLike' f EvaluateSettings (Dhall.Context.Context (Expr Src X))+ => LensLike' f EvaluateSettings (Dhall.Context.Context (Expr Src Void)) l k s = fmap (\x -> s { _startingContext = x}) (k (_startingContext s)) -- | Access the custom normalizer.@@ -329,11 +365,11 @@ -- @since 1.16 normalizer :: (Functor f, HasEvaluateSettings s)- => LensLike' f s (Maybe (Dhall.Core.ReifiedNormalizer X))+ => LensLike' f s (Maybe (Dhall.Core.ReifiedNormalizer Void)) normalizer = evaluateSettings . l where l :: (Functor f)- => LensLike' f EvaluateSettings (Maybe (Dhall.Core.ReifiedNormalizer X))+ => LensLike' f EvaluateSettings (Maybe (Dhall.Core.ReifiedNormalizer Void)) l k s = fmap (\x -> s { _normalizer = x }) (k (_normalizer s)) -- | @since 1.16@@ -398,8 +434,8 @@ let EvaluateSettings {..} = _evaluateSettings let transform =- set Dhall.Import.normalizer _normalizer- . set Dhall.Import.startingContext _startingContext+ Lens.Family.set Dhall.Import.normalizer _normalizer+ . Lens.Family.set Dhall.Import.startingContext _startingContext let status = transform (Dhall.Import.emptyStatus _rootDirectory) @@ -467,7 +503,7 @@ inputExpr :: Text -- ^ The Dhall program- -> IO (Expr Src X)+ -> IO (Expr Src Void) -- ^ The fully normalized AST inputExpr = inputExprWithSettings defaultInputSettings@@ -482,7 +518,7 @@ :: InputSettings -> Text -- ^ The Dhall program- -> IO (Expr Src X)+ -> IO (Expr Src Void) -- ^ The fully normalized AST inputExprWithSettings settings txt = do expr <- Dhall.Core.throws (Dhall.Parser.exprFromText (view sourceName settings) txt)@@ -492,8 +528,8 @@ let EvaluateSettings {..} = _evaluateSettings let transform =- set Dhall.Import.normalizer _normalizer- . set Dhall.Import.startingContext _startingContext+ Lens.Family.set Dhall.Import.normalizer _normalizer+ . Lens.Family.set Dhall.Import.startingContext _startingContext let status = transform (Dhall.Import.emptyStatus _rootDirectory) @@ -512,7 +548,7 @@ :: Alternative f => Type a -- ^ The type of value to decode from Dhall to Haskell- -> Expr s X+ -> Expr s Void -- ^ a closed form Dhall program, which evaluates to the expected type -> f a -- ^ The decoded value in Haskell@@ -629,11 +665,11 @@ detailed = Control.Exception.handle handler1 . Control.Exception.handle handler0 where- handler0 :: Imported (TypeError Src X) -> IO a+ handler0 :: Imported (TypeError Src Void) -> IO a handler0 (Imported ps e) = Control.Exception.throwIO (Imported ps (DetailedTypeError e)) - handler1 :: TypeError Src X -> IO a+ handler1 :: TypeError Src Void -> IO a handler1 e = Control.Exception.throwIO (DetailedTypeError e) {-| A @(Type a)@ represents a way to marshal a value of type @\'a\'@ from Dhall@@ -654,9 +690,9 @@ > input :: Type a -> Text -> IO a -} data Type a = Type- { extract :: Expr Src X -> Extractor Src X a+ { extract :: Expr Src Void -> Extractor Src Void a -- ^ Extracts Haskell value from the Dhall expression- , expected :: Expr Src X+ , expected :: Expr Src Void -- ^ Dhall type of the Haskell value } deriving (Functor)@@ -716,8 +752,8 @@ double :: Type Double double = Type {..} where- extract (DoubleLit n) = pure n- extract expr = typeError Double expr+ extract (DoubleLit (DhallDouble n)) = pure n+ extract expr = typeError Double expr expected = Double @@ -727,12 +763,7 @@ "Test" -} lazyText :: Type Data.Text.Lazy.Text-lazyText = Type {..}- where- extract (TextLit (Chunks [] t)) = pure (Data.Text.Lazy.fromStrict t)- extract expr = typeError Text expr-- expected = Text+lazyText = fmap Data.Text.Lazy.fromStrict strictText {-| Decode strict `Text` @@ -740,8 +771,12 @@ "Test" -} strictText :: Type Text-strictText = fmap Data.Text.Lazy.toStrict lazyText+strictText = Type {..}+ where+ extract (TextLit (Chunks [] t)) = pure t+ extract expr = typeError Text expr + expected = Text {-| Decode a `Maybe` >>> input (maybe natural) "Some 1"@@ -785,6 +820,176 @@ vector :: Type a -> Type (Vector a) vector = fmap Data.Vector.fromList . list +{-| Decode a `Set` from a `List`++>>> input (setIgnoringDuplicates natural) "[1, 2, 3]"+fromList [1,2,3]++Duplicate elements are ignored.++>>> input (setIgnoringDuplicates natural) "[1, 1, 3]"+fromList [1,3]++-}+setIgnoringDuplicates :: (Ord a) => Type a -> Type (Data.Set.Set a)+setIgnoringDuplicates = fmap Data.Set.fromList . list++{-| Decode a `HashSet` from a `List`++>>> input (hashSetIgnoringDuplicates natural) "[1, 2, 3]"+fromList [1,2,3]++Duplicate elements are ignored.++>>> input (hashSetIgnoringDuplicates natural) "[1, 1, 3]"+fromList [1,3]++-}+hashSetIgnoringDuplicates :: (Hashable a, Ord a)+ => Type a+ -> Type (Data.HashSet.HashSet a)+hashSetIgnoringDuplicates = fmap Data.HashSet.fromList . list++{-| Decode a `Set` from a `List` with distinct elements++>>> input (setFromDistinctList natural) "[1, 2, 3]"+fromList [1,2,3]++An error is thrown if the list contains duplicates.++> >>> input (setFromDistinctList natural) "[1, 1, 3]"+> *** Exception: Error: Failed extraction+>+> The expression type-checked successfully but the transformation to the target+> type failed with the following error:+>+> One duplicate element in the list: 1+>++> >>> input (setFromDistinctList natural) "[1, 1, 3, 3]"+> *** Exception: Error: Failed extraction+>+> The expression type-checked successfully but the transformation to the target+> type failed with the following error:+>+> 2 duplicates were found in the list, including 1+>++-}+setFromDistinctList :: (Ord a, Show a) => Type a -> Type (Data.Set.Set a)+setFromDistinctList = setHelper Data.Set.size Data.Set.fromList++{-| Decode a `HashSet` from a `List` with distinct elements++>>> input (hashSetFromDistinctList natural) "[1, 2, 3]"+fromList [1,2,3]++An error is thrown if the list contains duplicates.++> >>> input (hashSetFromDistinctList natural) "[1, 1, 3]"+> *** Exception: Error: Failed extraction+>+> The expression type-checked successfully but the transformation to the target+> type failed with the following error:+>+> One duplicate element in the list: 1+>++> >>> input (hashSetFromDistinctList natural) "[1, 1, 3, 3]"+> *** Exception: Error: Failed extraction+>+> The expression type-checked successfully but the transformation to the target+> type failed with the following error:+>+> 2 duplicates were found in the list, including 1+>++-}+hashSetFromDistinctList :: (Hashable a, Ord a, Show a)+ => Type a+ -> Type (Data.HashSet.HashSet a)+hashSetFromDistinctList = setHelper Data.HashSet.size Data.HashSet.fromList+++setHelper :: (Eq a, Foldable t, Show a)+ => (t a -> Int)+ -> ([a] -> t a)+ -> Type a+ -> Type (t a)+setHelper size toSet (Type extractIn expectedIn) = Type extractOut expectedOut+ where+ extractOut (ListLit _ es) = case traverse extractIn es of+ Success vSeq+ | sameSize -> Success vSet+ | otherwise -> extractError err+ where+ vList = Data.Foldable.toList vSeq+ vSet = toSet vList+ sameSize = size vSet == Data.Sequence.length vSeq+ duplicates = vList Data.List.\\ Data.Foldable.toList vSet+ err | length duplicates == 1 =+ "One duplicate element in the list: "+ <> (Data.Text.pack $ show $ head duplicates)+ | otherwise = Data.Text.pack $ unwords+ [ show $ length duplicates+ , "duplicates were found in the list, including"+ , show $ head duplicates+ ]+ Failure f -> Failure f+ extractOut expr = typeError expectedOut expr++ expectedOut = App List expectedIn++{-| Decode a `Map` from a @toMap@ expression or generally a @Prelude.Map.Type@++>>> input (Dhall.map strictText bool) "toMap { a = True, b = False }"+fromList [("a",True),("b",False)]+>>> input (Dhall.map strictText bool) "[ { mapKey = \"foo\", mapValue = True } ]"+fromList [("foo",True)]++If there are duplicate @mapKey@s, later @mapValue@s take precedence:++>>> let expr = "[ { mapKey = 1, mapValue = True }, { mapKey = 1, mapValue = False } ]"+>>> input (Dhall.map natural bool) expr+fromList [(1,False)]++-}+map :: Ord k => Type k -> Type v -> Type (Map k v)+map k v = fmap Data.Map.fromList (list (pairFromMapEntry k v))++{-| Decode a `HashMap` from a @toMap@ expression or generally a @Prelude.Map.Type@++>>> input (Dhall.hashMap strictText bool) "toMap { a = True, b = False }"+fromList [("a",True),("b",False)]+>>> input (Dhall.hashMap strictText bool) "[ { mapKey = \"foo\", mapValue = True } ]"+fromList [("foo",True)]++If there are duplicate @mapKey@s, later @mapValue@s take precedence:++>>> let expr = "[ { mapKey = 1, mapValue = True }, { mapKey = 1, mapValue = False } ]"+>>> input (Dhall.hashMap natural bool) expr+fromList [(1,False)]++-}+hashMap :: (Eq k, Hashable k) => Type k -> Type v -> Type (HashMap k v)+hashMap k v = fmap HashMap.fromList (list (pairFromMapEntry k v))++{-| Decode a tuple from a @Prelude.Map.Entry@ record++>>> input (pairFromMapEntry strictText natural) "{ mapKey = \"foo\", mapValue = 3 }"+("foo",3)+-}+pairFromMapEntry :: Type k -> Type v -> Type (k, v)+pairFromMapEntry k v = Type extractOut expectedOut+ where+ extractOut (RecordLit kvs)+ | Just key <- Dhall.Map.lookup "mapKey" kvs+ , Just value <- Dhall.Map.lookup "mapValue" kvs+ = liftA2 (,) (extract k key) (extract v value)+ extractOut expr = typeError expectedOut expr++ expectedOut = Record (Dhall.Map.fromList [("mapKey", expected k), ("mapValue", expected v)])+ {-| Decode @()@ from an empty record. >>> input unit "{=}" -- GHC doesn't print the result if it is ()@@ -799,6 +1004,13 @@ expectedOut = Record mempty +{-| Decode 'Void' from an empty union.++Since @<>@ is uninhabited, @'input' 'void'@ will always fail.+-}+void :: Type Void+void = union mempty+ {-| Decode a `String` >>> input string "\"ABC\""@@ -829,59 +1041,91 @@ ] ) -{-| Any value that implements `Interpret` can be automatically decoded based on+{-| Any value that implements `FromDhall` can be automatically decoded based on the inferred return type of `input` >>> input auto "[1, 2, 3]" :: IO (Vector Natural) [1,2,3]+>>> input auto "toMap { a = False, b = True }" :: IO (Map Text Bool)+fromList [("a",False),("b",True)] This class auto-generates a default implementation for records that implement `Generic`. This does not auto-generate an instance for recursive types. -}-class Interpret a where+class FromDhall a where autoWith:: InterpretOptions -> Type a default autoWith- :: (Generic a, GenericInterpret (Rep a)) => InterpretOptions -> Type a+ :: (Generic a, GenericFromDhall (Rep a)) => InterpretOptions -> Type a autoWith options = fmap GHC.Generics.to (evalState (genericAutoWith options) 1) -instance Interpret Bool where+{-| A compatibility alias for `FromDhall`++This will eventually be removed.+-}+type Interpret = FromDhall++instance FromDhall Void where+ autoWith _ = void++instance FromDhall () where+ autoWith _ = unit++instance FromDhall Bool where autoWith _ = bool -instance Interpret Natural where+instance FromDhall Natural where autoWith _ = natural -instance Interpret Integer where+instance FromDhall Integer where autoWith _ = integer -instance Interpret Scientific where+instance FromDhall Scientific where autoWith _ = scientific -instance Interpret Double where+instance FromDhall Double where autoWith _ = double -instance {-# OVERLAPS #-} Interpret [Char] where+instance {-# OVERLAPS #-} FromDhall [Char] where autoWith _ = string -instance Interpret Data.Text.Lazy.Text where+instance FromDhall Data.Text.Lazy.Text where autoWith _ = lazyText -instance Interpret Text where+instance FromDhall Text where autoWith _ = strictText -instance Interpret a => Interpret (Maybe a) where+instance FromDhall a => FromDhall (Maybe a) where autoWith opts = maybe (autoWith opts) -instance Interpret a => Interpret (Seq a) where+instance FromDhall a => FromDhall (Seq a) where autoWith opts = sequence (autoWith opts) -instance Interpret a => Interpret [a] where- autoWith = fmap (fmap Data.Vector.toList) autoWith+instance FromDhall a => FromDhall [a] where+ autoWith opts = list (autoWith opts) -instance Interpret a => Interpret (Vector a) where+instance FromDhall a => FromDhall (Vector a) where autoWith opts = vector (autoWith opts) -instance (Inject a, Interpret b) => Interpret (a -> b) where+{-| Note that this instance will throw errors in the presence of duplicates in+ the list. To ignore duplicates, use `setIgnoringDuplicates`.+-}+instance (FromDhall a, Ord a, Show a) => FromDhall (Data.Set.Set a) where+ autoWith opts = setFromDistinctList (autoWith opts)++{-| Note that this instance will throw errors in the presence of duplicates in+ the list. To ignore duplicates, use `hashSetIgnoringDuplicates`.+-}+instance (FromDhall a, Hashable a, Ord a, Show a) => FromDhall (Data.HashSet.HashSet a) where+ autoWith opts = hashSetFromDistinctList (autoWith opts)++instance (Ord k, FromDhall k, FromDhall v) => FromDhall (Map k v) where+ autoWith opts = Dhall.map (autoWith opts) (autoWith opts)++instance (Eq k, Hashable k, FromDhall k, FromDhall v) => FromDhall (HashMap k v) where+ autoWith opts = Dhall.hashMap (autoWith opts) (autoWith opts)++instance (ToDhall a, FromDhall b) => FromDhall (a -> b) where autoWith opts = Type extractOut expectedOut where normalizer_ = Just (inputNormalizer opts)@@ -889,7 +1133,7 @@ -- ToDo extractOut e = pure (\i -> case extractIn (Dhall.Core.normalizeWith normalizer_ (App e (embed i))) of Success o -> o- Failure _e -> error "Interpret: You cannot decode a function if it does not have the correct type" )+ Failure _e -> error "FromDhall: You cannot decode a function if it does not have the correct type" ) expectedOut = Pi "_" declared expectedIn @@ -897,25 +1141,25 @@ Type extractIn expectedIn = autoWith opts -instance (Interpret a, Interpret b) => Interpret (a, b)+instance (FromDhall a, FromDhall b) => FromDhall (a, b) {-| Use the default options for interpreting a configuration file > auto = autoWith defaultInterpretOptions -}-auto :: Interpret a => Type a+auto :: FromDhall a => Type a auto = autoWith defaultInterpretOptions {-| This type is exactly the same as `Data.Fix.Fix` except with a different- `Interpret` instance. This intermediate type simplies the implementation- of the inner loop for the `Interpret` instance for `Fix`+ `FromDhall` instance. This intermediate type simplies the implementation+ of the inner loop for the `FromDhall` instance for `Fix` -} newtype Result f = Result { _unResult :: f (Result f) } resultToFix :: Functor f => Result f -> Fix f resultToFix (Result x) = Fix (fmap resultToFix x) -instance Interpret (f (Result f)) => Interpret (Result f) where+instance FromDhall (f (Result f)) => FromDhall (Result f) where autoWith options = Type { expected = expected_, extract = extract_ } where expected_ = "result"@@ -939,30 +1183,30 @@ -- > {-# LANGUAGE StandaloneDeriving #-} -- > {-# LANGUAGE TypeFamilies #-} -- > {-# LANGUAGE TemplateHaskell #-}--- > +-- > -- > import Data.Fix (Fix(..)) -- > import Data.Text (Text)--- > import Dhall (Interpret)+-- > import Dhall (FromDhall) -- > import GHC.Generics (Generic) -- > import Numeric.Natural (Natural)--- > +-- > -- > import qualified Data.Fix as Fix -- > import qualified Data.Functor.Foldable as Foldable -- > import qualified Data.Functor.Foldable.TH as TH -- > import qualified Dhall -- > import qualified NeatInterpolation--- > +-- > -- > data Expr -- > = Lit Natural -- > | Add Expr Expr -- > | Mul Expr Expr -- > deriving (Show)--- > +-- > -- > TH.makeBaseFunctor ''Expr--- > +-- > -- > deriving instance Generic (ExprF a)--- > deriving instance Interpret a => Interpret (ExprF a)--- > +-- > deriving instance FromDhall a => FromDhall (ExprF a)+-- > -- > example :: Text -- > example = [NeatInterpolation.text| -- > \(Expr : Type)@@ -974,32 +1218,32 @@ -- > | MulF : -- > { _1 : Expr, _2 : Expr } -- > >--- > +-- > -- > in \(Fix : ExprF -> Expr) -- > -> let Lit = \(x : Natural) -> Fix (ExprF.LitF { _1 = x })--- > +-- > -- > let Add = -- > \(x : Expr) -- > -> \(y : Expr) -- > -> Fix (ExprF.AddF { _1 = x, _2 = y })--- > +-- > -- > let Mul = -- > \(x : Expr) -- > -> \(y : Expr) -- > -> Fix (ExprF.MulF { _1 = x, _2 = y })--- > +-- > -- > in Add (Mul (Lit 3) (Lit 7)) (Add (Lit 1) (Lit 2)) -- > |]--- > +-- > -- > convert :: Fix ExprF -> Expr -- > convert = Fix.cata Foldable.embed--- > +-- > -- > main :: IO () -- > main = do -- > x <- Dhall.input Dhall.auto example :: IO (Fix ExprF)--- > +-- > -- > print (convert x :: Expr)-instance (Functor f, Interpret (f (Result f))) => Interpret (Fix f) where+instance (Functor f, FromDhall (f (Result f))) => FromDhall (Fix f) where autoWith options = Type { expected = expected_, extract = extract_ } where expected_ =@@ -1015,15 +1259,15 @@ go0 _ = typeError expected_ expression0 {-| `genericAuto` is the default implementation for `auto` if you derive- `Interpret`. The difference is that you can use `genericAuto` without- having to explicitly provide an `Interpret` instance for a type as long as+ `FromDhall`. The difference is that you can use `genericAuto` without+ having to explicitly provide a `FromDhall` instance for a type as long as the type derives `Generic` -}-genericAuto :: (Generic a, GenericInterpret (Rep a)) => Type a+genericAuto :: (Generic a, GenericFromDhall (Rep a)) => Type a genericAuto = fmap to (evalState (genericAutoWith defaultInterpretOptions) 1) {-| Use these options to tweak how Dhall derives a generic implementation of- `Interpret`+ `FromDhall` -} data InterpretOptions = InterpretOptions { fieldModifier :: Text -> Text@@ -1036,8 +1280,8 @@ -- ^ Specify how to handle constructors with only one field. The default is -- `Wrapped` for backwards compatibility but will eventually be changed to -- `Smart`- , inputNormalizer :: Dhall.Core.ReifiedNormalizer X- -- ^ This is only used by the `Interpret` instance for functions in order+ , inputNormalizer :: Dhall.Core.ReifiedNormalizer Void+ -- ^ This is only used by the `FromDhall` instance for functions in order -- to normalize the function input before marshaling the input into a -- Dhall expression }@@ -1082,18 +1326,18 @@ Dhall.Core.ReifiedNormalizer (const (pure Nothing)) } -{-| This is the underlying class that powers the `Interpret` class's support+{-| This is the underlying class that powers the `FromDhall` class's support for automatically deriving a generic implementation -}-class GenericInterpret f where+class GenericFromDhall f where genericAutoWith :: InterpretOptions -> State Int (Type (f a)) -instance GenericInterpret f => GenericInterpret (M1 D d f) where+instance GenericFromDhall f => GenericFromDhall (M1 D d f) where genericAutoWith options = do res <- genericAutoWith options pure (fmap M1 res) -instance GenericInterpret V1 where+instance GenericFromDhall V1 where genericAutoWith _ = pure Type {..} where extract expr = typeError expected expr@@ -1101,14 +1345,15 @@ expected = Union mempty unsafeExpectUnion- :: Text -> Expr Src X -> Dhall.Map.Map Text (Maybe (Expr Src X))+ :: Text -> Expr Src Void -> Dhall.Map.Map Text (Maybe (Expr Src Void)) unsafeExpectUnion _ (Union kts) = kts unsafeExpectUnion name expression = Dhall.Core.internalError (name <> ": Unexpected constructor: " <> Dhall.Core.pretty expression) -unsafeExpectRecord :: Text -> Expr Src X -> Dhall.Map.Map Text (Expr Src X)+unsafeExpectRecord+ :: Text -> Expr Src Void -> Dhall.Map.Map Text (Expr Src Void) unsafeExpectRecord _ (Record kts) = kts unsafeExpectRecord name expression =@@ -1117,8 +1362,8 @@ unsafeExpectUnionLit :: Text- -> Expr Src X- -> (Text, Maybe (Expr Src X))+ -> Expr Src Void+ -> (Text, Maybe (Expr Src Void)) unsafeExpectUnionLit _ (Field (Union _) k) = (k, Nothing) unsafeExpectUnionLit _ (App (Field (Union _) k) v) =@@ -1127,7 +1372,8 @@ Dhall.Core.internalError (name <> ": Unexpected constructor: " <> Dhall.Core.pretty expression) -unsafeExpectRecordLit :: Text -> Expr Src X -> Dhall.Map.Map Text (Expr Src X)+unsafeExpectRecordLit+ :: Text -> Expr Src Void -> Dhall.Map.Map Text (Expr Src Void) unsafeExpectRecordLit _ (RecordLit kvs) = kvs unsafeExpectRecordLit name expression =@@ -1152,7 +1398,7 @@ extractUnionConstructor _ = empty -instance (Constructor c1, Constructor c2, GenericInterpret f1, GenericInterpret f2) => GenericInterpret (M1 C c1 f1 :+: M1 C c2 f2) where+instance (Constructor c1, Constructor c2, GenericFromDhall f1, GenericFromDhall f2) => GenericFromDhall (M1 C c1 f1 :+: M1 C c2 f2) where genericAutoWith options@(InterpretOptions {..}) = pure (Type {..}) where nL :: M1 i c1 f1 a@@ -1184,7 +1430,7 @@ Type extractL expectedL = evalState (genericAutoWith options) 1 Type extractR expectedR = evalState (genericAutoWith options) 1 -instance (Constructor c, GenericInterpret (f :+: g), GenericInterpret h) => GenericInterpret ((f :+: g) :+: M1 C c h) where+instance (Constructor c, GenericFromDhall (f :+: g), GenericFromDhall h) => GenericFromDhall ((f :+: g) :+: M1 C c h) where genericAutoWith options@(InterpretOptions {..}) = pure (Type {..}) where n :: M1 i c h a@@ -1207,7 +1453,7 @@ ktsL = unsafeExpectUnion "genericAutoWith (:+:)" expectedL -instance (Constructor c, GenericInterpret f, GenericInterpret (g :+: h)) => GenericInterpret (M1 C c f :+: (g :+: h)) where+instance (Constructor c, GenericFromDhall f, GenericFromDhall (g :+: h)) => GenericFromDhall (M1 C c f :+: (g :+: h)) where genericAutoWith options@(InterpretOptions {..}) = pure (Type {..}) where n :: M1 i c f a@@ -1230,7 +1476,7 @@ ktsR = unsafeExpectUnion "genericAutoWith (:+:)" expectedR -instance (GenericInterpret (f :+: g), GenericInterpret (h :+: i)) => GenericInterpret ((f :+: g) :+: (h :+: i)) where+instance (GenericFromDhall (f :+: g), GenericFromDhall (h :+: i)) => GenericFromDhall ((f :+: g) :+: (h :+: i)) where genericAutoWith options = pure (Type {..}) where extract e = fmap L1 (extractL e) `ealt` fmap R1 (extractR e)@@ -1243,12 +1489,12 @@ ktsL = unsafeExpectUnion "genericAutoWith (:+:)" expectedL ktsR = unsafeExpectUnion "genericAutoWith (:+:)" expectedR -instance GenericInterpret f => GenericInterpret (M1 C c f) where+instance GenericFromDhall f => GenericFromDhall (M1 C c f) where genericAutoWith options = do res <- genericAutoWith options pure (fmap M1 res) -instance GenericInterpret U1 where+instance GenericFromDhall U1 where genericAutoWith _ = pure (Type {..}) where extract _ = pure U1@@ -1262,7 +1508,7 @@ pure (Data.Text.pack ("_" ++ show i)) nn -> pure (Data.Text.pack nn) -instance (GenericInterpret (f :*: g), GenericInterpret (h :*: i)) => GenericInterpret ((f :*: g) :*: (h :*: i)) where+instance (GenericFromDhall (f :*: g), GenericFromDhall (h :*: i)) => GenericFromDhall ((f :*: g) :*: (h :*: i)) where genericAutoWith options = do Type extractL expectedL <- genericAutoWith options Type extractR expectedR <- genericAutoWith options@@ -1277,7 +1523,7 @@ return (Type {..}) -instance (GenericInterpret (f :*: g), Selector s, Interpret a) => GenericInterpret ((f :*: g) :*: M1 S s (K1 i a)) where+instance (GenericFromDhall (f :*: g), Selector s, FromDhall a) => GenericFromDhall ((f :*: g) :*: M1 S s (K1 i a)) where genericAutoWith options@InterpretOptions{..} = do let nR :: M1 S s (K1 i a) r nR = undefined@@ -1307,7 +1553,7 @@ return (Type {..}) -instance (Selector s, Interpret a, GenericInterpret (f :*: g)) => GenericInterpret (M1 S s (K1 i a) :*: (f :*: g)) where+instance (Selector s, FromDhall a, GenericFromDhall (f :*: g)) => GenericFromDhall (M1 S s (K1 i a) :*: (f :*: g)) where genericAutoWith options@InterpretOptions{..} = do let nL :: M1 S s (K1 i a) r nL = undefined@@ -1337,7 +1583,7 @@ return (Type {..}) -instance (Selector s1, Selector s2, Interpret a1, Interpret a2) => GenericInterpret (M1 S s1 (K1 i1 a1) :*: M1 S s2 (K1 i2 a2)) where+instance (Selector s1, Selector s2, FromDhall a1, FromDhall a2) => GenericFromDhall (M1 S s1 (K1 i1 a1) :*: M1 S s2 (K1 i2 a2)) where genericAutoWith options@InterpretOptions{..} = do let nL :: M1 S s1 (K1 i1 a1) r nL = undefined@@ -1374,7 +1620,7 @@ return (Type {..}) -instance (Selector s, Interpret a) => GenericInterpret (M1 S s (K1 i a)) where+instance (Selector s, FromDhall a) => GenericFromDhall (M1 S s (K1 i a)) where genericAutoWith options@InterpretOptions{..} = do let n :: M1 S s (K1 i a) r n = undefined@@ -1420,9 +1666,9 @@ Haskell into Dhall -} data InputType a = InputType- { embed :: a -> Expr Src X+ { embed :: a -> Expr Src Void -- ^ Embeds a Haskell value as a Dhall expression- , declared :: Expr Src X+ , declared :: Expr Src Void -- ^ Dhall type of the Haskell value } @@ -1431,53 +1677,66 @@ where embed' x = embed (f x) -{-| This class is used by `Interpret` instance for functions:+{-| This class is used by `FromDhall` instance for functions: -> instance (Inject a, Interpret b) => Interpret (a -> b)+> instance (ToDhall a, FromDhall b) => FromDhall (a -> b) You can convert Dhall functions with "simple" inputs (i.e. instances of this class) into Haskell functions. This works by: * Marshaling the input to the Haskell function into a Dhall expression (i.e.- @x :: Expr Src X@)- * Applying the Dhall function (i.e. @f :: Expr Src X@) to the Dhall input+ @x :: Expr Src Void@)+ * Applying the Dhall function (i.e. @f :: Expr Src Void@) to the Dhall input (i.e. @App f x@) * Normalizing the syntax tree (i.e. @normalize (App f x)@) * Marshaling the resulting Dhall expression back into a Haskell value -}-class Inject a where+class ToDhall a where injectWith :: InterpretOptions -> InputType a default injectWith- :: (Generic a, GenericInject (Rep a)) => InterpretOptions -> InputType a+ :: (Generic a, GenericToDhall (Rep a)) => InterpretOptions -> InputType a injectWith options- = contramap GHC.Generics.from (evalState (genericInjectWith options) 1)+ = contramap GHC.Generics.from (evalState (genericToDhallWith options) 1) +{-| A compatibility alias for `ToDhall`++This will eventually be removed.+-}+type Inject = ToDhall+ {-| Use the default options for injecting a value -> inject = inject defaultInterpretOptions+> inject = injectWith defaultInterpretOptions -}-inject :: Inject a => InputType a+inject :: ToDhall a => InputType a inject = injectWith defaultInterpretOptions {-| Use the default options for injecting a value, whose structure is determined generically. -This can be used when you want to use 'Inject' on types that you don't+This can be used when you want to use 'ToDhall' on types that you don't want to define orphan instances for. -}-genericInject- :: (Generic a, GenericInject (Rep a)) => InputType a-genericInject- = contramap GHC.Generics.from (evalState (genericInjectWith defaultInterpretOptions) 1)+genericToDhall+ :: (Generic a, GenericToDhall (Rep a)) => InputType a+genericToDhall+ = contramap GHC.Generics.from (evalState (genericToDhallWith defaultInterpretOptions) 1) -instance Inject Bool where+instance ToDhall Void where injectWith _ = InputType {..} where+ embed = Data.Void.absurd++ declared = Union mempty++instance ToDhall Bool where+ injectWith _ = InputType {..}+ where embed = BoolLit declared = Bool -instance Inject Data.Text.Lazy.Text where+instance ToDhall Data.Text.Lazy.Text where injectWith _ = InputType {..} where embed text =@@ -1485,85 +1744,122 @@ declared = Text -instance Inject Text where+instance ToDhall Text where injectWith _ = InputType {..} where embed text = TextLit (Chunks [] text) declared = Text -instance {-# OVERLAPS #-} Inject String where+instance {-# OVERLAPS #-} ToDhall String where injectWith options = contramap Data.Text.pack (injectWith options :: InputType Text) -instance Inject Natural where+instance ToDhall Natural where injectWith _ = InputType {..} where embed = NaturalLit declared = Natural -instance Inject Integer where+instance ToDhall Integer where injectWith _ = InputType {..} where embed = IntegerLit declared = Integer -instance Inject Int where+instance ToDhall Int where injectWith _ = InputType {..} where embed = IntegerLit . toInteger declared = Integer -instance Inject Word8 where+{-|++>>> embed inject (12 :: Word)+NaturalLit 12+-}++instance ToDhall Word where injectWith _ = InputType {..} where- embed = IntegerLit . toInteger+ embed = NaturalLit . fromIntegral - declared = Integer+ declared = Natural -instance Inject Word16 where+{-|++>>> embed inject (12 :: Word8)+NaturalLit 12+-}++instance ToDhall Word8 where injectWith _ = InputType {..} where- embed = IntegerLit . toInteger+ embed = NaturalLit . fromIntegral - declared = Integer+ declared = Natural -instance Inject Word32 where+{-|++>>> embed inject (12 :: Word16)+NaturalLit 12+-}++instance ToDhall Word16 where injectWith _ = InputType {..} where- embed = IntegerLit . toInteger+ embed = NaturalLit . fromIntegral - declared = Integer+ declared = Natural -instance Inject Word64 where+{-|++>>> embed inject (12 :: Word32)+NaturalLit 12+-}++instance ToDhall Word32 where injectWith _ = InputType {..} where- embed = IntegerLit . toInteger+ embed = NaturalLit . fromIntegral - declared = Integer+ declared = Natural -instance Inject Double where+{-|++>>> embed inject (12 :: Word64)+NaturalLit 12+-}++instance ToDhall Word64 where injectWith _ = InputType {..} where- embed = DoubleLit+ embed = NaturalLit . fromIntegral + declared = Natural++instance ToDhall Double where+ injectWith _ = InputType {..}+ where+ embed = DoubleLit . DhallDouble+ declared = Double -instance Inject Scientific where+instance ToDhall Scientific where injectWith options = contramap Data.Scientific.toRealFloat (injectWith options :: InputType Double) -instance Inject () where+instance ToDhall () where injectWith _ = InputType {..} where embed = const (RecordLit mempty) declared = Record mempty -instance Inject a => Inject (Maybe a) where+instance ToDhall a => ToDhall (Maybe a) where injectWith options = InputType embedOut declaredOut where embedOut (Just x ) = Some (embedIn x)@@ -1573,7 +1869,7 @@ declaredOut = App Optional declaredIn -instance Inject a => Inject (Seq a) where+instance ToDhall a => ToDhall (Seq a) where injectWith options = InputType embedOut declaredOut where embedOut xs = ListLit listType (fmap embedIn xs)@@ -1586,35 +1882,108 @@ InputType embedIn declaredIn = injectWith options -instance Inject a => Inject [a] where+instance ToDhall a => ToDhall [a] where injectWith = fmap (contramap Data.Sequence.fromList) injectWith -instance Inject a => Inject (Vector a) where+instance ToDhall a => ToDhall (Vector a) where injectWith = fmap (contramap Data.Vector.toList) injectWith -instance Inject a => Inject (Data.Set.Set a) where- injectWith = fmap (contramap Data.Set.toList) injectWith+{-| Note that the ouput list will be sorted -instance (Inject a, Inject b) => Inject (a, b)+>>> let x = Data.Set.fromList ["mom", "hi" :: Text]+>>> prettyExpr $ embed inject x+[ "hi", "mom" ] -{-| This is the underlying class that powers the `Interpret` class's support+-}+instance ToDhall a => ToDhall (Data.Set.Set a) where+ injectWith = fmap (contramap Data.Set.toAscList) injectWith++{-| Note that the ouput list may not be sorted++>>> let x = Data.HashSet.fromList ["hi", "mom" :: Text]+>>> prettyExpr $ embed inject x+[ "mom", "hi" ]++-}+instance ToDhall a => ToDhall (Data.HashSet.HashSet a) where+ injectWith = fmap (contramap Data.HashSet.toList) injectWith++instance (ToDhall a, ToDhall b) => ToDhall (a, b)++{-| Embed a `Data.Map` as a @Prelude.Map.Type@++>>> prettyExpr $ embed inject (Data.Map.fromList [(1 :: Natural, True)])+[ { mapKey = 1, mapValue = True } ]++>>> prettyExpr $ embed inject (Data.Map.fromList [] :: Data.Map.Map Natural Bool)+[] : List { mapKey : Natural, mapValue : Bool }++-}+instance (ToDhall k, ToDhall v) => ToDhall (Data.Map.Map k v) where+ injectWith options = InputType embedOut declaredOut+ where+ embedOut m = ListLit listType (mapEntries m)+ where+ listType+ | Data.Map.null m = Just declaredOut+ | otherwise = Nothing++ declaredOut = App List (Record (Dhall.Map.fromList+ [("mapKey", declaredK), ("mapValue", declaredV)]))++ mapEntries = Data.Sequence.fromList . fmap recordPair . Data.Map.toList+ recordPair (k, v) = RecordLit (Dhall.Map.fromList+ [("mapKey", embedK k), ("mapValue", embedV v)])++ InputType embedK declaredK = injectWith options+ InputType embedV declaredV = injectWith options++{-| Embed a `Data.HashMap` as a @Prelude.Map.Type@++>>> prettyExpr $ embed inject (HashMap.fromList [(1 :: Natural, True)])+[ { mapKey = 1, mapValue = True } ]++>>> prettyExpr $ embed inject (HashMap.fromList [] :: HashMap Natural Bool)+[] : List { mapKey : Natural, mapValue : Bool }++-}+instance (ToDhall k, ToDhall v) => ToDhall (HashMap k v) where+ injectWith options = InputType embedOut declaredOut+ where+ embedOut m = ListLit listType (mapEntries m)+ where+ listType+ | HashMap.null m = Just declaredOut+ | otherwise = Nothing++ declaredOut = App List (Record (Dhall.Map.fromList+ [("mapKey", declaredK), ("mapValue", declaredV)]))++ mapEntries = Data.Sequence.fromList . fmap recordPair . HashMap.toList+ recordPair (k, v) = RecordLit (Dhall.Map.fromList+ [("mapKey", embedK k), ("mapValue", embedV v)])++ InputType embedK declaredK = injectWith options+ InputType embedV declaredV = injectWith options++{-| This is the underlying class that powers the `FromDhall` class's support for automatically deriving a generic implementation -}-class GenericInject f where- genericInjectWith :: InterpretOptions -> State Int (InputType (f a))+class GenericToDhall f where+ genericToDhallWith :: InterpretOptions -> State Int (InputType (f a)) -instance GenericInject f => GenericInject (M1 D d f) where- genericInjectWith options = do- res <- genericInjectWith options+instance GenericToDhall f => GenericToDhall (M1 D d f) where+ genericToDhallWith options = do+ res <- genericToDhallWith options pure (contramap unM1 res) -instance GenericInject f => GenericInject (M1 C c f) where- genericInjectWith options = do- res <- genericInjectWith options+instance GenericToDhall f => GenericToDhall (M1 C c f) where+ genericToDhallWith options = do+ res <- genericToDhallWith options pure (contramap unM1 res) -instance (Selector s, Inject a) => GenericInject (M1 S s (K1 i a)) where- genericInjectWith options@InterpretOptions{..} = do+instance (Selector s, ToDhall a) => GenericToDhall (M1 S s (K1 i a)) where+ genericToDhallWith options@InterpretOptions{..} = do let InputType { embed = embed', declared = declared' } = injectWith options @@ -1645,8 +2014,8 @@ return (InputType {..}) -instance (Constructor c1, Constructor c2, GenericInject f1, GenericInject f2) => GenericInject (M1 C c1 f1 :+: M1 C c2 f2) where- genericInjectWith options@(InterpretOptions {..}) = pure (InputType {..})+instance (Constructor c1, Constructor c2, GenericToDhall f1, GenericToDhall f2) => GenericToDhall (M1 C c1 f1 :+: M1 C c2 f2) where+ genericToDhallWith options@(InterpretOptions {..}) = pure (InputType {..}) where embed (L1 (M1 l)) = case notEmptyRecordLit (embedL l) of@@ -1679,11 +2048,11 @@ keyL = constructorModifier (Data.Text.pack (conName nL)) keyR = constructorModifier (Data.Text.pack (conName nR)) - InputType embedL declaredL = evalState (genericInjectWith options) 1- InputType embedR declaredR = evalState (genericInjectWith options) 1+ InputType embedL declaredL = evalState (genericToDhallWith options) 1+ InputType embedR declaredR = evalState (genericToDhallWith options) 1 -instance (Constructor c, GenericInject (f :+: g), GenericInject h) => GenericInject ((f :+: g) :+: M1 C c h) where- genericInjectWith options@(InterpretOptions {..}) = pure (InputType {..})+instance (Constructor c, GenericToDhall (f :+: g), GenericToDhall h) => GenericToDhall ((f :+: g) :+: M1 C c h) where+ genericToDhallWith options@(InterpretOptions {..}) = pure (InputType {..}) where embed (L1 l) = case maybeValL of@@ -1691,7 +2060,7 @@ Just valL -> App (Field declared keyL) valL where (keyL, maybeValL) =- unsafeExpectUnionLit "genericInjectWith (:+:)" (embedL l)+ unsafeExpectUnionLit "genericToDhallWith (:+:)" (embedL l) embed (R1 (M1 r)) = case notEmptyRecordLit (embedR r) of Nothing -> Field declared keyR@@ -1704,13 +2073,13 @@ declared = Union (Dhall.Map.insert keyR (notEmptyRecord declaredR) ktsL) - InputType embedL declaredL = evalState (genericInjectWith options) 1- InputType embedR declaredR = evalState (genericInjectWith options) 1+ InputType embedL declaredL = evalState (genericToDhallWith options) 1+ InputType embedR declaredR = evalState (genericToDhallWith options) 1 - ktsL = unsafeExpectUnion "genericInjectWith (:+:)" declaredL+ ktsL = unsafeExpectUnion "genericToDhallWith (:+:)" declaredL -instance (Constructor c, GenericInject f, GenericInject (g :+: h)) => GenericInject (M1 C c f :+: (g :+: h)) where- genericInjectWith options@(InterpretOptions {..}) = pure (InputType {..})+instance (Constructor c, GenericToDhall f, GenericToDhall (g :+: h)) => GenericToDhall (M1 C c f :+: (g :+: h)) where+ genericToDhallWith options@(InterpretOptions {..}) = pure (InputType {..}) where embed (L1 (M1 l)) = case notEmptyRecordLit (embedL l) of@@ -1722,7 +2091,7 @@ Just valR -> App (Field declared keyR) valR where (keyR, maybeValR) =- unsafeExpectUnionLit "genericInjectWith (:+:)" (embedR r)+ unsafeExpectUnionLit "genericToDhallWith (:+:)" (embedR r) nL :: M1 i c f a nL = undefined@@ -1731,13 +2100,13 @@ declared = Union (Dhall.Map.insert keyL (notEmptyRecord declaredL) ktsR) - InputType embedL declaredL = evalState (genericInjectWith options) 1- InputType embedR declaredR = evalState (genericInjectWith options) 1+ InputType embedL declaredL = evalState (genericToDhallWith options) 1+ InputType embedR declaredR = evalState (genericToDhallWith options) 1 - ktsR = unsafeExpectUnion "genericInjectWith (:+:)" declaredR+ ktsR = unsafeExpectUnion "genericToDhallWith (:+:)" declaredR -instance (GenericInject (f :+: g), GenericInject (h :+: i)) => GenericInject ((f :+: g) :+: (h :+: i)) where- genericInjectWith options = pure (InputType {..})+instance (GenericToDhall (f :+: g), GenericToDhall (h :+: i)) => GenericToDhall ((f :+: g) :+: (h :+: i)) where+ genericToDhallWith options = pure (InputType {..}) where embed (L1 l) = case maybeValL of@@ -1745,52 +2114,52 @@ Just valL -> App (Field declared keyL) valL where (keyL, maybeValL) =- unsafeExpectUnionLit "genericInjectWith (:+:)" (embedL l)+ unsafeExpectUnionLit "genericToDhallWith (:+:)" (embedL l) embed (R1 r) = case maybeValR of Nothing -> Field declared keyR Just valR -> App (Field declared keyR) valR where (keyR, maybeValR) =- unsafeExpectUnionLit "genericInjectWith (:+:)" (embedR r)+ unsafeExpectUnionLit "genericToDhallWith (:+:)" (embedR r) declared = Union (Dhall.Map.union ktsL ktsR) - InputType embedL declaredL = evalState (genericInjectWith options) 1- InputType embedR declaredR = evalState (genericInjectWith options) 1+ InputType embedL declaredL = evalState (genericToDhallWith options) 1+ InputType embedR declaredR = evalState (genericToDhallWith options) 1 - ktsL = unsafeExpectUnion "genericInjectWith (:+:)" declaredL- ktsR = unsafeExpectUnion "genericInjectWith (:+:)" declaredR+ ktsL = unsafeExpectUnion "genericToDhallWith (:+:)" declaredL+ ktsR = unsafeExpectUnion "genericToDhallWith (:+:)" declaredR -instance (GenericInject (f :*: g), GenericInject (h :*: i)) => GenericInject ((f :*: g) :*: (h :*: i)) where- genericInjectWith options = do- InputType embedL declaredL <- genericInjectWith options- InputType embedR declaredR <- genericInjectWith options+instance (GenericToDhall (f :*: g), GenericToDhall (h :*: i)) => GenericToDhall ((f :*: g) :*: (h :*: i)) where+ genericToDhallWith options = do+ InputType embedL declaredL <- genericToDhallWith options+ InputType embedR declaredR <- genericToDhallWith options let embed (l :*: r) = RecordLit (Dhall.Map.union mapL mapR) where mapL =- unsafeExpectRecordLit "genericInjectWith (:*:)" (embedL l)+ unsafeExpectRecordLit "genericToDhallWith (:*:)" (embedL l) mapR =- unsafeExpectRecordLit "genericInjectWith (:*:)" (embedR r)+ unsafeExpectRecordLit "genericToDhallWith (:*:)" (embedR r) let declared = Record (Dhall.Map.union mapL mapR) where- mapL = unsafeExpectRecord "genericInjectWith (:*:)" declaredL- mapR = unsafeExpectRecord "genericInjectWith (:*:)" declaredR+ mapL = unsafeExpectRecord "genericToDhallWith (:*:)" declaredL+ mapR = unsafeExpectRecord "genericToDhallWith (:*:)" declaredR pure (InputType {..}) -instance (GenericInject (f :*: g), Selector s, Inject a) => GenericInject ((f :*: g) :*: M1 S s (K1 i a)) where- genericInjectWith options@InterpretOptions{..} = do+instance (GenericToDhall (f :*: g), Selector s, ToDhall a) => GenericToDhall ((f :*: g) :*: M1 S s (K1 i a)) where+ genericToDhallWith options@InterpretOptions{..} = do let nR :: M1 S s (K1 i a) r nR = undefined nameR <- fmap fieldModifier (getSelName nR) - InputType embedL declaredL <- genericInjectWith options+ InputType embedL declaredL <- genericToDhallWith options let InputType embedR declaredR = injectWith options @@ -1798,16 +2167,16 @@ RecordLit (Dhall.Map.insert nameR (embedR r) mapL) where mapL =- unsafeExpectRecordLit "genericInjectWith (:*:)" (embedL l)+ unsafeExpectRecordLit "genericToDhallWith (:*:)" (embedL l) let declared = Record (Dhall.Map.insert nameR declaredR mapL) where- mapL = unsafeExpectRecord "genericInjectWith (:*:)" declaredL+ mapL = unsafeExpectRecord "genericToDhallWith (:*:)" declaredL return (InputType {..}) -instance (Selector s, Inject a, GenericInject (f :*: g)) => GenericInject (M1 S s (K1 i a) :*: (f :*: g)) where- genericInjectWith options@InterpretOptions{..} = do+instance (Selector s, ToDhall a, GenericToDhall (f :*: g)) => GenericToDhall (M1 S s (K1 i a) :*: (f :*: g)) where+ genericToDhallWith options@InterpretOptions{..} = do let nL :: M1 S s (K1 i a) r nL = undefined @@ -1815,22 +2184,22 @@ let InputType embedL declaredL = injectWith options - InputType embedR declaredR <- genericInjectWith options+ InputType embedR declaredR <- genericToDhallWith options let embed (M1 (K1 l) :*: r) = RecordLit (Dhall.Map.insert nameL (embedL l) mapR) where mapR =- unsafeExpectRecordLit "genericInjectWith (:*:)" (embedR r)+ unsafeExpectRecordLit "genericToDhallWith (:*:)" (embedR r) let declared = Record (Dhall.Map.insert nameL declaredL mapR) where- mapR = unsafeExpectRecord "genericInjectWith (:*:)" declaredR+ mapR = unsafeExpectRecord "genericToDhallWith (:*:)" declaredR return (InputType {..}) -instance (Selector s1, Selector s2, Inject a1, Inject a2) => GenericInject (M1 S s1 (K1 i1 a1) :*: M1 S s2 (K1 i2 a2)) where- genericInjectWith options@InterpretOptions{..} = do+instance (Selector s1, Selector s2, ToDhall a1, ToDhall a2) => GenericToDhall (M1 S s1 (K1 i1 a1) :*: M1 S s2 (K1 i2 a2)) where+ genericToDhallWith options@InterpretOptions{..} = do let nL :: M1 S s1 (K1 i1 a1) r nL = undefined @@ -1857,8 +2226,8 @@ return (InputType {..}) -instance GenericInject U1 where- genericInjectWith _ = pure (InputType {..})+instance GenericToDhall U1 where+ genericToDhallWith _ = pure (InputType {..}) where embed _ = RecordLit mempty @@ -1909,12 +2278,12 @@ ( Control.Applicative.Const ( Dhall.Map.Map Text- ( Expr Src X )+ ( Expr Src Void ) ) ) ( Data.Functor.Compose.Compose- ( (->) ( Expr Src X ) )- (Extractor Src X)+ ( (->) ( Expr Src Void ) )+ (Extractor Src Void) ) a )@@ -2062,7 +2431,7 @@ adapt (Project{..}) = (projectName, (projectDescription, projectStars)) :} - Or, since we are simply using the `Inject` instance to inject each field, we could write+ Or, since we are simply using the `ToDhall` instance to inject each field, we could write >>> :{ injectProject :: InputType Project@@ -2084,6 +2453,7 @@ infixr 5 >*< +-- | Intermediate type used for building a `ToDhall` instance for a record newtype RecordInputType a = RecordInputType (Dhall.Map.Map Text (InputType a)) @@ -2098,12 +2468,19 @@ ((contramap $ snd . f) <$> cInputTypeRecord) conquer = RecordInputType mempty +{-| Specify how to encode one field of a record by supplying an explicit+ `InputType` for that field+-} inputFieldWith :: Text -> InputType a -> RecordInputType a inputFieldWith name inputType = RecordInputType $ Dhall.Map.singleton name inputType -inputField :: Inject a => Text -> RecordInputType a+{-| Specify how to encode one field of a record using the default `ToDhall`+ instance for that type+-}+inputField :: ToDhall a => Text -> RecordInputType a inputField name = inputFieldWith name inject +-- | Convert a `RecordInputType` into the equivalent `InputType` inputRecord :: RecordInputType a -> InputType a inputRecord (RecordInputType inputTypeRecord) = InputType makeRecordLit recordType where@@ -2146,7 +2523,7 @@ adapt (Errored e) = Right (Right e) :} - Or, since we are simply using the `Inject` instance to inject each branch, we could write+ Or, since we are simply using the `ToDhall` instance to inject each branch, we could write >>> :{ injectStatus :: InputType Status@@ -2168,10 +2545,10 @@ ( Control.Applicative.Const ( Dhall.Map.Map Text- ( Expr Src X )+ ( Expr Src Void ) ) )- ( Op (Text, Expr Src X) )+ ( Op (Text, Expr Src Void) ) a ) deriving (Contravariant)@@ -2193,6 +2570,7 @@ infixr 5 >|< +-- | Convert a `UnionInputType` into the equivalent `InputType` inputUnion :: UnionInputType a -> InputType a inputUnion ( UnionInputType ( Data.Functor.Product.Pair ( Control.Applicative.Const fields ) ( Op embedF ) ) ) = InputType@@ -2207,6 +2585,9 @@ where fields' = fmap notEmptyRecord fields +{-| Specify how to encode an alternative by providing an explicit `InputType`+ for that alternative+-} inputConstructorWith :: Text -> InputType a@@ -2222,8 +2603,11 @@ ( Op ( (name,) . embed inputType ) ) +{-| Specify how to encode an alternative by using the default `ToDhall` instance+ for that type+-} inputConstructor- :: Inject a+ :: ToDhall a => Text -> UnionInputType a inputConstructor name = inputConstructorWith name inject
src/Dhall/Binary.hs view
@@ -31,6 +31,7 @@ , Chunks(..) , Const(..) , Directory(..)+ , DhallDouble(..) , Expr(..) , File(..) , FilePrefix(..)@@ -59,7 +60,11 @@ import qualified Dhall.Map import qualified Dhall.Set --- | Supported version strings+{-| Supported version strings++ This exists primarily for backwards compatibility for expressions encoded+ before Dhall removed version tags from the binary encoding+-} data StandardVersion = NoVersion -- ^ No version string@@ -75,6 +80,7 @@ -- ^ Version "1.0.0" deriving (Enum, Bounded) +-- | Render a `StandardVersion` as `Text` renderStandardVersion :: StandardVersion -> Text renderStandardVersion NoVersion = "none" renderStandardVersion V_1_0_0 = "1.0.0"@@ -263,6 +269,11 @@ where l₁ = encode l₀ r₁ = encode r₀+ encode (RecordCompletion l₀ r₀) =+ TList [ TInt 3, TInt 13, l₁, r₁ ]+ where+ l₁ = encode l₀+ r₁ = encode r₀ encode (ListLit _T₀ xs₀) | null xs₀ = TList [ TInt label, _T₁ ] | otherwise = TList ([ TInt 4, TNull ] ++ xs₁)@@ -343,18 +354,8 @@ TList [ TInt 15, TInteger (fromIntegral n) ] encode (IntegerLit n) = TList [ TInt 16, TInteger n ]- encode (DoubleLit n64)- -- cborg always encodes NaN as "7e00"- | isNaN n64 = THalf n32- | useHalf = THalf n32- | useFloat = TFloat n32- | otherwise = TDouble n64- where- n32 = double2Float n64- useFloat = n64 == float2Double n32- -- the other three cases for Half-floats are 0.0 and the infinities- useHalf = or $ fmap (n64 ==) [0.0, infinity, -infinity]- infinity = 1/0 :: Double+ encode (DoubleLit d) =+ encode d encode (TextLit (Chunks xys₀ z₀)) = TList ([ TInt 18 ] ++ xys₁ ++ [ z₁ ]) where@@ -472,6 +473,19 @@ instance ToTerm Void where encode = absurd +instance ToTerm DhallDouble where+ encode (DhallDouble n64)+ -- cborg always encodes NaN as a half-precision float of value "7e00"+ | useHalf = THalf n32+ | useFloat = TFloat n32+ | otherwise = TDouble n64+ where+ n32 = double2Float n64+ useFloat = n64 == float2Double n32+ -- the other four cases for Half-floats are -0.0, 0.0 and the infinities+ useHalf = n64 == 0.0 || n64 == infinity || n64 == -infinity+ infinity = 1/0 :: Double+ -- | Types that can be decoded from a CBOR `Term` class FromTerm a where decode :: Term -> Maybe a@@ -593,6 +607,7 @@ 10 -> return CombineTypes 11 -> return ImportAlt 12 -> return Equivalent+ 13 -> return RecordCompletion _ -> empty return (op l₀ r₀) decode (TList [ TInt 4, _T₁ ]) = do@@ -693,11 +708,11 @@ decode (TList [ TInt 16, TInteger n ]) = do return (IntegerLit n) decode (THalf n) = do- return (DoubleLit (float2Double n))+ return (DoubleLit (DhallDouble (float2Double n))) decode (TFloat n) = do- return (DoubleLit (float2Double n))+ return (DoubleLit (DhallDouble (float2Double n))) decode (TDouble n) = do- return (DoubleLit n)+ return (DoubleLit (DhallDouble n)) decode (TList (TInt 18 : xs)) = do let process (TString x : y₁ : zs) = do y₀ <- decode y₁@@ -932,6 +947,9 @@ decode taggedTerm +{-| This indicates that a given CBOR expression did not correspond to a valid+ Dhall expression+-} data DecodingFailure = CBORIsNotDhall Term deriving (Eq)
− src/Dhall/Binary.hs-boot
@@ -1,10 +0,0 @@-module Dhall.Binary where--import Codec.CBOR.Term (Term)--import {-# SOURCE #-} Dhall.Core--class ToTerm a where- encode :: a -> Term--instance ToTerm a => ToTerm (Expr s a)
src/Dhall/Core.hs view
@@ -1,2390 +1,1384 @@ {-# LANGUAGE BangPatterns #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DeriveTraversable #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE UnicodeSyntax #-}-{-# OPTIONS_GHC -Wall #-}--{-| This module contains the core calculus for the Dhall language.-- Dhall is essentially a fork of the @morte@ compiler but with more built-in- functionality, better error messages, and Haskell integration--}--module Dhall.Core (- -- * Syntax- Const(..)- , Directory(..)- , File(..)- , FilePrefix(..)- , Import(..)- , ImportHashed(..)- , ImportMode(..)- , ImportType(..)- , URL(..)- , Scheme(..)- , Var(..)- , Chunks(..)- , Expr(..)-- -- * Normalization- , Eval.alphaNormalize- , Eval.normalize- , normalizeWith- , normalizeWithM- , Normalizer- , NormalizerM- , ReifiedNormalizer (..)- , Eval.judgmentallyEqual- , subst- , shift- , isNormalized- , isNormalizedWith- , denote- , renote- , shallowDenote- , freeIn-- -- * Pretty-printing- , pretty-- -- * Optics- , subExpressions- , chunkExprs- , bindingExprs-- -- * Let-blocks- , multiLet- , wrapInLets- , MultiLet(..)- , Binding(..)- , makeBinding-- -- * Miscellaneous- , internalError- , reservedIdentifiers- , escapeText- , pathCharacter- , throws- , textShow- , censorExpression- , censorText- ) where--import Control.Applicative (empty)-import Control.DeepSeq (NFData)-import Control.Exception (Exception)-import Control.Monad.IO.Class (MonadIO(..))-import Data.Bifunctor (Bifunctor(..))-import Data.Data (Data)-import Data.Foldable-import Data.Functor.Identity (Identity(..))-import Data.HashSet (HashSet)-import Data.List.NonEmpty (NonEmpty(..))-import Data.String (IsString(..))-import Data.Semigroup (Semigroup(..))-import Data.Sequence (Seq, ViewL(..), ViewR(..))-import Data.Text (Text)-import Data.Text.Prettyprint.Doc (Doc, Pretty)-import Data.Traversable-import Data.Void (Void)-import Dhall.Map (Map)-import Dhall.Set (Set)-import Dhall.Src (Src(..))-import {-# SOURCE #-} Dhall.Pretty.Internal-import GHC.Generics (Generic)-import Instances.TH.Lift ()-import Language.Haskell.TH.Syntax (Lift)-import Lens.Family (over)-import Numeric.Natural (Natural)-import Prelude hiding (succ)-import Unsafe.Coerce (unsafeCoerce)--import qualified Control.Exception-import qualified Control.Monad-import qualified Data.Char-import {-# SOURCE #-} qualified Dhall.Eval as Eval-import qualified Data.HashSet-import qualified Data.List.NonEmpty-import qualified Data.Sequence-import qualified Data.Set-import qualified Data.Text-import qualified Data.Text.Prettyprint.Doc as Pretty-import qualified Dhall.Crypto-import qualified Dhall.Map-import qualified Dhall.Set-import qualified Network.URI as URI-import qualified Text.Printf---{-| Constants for a pure type system-- The axioms are:--> ⊦ Type : Kind-> ⊦ Kind : Sort-- ... and the valid rule pairs are:--> ⊦ Type ↝ Type : Type -- Functions from terms to terms (ordinary functions)-> ⊦ Kind ↝ Type : Type -- Functions from types to terms (type-polymorphic functions)-> ⊦ Sort ↝ Type : Type -- Functions from kinds to terms-> ⊦ Kind ↝ Kind : Kind -- Functions from types to types (type-level functions)-> ⊦ Sort ↝ Kind : Sort -- Functions from kinds to types (kind-polymorphic functions)-> ⊦ Sort ↝ Sort : Sort -- Functions from kinds to kinds (kind-level functions)-- Note that Dhall does not support functions from terms to types and therefore- Dhall is not a dependently typed language--}-data Const = Type | Kind | Sort- deriving (Show, Eq, Ord, Data, Bounded, Enum, Generic, NFData)--instance Lift Const--instance Pretty Const where- pretty = Pretty.unAnnotate . prettyConst--{-| Internal representation of a directory that stores the path components in- reverse order-- In other words, the directory @\/foo\/bar\/baz@ is encoded as- @Directory { components = [ "baz", "bar", "foo" ] }@--}-newtype Directory = Directory { components :: [Text] }- deriving (Eq, Generic, Ord, Show, NFData)--instance Semigroup Directory where- Directory components₀ <> Directory components₁ =- Directory (components₁ <> components₀)--instance Pretty Directory where- pretty (Directory {..}) = foldMap prettyPathComponent (reverse components)--{-| A `File` is a `directory` followed by one additional path component- representing the `file` name--}-data File = File- { directory :: Directory- , file :: Text- } deriving (Eq, Generic, Ord, Show, NFData)--instance Pretty File where- pretty (File {..}) =- Pretty.pretty directory- <> prettyPathComponent file--instance Semigroup File where- 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- | Here- -- ^ Path relative to @.@- | Parent- -- ^ Path relative to @..@- | Home- -- ^ Path relative to @~@- deriving (Eq, Generic, Ord, Show, NFData)--instance Pretty FilePrefix where- pretty Absolute = ""- pretty Here = "."- pretty Parent = ".."- pretty Home = "~"--data Scheme = HTTP | HTTPS deriving (Eq, Generic, Ord, Show, NFData)--data URL = URL- { scheme :: Scheme- , authority :: Text- , path :: File- , query :: Maybe Text- , headers :: Maybe (Expr Src Import)- } deriving (Eq, Generic, Ord, Show, NFData)--instance Pretty URL where- pretty (URL {..}) =- schemeDoc- <> "://"- <> Pretty.pretty authority- <> pathDoc- <> queryDoc- <> foldMap prettyHeaders headers- where- prettyHeaders h = " using " <> Pretty.pretty h-- File {..} = path-- Directory {..} = directory-- pathDoc =- foldMap prettyURIComponent (reverse components)- <> prettyURIComponent file-- schemeDoc = case scheme of- HTTP -> "http"- HTTPS -> "https"-- queryDoc = case query of- Nothing -> ""- Just q -> "?" <> Pretty.pretty q---- | The type of import (i.e. local vs. remote vs. environment)-data ImportType- = Local FilePrefix File- -- ^ Local path- | Remote URL- -- ^ URL of remote resource and optional headers stored in an import- | Env Text- -- ^ Environment variable- | Missing- deriving (Eq, Generic, Ord, Show, NFData)--parent :: File-parent = File { directory = Directory { components = [ ".." ] }, file = "" }--instance Semigroup ImportType where- Local prefix file₀ <> Local Here file₁ = Local prefix (file₀ <> file₁)-- Remote (URL { path = path₀, ..}) <> Local Here path₁ =- Remote (URL { path = path₀ <> path₁, ..})-- Local prefix file₀ <> Local Parent file₁ =- Local prefix (file₀ <> parent <> file₁)-- Remote (URL { path = path₀, .. }) <> Local Parent path₁ =- Remote (URL { path = path₀ <> parent <> path₁, .. })-- import₀ <> Remote (URL { headers = headers₀, .. }) =- Remote (URL { headers = headers₁, .. })- where- importHashed₀ = Import (ImportHashed Nothing import₀) Code-- headers₁ = fmap (fmap (importHashed₀ <>)) headers₀-- _ <> import₁ =- import₁--instance Pretty ImportType where- pretty (Local prefix file) =- Pretty.pretty prefix <> Pretty.pretty file-- pretty (Remote url) = Pretty.pretty url-- 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 | Location- deriving (Eq, Generic, Ord, Show, NFData)---- | A `ImportType` extended with an optional hash for semantic integrity checks-data ImportHashed = ImportHashed- { hash :: Maybe Dhall.Crypto.SHA256Digest- , importType :: ImportType- } deriving (Eq, Generic, Ord, Show, NFData)--instance Semigroup ImportHashed where- ImportHashed _ importType₀ <> ImportHashed hash importType₁ =- ImportHashed hash (importType₀ <> importType₁)--instance Pretty ImportHashed where- pretty (ImportHashed Nothing p) =- Pretty.pretty p- pretty (ImportHashed (Just h) p) =- Pretty.pretty p <> " sha256:" <> Pretty.pretty (show h)---- | Reference to an external resource-data Import = Import- { importHashed :: ImportHashed- , importMode :: ImportMode- } deriving (Eq, Generic, Ord, Show, NFData)--instance Semigroup Import where- Import importHashed₀ _ <> Import importHashed₁ code =- Import (importHashed₀ <> importHashed₁) code--instance Pretty Import where- pretty (Import {..}) = Pretty.pretty importHashed <> Pretty.pretty suffix- where- suffix :: Text- suffix = case importMode of- RawText -> " as Text"- Location -> " as Location"- Code -> ""--{-| Label for a bound variable-- The `Text` field is the variable's name (i.e. \"@x@\").-- The `Int` field disambiguates variables with the same name if there are- multiple bound variables of the same name in scope. Zero refers to the- nearest bound variable and the index increases by one for each bound- variable of the same name going outward. The following diagram may help:--> ┌──refers to──┐-> │ │-> v │-> λ(x : Type) → λ(y : Type) → λ(x : Type) → x@0->-> ┌─────────────────refers to─────────────────┐-> │ │-> v │-> λ(x : Type) → λ(y : Type) → λ(x : Type) → x@1-- This `Int` behaves like a De Bruijn index in the special case where all- variables have the same name.-- You can optionally omit the index if it is @0@:--> ┌─refers to─┐-> │ │-> v │-> λ(x : Type) → λ(y : Type) → λ(x : Type) → x-- Zero indices are omitted when pretty-printing `Var`s and non-zero indices- appear as a numeric suffix.--}-data Var = V Text !Int- deriving (Data, Generic, Eq, Ord, Show, NFData)--instance Lift Var--instance IsString Var where- fromString str = V (fromString str) 0--instance Pretty Var where- pretty = Pretty.unAnnotate . prettyVar--{-| Syntax tree for expressions-- The @s@ type parameter is used to track the presence or absence of `Src`- spans:-- * If @s = `Src`@ then the code may contains `Src` spans (either in a `Noted`- constructor or inline within another constructor, like `Let`)- * If @s = `Void`@ then the code has no `Src` spans-- The @a@ type parameter is used to track the presence or absence of imports-- * If @a = `Import`@ then the code may contain unresolved `Import`s- * If @a = `Void`@ then the code has no `Import`s--}-data Expr s a- -- | > Const c ~ c- = Const Const- -- | > Var (V x 0) ~ x- -- > Var (V x n) ~ x@n- | Var Var- -- | > Lam x A b ~ λ(x : A) -> b- | Lam Text (Expr s a) (Expr s a)- -- | > Pi "_" A B ~ A -> B- -- > Pi x A B ~ ∀(x : A) -> B- | Pi Text (Expr s a) (Expr s a)- -- | > App f a ~ f a- | App (Expr s a) (Expr s a)- -- | > Let (Binding _ x _ Nothing _ r) e ~ let x = r in e- -- > Let (Binding _ x _ (Just t ) _ r) e ~ let x : t = r in e- --- -- The difference between- --- -- > let x = a let y = b in e- --- -- and- --- -- > let x = a in let y = b in e- --- -- is only an additional 'Note' around @'Let' "y" …@ in the second- -- example.- --- -- See 'MultiLet' for a representation of let-blocks that mirrors the- -- source code more closely.- | Let (Binding s a) (Expr s a)- -- | > Annot x t ~ x : t- | Annot (Expr s a) (Expr s a)- -- | > Bool ~ Bool- | Bool- -- | > BoolLit b ~ b- | BoolLit Bool- -- | > BoolAnd x y ~ x && y- | BoolAnd (Expr s a) (Expr s a)- -- | > BoolOr x y ~ x || y- | BoolOr (Expr s a) (Expr s a)- -- | > BoolEQ x y ~ x == y- | BoolEQ (Expr s a) (Expr s a)- -- | > BoolNE x y ~ x != y- | BoolNE (Expr s a) (Expr s a)- -- | > BoolIf x y z ~ if x then y else z- | BoolIf (Expr s a) (Expr s a) (Expr s a)- -- | > Natural ~ Natural- | Natural- -- | > NaturalLit n ~ n- | NaturalLit Natural- -- | > NaturalFold ~ Natural/fold- | NaturalFold- -- | > NaturalBuild ~ Natural/build- | NaturalBuild- -- | > NaturalIsZero ~ Natural/isZero- | NaturalIsZero- -- | > NaturalEven ~ Natural/even- | NaturalEven- -- | > NaturalOdd ~ Natural/odd- | NaturalOdd- -- | > NaturalToInteger ~ Natural/toInteger- | NaturalToInteger- -- | > NaturalShow ~ Natural/show- | NaturalShow- -- | > NaturalSubtract ~ Natural/subtract- | NaturalSubtract- -- | > NaturalPlus x y ~ x + y- | NaturalPlus (Expr s a) (Expr s a)- -- | > NaturalTimes x y ~ x * y- | NaturalTimes (Expr s a) (Expr s a)- -- | > Integer ~ Integer- | Integer- -- | > IntegerLit n ~ ±n- | IntegerLit Integer- -- | > IntegerShow ~ Integer/show- | IntegerShow- -- | > IntegerToDouble ~ Integer/toDouble- | IntegerToDouble- -- | > Double ~ Double- | Double- -- | > DoubleLit n ~ n- | DoubleLit Double- -- | > DoubleShow ~ Double/show- | DoubleShow- -- | > Text ~ Text- | Text- -- | > TextLit (Chunks [(t1, e1), (t2, e2)] t3) ~ "t1${e1}t2${e2}t3"- | TextLit (Chunks s a)- -- | > TextAppend x y ~ x ++ y- | TextAppend (Expr s a) (Expr s a)- -- | > TextShow ~ Text/show- | TextShow- -- | > List ~ List- | List- -- | > ListLit (Just t ) [x, y, z] ~ [x, y, z] : t- -- > ListLit Nothing [x, y, z] ~ [x, y, z]- | ListLit (Maybe (Expr s a)) (Seq (Expr s a))- -- | > ListAppend x y ~ x # y- | ListAppend (Expr s a) (Expr s a)- -- | > ListBuild ~ List/build- | ListBuild- -- | > ListFold ~ List/fold- | ListFold- -- | > ListLength ~ List/length- | ListLength- -- | > ListHead ~ List/head- | ListHead- -- | > ListLast ~ List/last- | ListLast- -- | > ListIndexed ~ List/indexed- | ListIndexed- -- | > ListReverse ~ List/reverse- | ListReverse- -- | > Optional ~ Optional- | Optional- -- | > Some e ~ Some e- | Some (Expr s a)- -- | > None ~ None- | None- -- | > OptionalFold ~ Optional/fold- | OptionalFold- -- | > OptionalBuild ~ Optional/build- | OptionalBuild- -- | > Record [(k1, t1), (k2, t2)] ~ { k1 : t1, k2 : t1 }- | Record (Map Text (Expr s a))- -- | > RecordLit [(k1, v1), (k2, v2)] ~ { k1 = v1, k2 = v2 }- | RecordLit (Map Text (Expr s a))- -- | > Union [(k1, Just t1), (k2, Nothing)] ~ < k1 : t1 | k2 >- | Union (Map Text (Maybe (Expr s a)))- -- | > Combine x y ~ x ∧ y- | Combine (Expr s a) (Expr s a)- -- | > CombineTypes x y ~ x ⩓ y- | CombineTypes (Expr s a) (Expr s a)- -- | > Prefer x y ~ x ⫽ y- | Prefer (Expr s a) (Expr s a)- -- | > Merge x y (Just t ) ~ merge x y : t- -- > Merge x y Nothing ~ merge x y- | Merge (Expr s a) (Expr s a) (Maybe (Expr s a))- -- | > ToMap x (Just t) ~ toMap x : t- -- > ToMap x Nothing ~ toMap x- | ToMap (Expr s a) (Maybe (Expr s a))- -- | > Field e x ~ e.x- | Field (Expr s a) Text- -- | > Project e (Left xs) ~ e.{ xs }- -- | > Project e (Right t) ~ e.(t)- | Project (Expr s a) (Either (Set Text) (Expr s a))- -- | > Assert e ~ assert : e- | Assert (Expr s a)- -- | > Equivalent x y ~ x ≡ y- | Equivalent (Expr s a) (Expr s a)- -- | > Note s x ~ e- | Note s (Expr s a)- -- | > ImportAlt ~ e1 ? e2- | ImportAlt (Expr s a) (Expr s a)- -- | > Embed import ~ import- | Embed a- deriving (Foldable, Generic, Traversable, Show, Data, NFData)--- NB: If you add a constructor to Expr, please also update the Arbitrary--- instance in Dhall.Test.QuickCheck.---- | Note that this 'Eq' instance inherits `Double`'s defects, e.g.------ >>> nan = 0/0--- >>> DoubleLit nan == DoubleLit nan--- False-deriving instance (Eq s, Eq a) => Eq (Expr s a)---- | Note that this 'Eq' instance inherits `Double`'s defects, e.g.------ >>> nan = 0/0--- >>> DoubleLit nan <= DoubleLit nan--- False-deriving instance (Ord s, Ord a) => Ord (Expr s a)--instance (Lift s, Lift a, Data s, Data a) => Lift (Expr s a)---- This instance is hand-written due to the fact that deriving--- it does not give us an INLINABLE pragma. We annotate this fmap--- implementation with this pragma below to allow GHC to, possibly,--- inline the implementation for performance improvements.-instance Functor (Expr s) where- fmap _ (Const c) = Const c- fmap _ (Var v) = Var v- fmap f (Lam v e1 e2) = Lam v (fmap f e1) (fmap f e2)- fmap f (Pi v e1 e2) = Pi v (fmap f e1) (fmap f e2)- fmap f (App e1 e2) = App (fmap f e1) (fmap f e2)- fmap f (Let b e2) = Let (fmap f b) (fmap f e2)- fmap f (Annot e1 e2) = Annot (fmap f e1) (fmap f e2)- fmap _ Bool = Bool- fmap _ (BoolLit b) = BoolLit b- fmap f (BoolAnd e1 e2) = BoolAnd (fmap f e1) (fmap f e2)- fmap f (BoolOr e1 e2) = BoolOr (fmap f e1) (fmap f e2)- fmap f (BoolEQ e1 e2) = BoolEQ (fmap f e1) (fmap f e2)- fmap f (BoolNE e1 e2) = BoolNE (fmap f e1) (fmap f e2)- fmap f (BoolIf e1 e2 e3) = BoolIf (fmap f e1) (fmap f e2) (fmap f e3)- fmap _ Natural = Natural- fmap _ (NaturalLit n) = NaturalLit n- fmap _ NaturalFold = NaturalFold- fmap _ NaturalBuild = NaturalBuild- fmap _ NaturalIsZero = NaturalIsZero- fmap _ NaturalEven = NaturalEven- fmap _ NaturalOdd = NaturalOdd- fmap _ NaturalToInteger = NaturalToInteger- fmap _ NaturalShow = NaturalShow- fmap _ NaturalSubtract = NaturalSubtract- fmap f (NaturalPlus e1 e2) = NaturalPlus (fmap f e1) (fmap f e2)- fmap f (NaturalTimes e1 e2) = NaturalTimes (fmap f e1) (fmap f e2)- fmap _ Integer = Integer- fmap _ (IntegerLit i) = IntegerLit i- fmap _ IntegerShow = IntegerShow- fmap _ IntegerToDouble = IntegerToDouble- fmap _ Double = Double- fmap _ (DoubleLit d) = DoubleLit d- fmap _ DoubleShow = DoubleShow- fmap _ Text = Text- fmap f (TextLit cs) = TextLit (fmap f cs)- fmap f (TextAppend e1 e2) = TextAppend (fmap f e1) (fmap f e2)- fmap _ TextShow = TextShow- fmap _ List = List- fmap f (ListLit maybeE seqE) = ListLit (fmap (fmap f) maybeE) (fmap (fmap f) seqE)- fmap f (ListAppend e1 e2) = ListAppend (fmap f e1) (fmap f e2)- fmap _ ListBuild = ListBuild- fmap _ ListFold = ListFold- fmap _ ListLength = ListLength- fmap _ ListHead = ListHead- fmap _ ListLast = ListLast- fmap _ ListIndexed = ListIndexed- fmap _ ListReverse = ListReverse- fmap _ Optional = Optional- fmap f (Some e) = Some (fmap f e)- fmap _ None = None- fmap _ OptionalFold = OptionalFold- fmap _ OptionalBuild = OptionalBuild- fmap f (Record r) = Record (fmap (fmap f) r)- fmap f (RecordLit r) = RecordLit (fmap (fmap f) r)- fmap f (Union u) = Union (fmap (fmap (fmap f)) u)- fmap f (Combine e1 e2) = Combine (fmap f e1) (fmap f e2)- fmap f (CombineTypes e1 e2) = CombineTypes (fmap f e1) (fmap f e2)- fmap f (Prefer e1 e2) = Prefer (fmap f e1) (fmap f e2)- fmap f (Merge e1 e2 maybeE) = Merge (fmap f e1) (fmap f e2) (fmap (fmap f) maybeE)- fmap f (ToMap e maybeE) = ToMap (fmap f e) (fmap (fmap f) maybeE)- fmap f (Field e1 v) = Field (fmap f e1) v- fmap f (Project e1 vs) = Project (fmap f e1) (fmap (fmap f) vs)- fmap f (Assert t) = Assert (fmap f t)- fmap f (Equivalent e1 e2) = Equivalent (fmap f e1) (fmap f e2)- fmap f (Note s e1) = Note s (fmap f e1)- fmap f (ImportAlt e1 e2) = ImportAlt (fmap f e1) (fmap f e2)- fmap f (Embed a) = Embed (f a)- {-# INLINABLE fmap #-}--instance Applicative (Expr s) where- pure = Embed-- (<*>) = Control.Monad.ap--instance Monad (Expr s) where- return = pure-- Const a >>= _ = Const a- Var a >>= _ = Var a- Lam a b c >>= k = Lam a (b >>= k) (c >>= k)- Pi a b c >>= k = Pi a (b >>= k) (c >>= k)- App a b >>= k = App (a >>= k) (b >>= k)- Let a b >>= k = Let (adapt0 a) (b >>= k)- where- adapt0 (Binding src0 c src1 d src2 e) =- Binding src0 c src1 (fmap adapt1 d) src2 (e >>= k)-- adapt1 (src3, f) = (src3, f >>= k)- Annot a b >>= k = Annot (a >>= k) (b >>= k)- Bool >>= _ = Bool- BoolLit a >>= _ = BoolLit a- BoolAnd a b >>= k = BoolAnd (a >>= k) (b >>= k)- BoolOr a b >>= k = BoolOr (a >>= k) (b >>= k)- BoolEQ a b >>= k = BoolEQ (a >>= k) (b >>= k)- BoolNE a b >>= k = BoolNE (a >>= k) (b >>= k)- BoolIf a b c >>= k = BoolIf (a >>= k) (b >>= k) (c >>= k)- Natural >>= _ = Natural- NaturalLit a >>= _ = NaturalLit a- NaturalFold >>= _ = NaturalFold- NaturalBuild >>= _ = NaturalBuild- NaturalIsZero >>= _ = NaturalIsZero- NaturalEven >>= _ = NaturalEven- NaturalOdd >>= _ = NaturalOdd- NaturalToInteger >>= _ = NaturalToInteger- NaturalShow >>= _ = NaturalShow- NaturalSubtract >>= _ = NaturalSubtract- NaturalPlus a b >>= k = NaturalPlus (a >>= k) (b >>= k)- NaturalTimes a b >>= k = NaturalTimes (a >>= k) (b >>= k)- Integer >>= _ = Integer- IntegerLit a >>= _ = IntegerLit a- IntegerShow >>= _ = IntegerShow- IntegerToDouble >>= _ = IntegerToDouble- Double >>= _ = Double- DoubleLit a >>= _ = DoubleLit a- DoubleShow >>= _ = DoubleShow- Text >>= _ = Text- TextLit (Chunks a b) >>= k = TextLit (Chunks (fmap (fmap (>>= k)) a) b)- TextAppend a b >>= k = TextAppend (a >>= k) (b >>= k)- TextShow >>= _ = TextShow- List >>= _ = List- ListLit a b >>= k = ListLit (fmap (>>= k) a) (fmap (>>= k) b)- ListAppend a b >>= k = ListAppend (a >>= k) (b >>= k)- ListBuild >>= _ = ListBuild- ListFold >>= _ = ListFold- ListLength >>= _ = ListLength- ListHead >>= _ = ListHead- ListLast >>= _ = ListLast- ListIndexed >>= _ = ListIndexed- ListReverse >>= _ = ListReverse- Optional >>= _ = Optional- Some a >>= k = Some (a >>= k)- None >>= _ = None- OptionalFold >>= _ = OptionalFold- OptionalBuild >>= _ = OptionalBuild- Record a >>= k = Record (fmap (>>= k) a)- RecordLit a >>= k = RecordLit (fmap (>>= k) a)- Union a >>= k = Union (fmap (fmap (>>= k)) a)- Combine a b >>= k = Combine (a >>= k) (b >>= k)- CombineTypes a b >>= k = CombineTypes (a >>= k) (b >>= k)- Prefer a b >>= k = Prefer (a >>= k) (b >>= k)- Merge a b c >>= k = Merge (a >>= k) (b >>= k) (fmap (>>= k) c)- ToMap a b >>= k = ToMap (a >>= k) (fmap (>>= k) b)- Field a b >>= k = Field (a >>= k) b- Project a b >>= k = Project (a >>= k) (fmap (>>= k) b)- Assert a >>= k = Assert (a >>= k)- Equivalent a b >>= k = Equivalent (a >>= k) (b >>= k)- Note a b >>= k = Note a (b >>= k)- ImportAlt a b >>= k = ImportAlt (a >>= k) (b >>= k)- Embed a >>= k = k a--instance Bifunctor Expr where- first _ (Const a ) = Const a- first _ (Var a ) = Var a- first k (Lam a b c ) = Lam a (first k b) (first k c)- first k (Pi a b c ) = Pi a (first k b) (first k c)- first k (App a b ) = App (first k a) (first k b)- first k (Let a b ) = Let (first k a) (first k b)- first k (Annot a b ) = Annot (first k a) (first k b)- first _ Bool = Bool- first _ (BoolLit a ) = BoolLit a- first k (BoolAnd a b ) = BoolAnd (first k a) (first k b)- first k (BoolOr a b ) = BoolOr (first k a) (first k b)- first k (BoolEQ a b ) = BoolEQ (first k a) (first k b)- first k (BoolNE a b ) = BoolNE (first k a) (first k b)- first k (BoolIf a b c ) = BoolIf (first k a) (first k b) (first k c)- first _ Natural = Natural- first _ (NaturalLit a ) = NaturalLit a- first _ NaturalFold = NaturalFold- first _ NaturalBuild = NaturalBuild- first _ NaturalIsZero = NaturalIsZero- first _ NaturalEven = NaturalEven- first _ NaturalOdd = NaturalOdd- first _ NaturalToInteger = NaturalToInteger- first _ NaturalShow = NaturalShow- first _ NaturalSubtract = NaturalSubtract- first k (NaturalPlus a b ) = NaturalPlus (first k a) (first k b)- first k (NaturalTimes a b ) = NaturalTimes (first k a) (first k b)- first _ Integer = Integer- first _ (IntegerLit a ) = IntegerLit a- first _ IntegerShow = IntegerShow- first _ IntegerToDouble = IntegerToDouble- first _ Double = Double- first _ (DoubleLit a ) = DoubleLit a- first _ DoubleShow = DoubleShow- first _ Text = Text- first k (TextLit (Chunks a b)) = TextLit (Chunks (fmap (fmap (first k)) a) b)- first k (TextAppend a b ) = TextAppend (first k a) (first k b)- first _ TextShow = TextShow- first _ List = List- first k (ListLit a b ) = ListLit (fmap (first k) a) (fmap (first k) b)- first k (ListAppend a b ) = ListAppend (first k a) (first k b)- first _ ListBuild = ListBuild- first _ ListFold = ListFold- first _ ListLength = ListLength- first _ ListHead = ListHead- first _ ListLast = ListLast- first _ ListIndexed = ListIndexed- first _ ListReverse = ListReverse- first _ Optional = Optional- first k (Some a ) = Some (first k a)- first _ None = None- first _ OptionalFold = OptionalFold- first _ OptionalBuild = OptionalBuild- first k (Record a ) = Record (fmap (first k) a)- first k (RecordLit a ) = RecordLit (fmap (first k) a)- first k (Union a ) = Union (fmap (fmap (first k)) a)- first k (Combine a b ) = Combine (first k a) (first k b)- first k (CombineTypes a b ) = CombineTypes (first k a) (first k b)- first k (Prefer a b ) = Prefer (first k a) (first k b)- first k (Merge a b c ) = Merge (first k a) (first k b) (fmap (first k) c)- first k (ToMap a b ) = ToMap (first k a) (fmap (first k) b)- first k (Field a b ) = Field (first k a) b- first k (Assert a ) = Assert (first k a)- first k (Equivalent a b ) = Equivalent (first k a) (first k b)- first k (Project a b ) = Project (first k a) (fmap (first k) b)- first k (Note a b ) = Note (k a) (first k b)- first k (ImportAlt a b ) = ImportAlt (first k a) (first k b)- first _ (Embed a ) = Embed a-- second = fmap--instance IsString (Expr s a) where- fromString str = Var (fromString str)---- | The body of an interpolated @Text@ literal-data Chunks s a = Chunks [(Text, Expr s a)] Text- deriving (Functor, Foldable, Generic, Traversable, Show, Eq, Ord, Data, NFData)--instance (Lift s, Lift a, Data s, Data a) => Lift (Chunks s a)--instance Data.Semigroup.Semigroup (Chunks s a) where- Chunks xysL zL <> Chunks [] zR =- Chunks xysL (zL <> zR)- Chunks xysL zL <> Chunks ((x, y):xysR) zR =- Chunks (xysL ++ (zL <> x, y):xysR) zR--instance Monoid (Chunks s a) where- mempty = Chunks [] mempty--#if !(MIN_VERSION_base(4,11,0))- mappend = (<>)-#endif--instance IsString (Chunks s a) where- fromString str = Chunks [] (fromString str)--{- There is a one-to-one correspondence between the builders in this section- and the sub-parsers in "Dhall.Parser". Each builder is named after the- corresponding parser and the relationship between builders exactly matches- the relationship between parsers. This leads to the nice emergent property- of automatically getting all the parentheses and precedences right.-- This approach has one major disadvantage: you can get an infinite loop if- you add a new constructor to the syntax tree without adding a matching- case the corresponding builder.--}---- | Generates a syntactically valid Dhall program-instance Pretty a => Pretty (Expr s a) where- pretty = Pretty.unAnnotate . prettyExpr--{-| `shift` is used by both normalization and type-checking to avoid variable- capture by shifting variable indices-- For example, suppose that you were to normalize the following expression:--> λ(a : Type) → λ(x : a) → (λ(y : a) → λ(x : a) → y) x-- If you were to substitute @y@ with @x@ without shifting any variable- indices, then you would get the following incorrect result:--> λ(a : Type) → λ(x : a) → λ(x : a) → x -- Incorrect normalized form-- In order to substitute @x@ in place of @y@ we need to `shift` @x@ by @1@ in- order to avoid being misinterpreted as the @x@ bound by the innermost- lambda. If we perform that `shift` then we get the correct result:--> λ(a : Type) → λ(x : a) → λ(x : a) → x@1-- As a more worked example, suppose that you were to normalize the following- expression:--> λ(a : Type)-> → λ(f : a → a → a)-> → λ(x : a)-> → λ(x : a)-> → (λ(x : a) → f x x@1) x@1-- The correct normalized result would be:--> λ(a : Type)-> → λ(f : a → a → a)-> → λ(x : a)-> → λ(x : a)-> → f x@1 x-- The above example illustrates how we need to both increase and decrease- variable indices as part of substitution:-- * We need to increase the index of the outer @x\@1@ to @x\@2@ before we- substitute it into the body of the innermost lambda expression in order- to avoid variable capture. This substitution changes the body of the- lambda expression to @(f x\@2 x\@1)@-- * We then remove the innermost lambda and therefore decrease the indices of- both @x@s in @(f x\@2 x\@1)@ to @(f x\@1 x)@ in order to reflect that one- less @x@ variable is now bound within that scope-- Formally, @(shift d (V x n) e)@ modifies the expression @e@ by adding @d@ to- the indices of all variables named @x@ whose indices are greater than- @(n + m)@, where @m@ is the number of bound variables of the same name- within that scope-- In practice, @d@ is always @1@ or @-1@ because we either:-- * increment variables by @1@ to avoid variable capture during substitution- * decrement variables by @1@ when deleting lambdas after substitution-- @n@ starts off at @0@ when substitution begins and increments every time we- descend into a lambda or let expression that binds a variable of the same- name in order to avoid shifting the bound variables by mistake.--}-shift :: Int -> Var -> Expr s a -> Expr s a-shift _ _ (Const a) = Const a-shift d (V x n) (Var (V x' n')) = Var (V x' n'')- where- n'' = if x == x' && n <= n' then n' + d else n'-shift d (V x n) (Lam x' _A b) = Lam x' _A' b'- where- _A' = shift d (V x n ) _A- b' = shift d (V x n') b- where- n' = if x == x' then n + 1 else n-shift d (V x n) (Pi x' _A _B) = Pi x' _A' _B'- where- _A' = shift d (V x n ) _A- _B' = shift d (V x n') _B- where- n' = if x == x' then n + 1 else n-shift d v (App f a) = App f' a'- where- f' = shift d v f- a' = shift d v a-shift d (V x n) (Let (Binding src0 f src1 mt src2 r) e) =- Let (Binding src0 f src1 mt' src2 r') e'- where- e' = shift d (V x n') e- where- n' = if x == f then n + 1 else n-- mt' = fmap (fmap (shift d (V x n))) mt- r' = shift d (V x n) r-shift d v (Annot a b) = Annot a' b'- where- a' = shift d v a- b' = shift d v b-shift _ _ Bool = Bool-shift _ _ (BoolLit a) = BoolLit a-shift d v (BoolAnd a b) = BoolAnd a' b'- where- a' = shift d v a- b' = shift d v b-shift d v (BoolOr a b) = BoolOr a' b'- where- a' = shift d v a- b' = shift d v b-shift d v (BoolEQ a b) = BoolEQ a' b'- where- a' = shift d v a- b' = shift d v b-shift d v (BoolNE a b) = BoolNE a' b'- where- a' = shift d v a- b' = shift d v b-shift d v (BoolIf a b c) = BoolIf a' b' c'- where- a' = shift d v a- b' = shift d v b- c' = shift d v c-shift _ _ Natural = Natural-shift _ _ (NaturalLit a) = NaturalLit a-shift _ _ NaturalFold = NaturalFold-shift _ _ NaturalBuild = NaturalBuild-shift _ _ NaturalIsZero = NaturalIsZero-shift _ _ NaturalEven = NaturalEven-shift _ _ NaturalOdd = NaturalOdd-shift _ _ NaturalToInteger = NaturalToInteger-shift _ _ NaturalShow = NaturalShow-shift _ _ NaturalSubtract = NaturalSubtract-shift d v (NaturalPlus a b) = NaturalPlus a' b'- where- a' = shift d v a- b' = shift d v b-shift d v (NaturalTimes a b) = NaturalTimes a' b'- where- a' = shift d v a- b' = shift d v b-shift _ _ Integer = Integer-shift _ _ (IntegerLit a) = IntegerLit a-shift _ _ IntegerShow = IntegerShow-shift _ _ IntegerToDouble = IntegerToDouble-shift _ _ Double = Double-shift _ _ (DoubleLit a) = DoubleLit a-shift _ _ DoubleShow = DoubleShow-shift _ _ Text = Text-shift d v (TextLit (Chunks a b)) = TextLit (Chunks a' b)- where- a' = fmap (fmap (shift d v)) a-shift d v (TextAppend a b) = TextAppend a' b'- where- a' = shift d v a- b' = shift d v b-shift _ _ TextShow = TextShow-shift _ _ List = List-shift d v (ListLit a b) = ListLit a' b'- where- a' = fmap (shift d v) a- b' = fmap (shift d v) b-shift _ _ ListBuild = ListBuild-shift d v (ListAppend a b) = ListAppend a' b'- where- a' = shift d v a- b' = shift d v b-shift _ _ ListFold = ListFold-shift _ _ ListLength = ListLength-shift _ _ ListHead = ListHead-shift _ _ ListLast = ListLast-shift _ _ ListIndexed = ListIndexed-shift _ _ ListReverse = ListReverse-shift _ _ Optional = Optional-shift d v (Some a) = Some a'- where- a' = shift d v a-shift _ _ None = None-shift _ _ OptionalFold = OptionalFold-shift _ _ OptionalBuild = OptionalBuild-shift d v (Record a) = Record a'- where- a' = fmap (shift d v) a-shift d v (RecordLit a) = RecordLit a'- where- a' = fmap (shift d v) a-shift d v (Union a) = Union a'- where- a' = fmap (fmap (shift d v)) a-shift d v (Combine a b) = Combine a' b'- where- a' = shift d v a- b' = shift d v b-shift d v (CombineTypes a b) = CombineTypes a' b'- where- a' = shift d v a- b' = shift d v b-shift d v (Prefer a b) = Prefer a' b'- where- a' = shift d v a- b' = shift d v b-shift d v (Merge a b c) = Merge a' b' c'- where- a' = shift d v a- b' = shift d v b- c' = fmap (shift d v) c-shift d v (ToMap a b) = ToMap a' b'- where- a' = shift d v a- b' = fmap (shift d v) b-shift d v (Field a b) = Field a' b- where- a' = shift d v a-shift d v (Assert a) = Assert a'- where- a' = shift d v a-shift d v (Equivalent a b) = Equivalent a' b'- where- a' = shift d v a- b' = shift d v b-shift d v (Project a b) = Project a' b'- where- a' = shift d v a- b' = fmap (shift d v) b-shift d v (Note a b) = Note a b'- where- b' = shift d v b-shift d v (ImportAlt a b) = ImportAlt a' b'- where- a' = shift d v a- b' = shift d v b--- The Dhall compiler enforces that all embedded values are closed expressions--- and `shift` does nothing to a closed expression-shift _ _ (Embed p) = Embed p--{-| Substitute all occurrences of a variable with an expression--> subst x C B ~ B[x := C]--}-subst :: Var -> Expr s a -> Expr s a -> Expr s a-subst _ _ (Const a) = Const a-subst (V x n) e (Lam y _A b) = Lam y _A' b'- where- _A' = subst (V x n ) e _A- b' = subst (V x n') (shift 1 (V y 0) e) b- n' = if x == y then n + 1 else n-subst (V x n) e (Pi y _A _B) = Pi y _A' _B'- where- _A' = subst (V x n ) e _A- _B' = subst (V x n') (shift 1 (V y 0) e) _B- n' = if x == y then n + 1 else n-subst v e (App f a) = App f' a'- where- f' = subst v e f- a' = subst v e a-subst v e (Var v') = if v == v' then e else Var v'-subst (V x n) e (Let (Binding src0 f src1 mt src2 r) b) =- Let (Binding src0 f src1 mt' src2 r') b'- where- b' = subst (V x n') (shift 1 (V f 0) e) b- where- n' = if x == f then n + 1 else n-- mt' = fmap (fmap (subst (V x n) e)) mt- r' = subst (V x n) e r-subst x e (Annot a b) = Annot a' b'- where- a' = subst x e a- b' = subst x e b-subst _ _ Bool = Bool-subst _ _ (BoolLit a) = BoolLit a-subst x e (BoolAnd a b) = BoolAnd a' b'- where- a' = subst x e a- b' = subst x e b-subst x e (BoolOr a b) = BoolOr a' b'- where- a' = subst x e a- b' = subst x e b-subst x e (BoolEQ a b) = BoolEQ a' b'- where- a' = subst x e a- b' = subst x e b-subst x e (BoolNE a b) = BoolNE a' b'- where- a' = subst x e a- b' = subst x e b-subst x e (BoolIf a b c) = BoolIf a' b' c'- where- a' = subst x e a- b' = subst x e b- c' = subst x e c-subst _ _ Natural = Natural-subst _ _ (NaturalLit a) = NaturalLit a-subst _ _ NaturalFold = NaturalFold-subst _ _ NaturalBuild = NaturalBuild-subst _ _ NaturalIsZero = NaturalIsZero-subst _ _ NaturalEven = NaturalEven-subst _ _ NaturalOdd = NaturalOdd-subst _ _ NaturalToInteger = NaturalToInteger-subst _ _ NaturalShow = NaturalShow-subst _ _ NaturalSubtract = NaturalSubtract-subst x e (NaturalPlus a b) = NaturalPlus a' b'- where- a' = subst x e a- b' = subst x e b-subst x e (NaturalTimes a b) = NaturalTimes a' b'- where- a' = subst x e a- b' = subst x e b-subst _ _ Integer = Integer-subst _ _ (IntegerLit a) = IntegerLit a-subst _ _ IntegerShow = IntegerShow-subst _ _ IntegerToDouble = IntegerToDouble-subst _ _ Double = Double-subst _ _ (DoubleLit a) = DoubleLit a-subst _ _ DoubleShow = DoubleShow-subst _ _ Text = Text-subst x e (TextLit (Chunks a b)) = TextLit (Chunks a' b)- where- a' = fmap (fmap (subst x e)) a-subst x e (TextAppend a b) = TextAppend a' b'- where- a' = subst x e a- b' = subst x e b-subst _ _ TextShow = TextShow-subst _ _ List = List-subst x e (ListLit a b) = ListLit a' b'- where- a' = fmap (subst x e) a- b' = fmap (subst x e) b-subst x e (ListAppend a b) = ListAppend a' b'- where- a' = subst x e a- b' = subst x e b-subst _ _ ListBuild = ListBuild-subst _ _ ListFold = ListFold-subst _ _ ListLength = ListLength-subst _ _ ListHead = ListHead-subst _ _ ListLast = ListLast-subst _ _ ListIndexed = ListIndexed-subst _ _ ListReverse = ListReverse-subst _ _ Optional = Optional-subst x e (Some a) = Some a'- where- a' = subst x e a-subst _ _ None = None-subst _ _ OptionalFold = OptionalFold-subst _ _ OptionalBuild = OptionalBuild-subst x e (Record kts) = Record kts'- where- kts' = fmap (subst x e) kts-subst x e (RecordLit kvs) = RecordLit kvs'- where- kvs' = fmap (subst x e) kvs-subst x e (Union kts) = Union kts'- where- kts' = fmap (fmap (subst x e)) kts-subst x e (Combine a b) = Combine a' b'- where- a' = subst x e a- b' = subst x e b-subst x e (CombineTypes a b) = CombineTypes a' b'- where- a' = subst x e a- b' = subst x e b-subst x e (Prefer a b) = Prefer a' b'- where- a' = subst x e a- b' = subst x e b-subst x e (Merge a b c) = Merge a' b' c'- where- a' = subst x e a- b' = subst x e b- c' = fmap (subst x e) c-subst x e (ToMap a b) = ToMap a' b'- where- a' = subst x e a- b' = fmap (subst x e) b-subst x e (Field a b) = Field a' b- where- a' = subst x e a-subst x e (Project a b) = Project a' b'- where- a' = subst x e a- b' = fmap (subst x e) b-subst x e (Assert a) = Assert a'- where- a' = subst x e a-subst x e (Equivalent a b) = Equivalent a' b'- where- a' = subst x e a- b' = subst x e b-subst x e (Note a b) = Note a b'- where- b' = subst x e b-subst x e (ImportAlt a b) = ImportAlt a' b'- where- a' = subst x e a- b' = subst x e b--- The Dhall compiler enforces that all embedded values are closed expressions--- and `subst` does nothing to a closed expression-subst _ _ (Embed p) = Embed p--{-| This function is used to determine whether folds like @Natural/fold@ or- @List/fold@ should be lazy or strict in their accumulator based on the type- of the accumulator-- If this function returns `True`, then they will be strict in their- accumulator since we can guarantee an upper bound on the amount of work to- normalize the accumulator on each step of the loop. If this function- returns `False` then they will be lazy in their accumulator and only- normalize the final result at the end of the fold--}-boundedType :: Expr s a -> Bool-boundedType Bool = True-boundedType Natural = True-boundedType Integer = True-boundedType Double = True-boundedType Text = True-boundedType (App List _) = False-boundedType (App Optional t) = boundedType t-boundedType (Record kvs) = all boundedType kvs-boundedType (Union kvs) = all (all boundedType) kvs-boundedType _ = False---- | Remove all `Note` constructors from an `Expr` (i.e. de-`Note`)-denote :: Expr s a -> Expr t a-denote (Note _ b ) = denote b-denote (Const a ) = Const a-denote (Var a ) = Var a-denote (Lam a b c ) = Lam a (denote b) (denote c)-denote (Pi a b c ) = Pi a (denote b) (denote c)-denote (App a b ) = App (denote a) (denote b)-denote (Let a b ) = Let (adapt0 a) (denote b)- where- adapt0 (Binding _ c _ d _ e) =- Binding Nothing c Nothing (fmap adapt1 d) Nothing (denote e)-- adapt1 (_, f) = (Nothing, denote f)-denote (Annot a b ) = Annot (denote a) (denote b)-denote Bool = Bool-denote (BoolLit a ) = BoolLit a-denote (BoolAnd a b ) = BoolAnd (denote a) (denote b)-denote (BoolOr a b ) = BoolOr (denote a) (denote b)-denote (BoolEQ a b ) = BoolEQ (denote a) (denote b)-denote (BoolNE a b ) = BoolNE (denote a) (denote b)-denote (BoolIf a b c ) = BoolIf (denote a) (denote b) (denote c)-denote Natural = Natural-denote (NaturalLit a ) = NaturalLit a-denote NaturalFold = NaturalFold-denote NaturalBuild = NaturalBuild-denote NaturalIsZero = NaturalIsZero-denote NaturalEven = NaturalEven-denote NaturalOdd = NaturalOdd-denote NaturalToInteger = NaturalToInteger-denote NaturalShow = NaturalShow-denote NaturalSubtract = NaturalSubtract-denote (NaturalPlus a b ) = NaturalPlus (denote a) (denote b)-denote (NaturalTimes a b ) = NaturalTimes (denote a) (denote b)-denote Integer = Integer-denote (IntegerLit a ) = IntegerLit a-denote IntegerShow = IntegerShow-denote IntegerToDouble = IntegerToDouble-denote Double = Double-denote (DoubleLit a ) = DoubleLit a-denote DoubleShow = DoubleShow-denote Text = Text-denote (TextLit (Chunks a b)) = TextLit (Chunks (fmap (fmap denote) a) b)-denote (TextAppend a b ) = TextAppend (denote a) (denote b)-denote TextShow = TextShow-denote List = List-denote (ListLit a b ) = ListLit (fmap denote a) (fmap denote b)-denote (ListAppend a b ) = ListAppend (denote a) (denote b)-denote ListBuild = ListBuild-denote ListFold = ListFold-denote ListLength = ListLength-denote ListHead = ListHead-denote ListLast = ListLast-denote ListIndexed = ListIndexed-denote ListReverse = ListReverse-denote Optional = Optional-denote (Some a ) = Some (denote a)-denote None = None-denote OptionalFold = OptionalFold-denote OptionalBuild = OptionalBuild-denote (Record a ) = Record (fmap denote a)-denote (RecordLit a ) = RecordLit (fmap denote a)-denote (Union a ) = Union (fmap (fmap denote) a)-denote (Combine a b ) = Combine (denote a) (denote b)-denote (CombineTypes a b ) = CombineTypes (denote a) (denote b)-denote (Prefer a b ) = Prefer (denote a) (denote b)-denote (Merge a b c ) = Merge (denote a) (denote b) (fmap denote c)-denote (ToMap a b ) = ToMap (denote a) (fmap denote b)-denote (Field a b ) = Field (denote a) b-denote (Project a b ) = Project (denote a) (fmap denote b)-denote (Assert a ) = Assert (denote a)-denote (Equivalent a b ) = Equivalent (denote a) (denote b)-denote (ImportAlt a b ) = ImportAlt (denote a) (denote b)-denote (Embed a ) = Embed a---- | The \"opposite\" of `denote`, like @first absurd@ but faster-renote :: Expr Void a -> Expr s a-renote = unsafeCoerce-{-# INLINE renote #-}--shallowDenote :: Expr s a -> Expr s a-shallowDenote (Note _ e) = shallowDenote e-shallowDenote e = e--{-| Reduce an expression to its normal form, performing beta reduction and applying- any custom definitions.-- `normalizeWith` is designed to be used with function `typeWith`. The `typeWith`- function allows typing of Dhall functions in a custom typing context whereas- `normalizeWith` allows evaluating Dhall expressions in a custom context.-- To be more precise `normalizeWith` applies the given normalizer when it finds an- application term that it cannot reduce by other means.-- Note that the context used in normalization will determine the properties of normalization.- That is, if the functions in custom context are not total then the Dhall language, evaluated- with those functions is not total either.-- `normalizeWith` can fail with an `error` if you normalize an ill-typed- expression--}-normalizeWith :: Eq a => Maybe (ReifiedNormalizer a) -> Expr s a -> Expr t a-normalizeWith (Just ctx) t = runIdentity (normalizeWithM (getReifiedNormalizer ctx) t)-normalizeWith _ t = Eval.normalize t--{-| This function generalizes `normalizeWith` by allowing the custom normalizer- to use an arbitrary `Monad`-- `normalizeWithM` can fail with an `error` if you normalize an ill-typed- expression--}-normalizeWithM- :: (Monad m, Eq a) => NormalizerM m a -> Expr s a -> m (Expr t a)-normalizeWithM ctx e0 = loop (denote e0)- where- loop e = case e of- Const k -> pure (Const k)- Var v -> pure (Var v)- Lam x _A b -> Lam x <$> _A' <*> b'- where- _A' = loop _A- b' = loop b- Pi x _A _B -> Pi x <$> _A' <*> _B'- where- _A' = loop _A- _B' = loop _B- App f a -> do- res <- ctx (App f a)- case res of- Just e1 -> loop e1- Nothing -> do- f' <- loop f- a' <- loop a- case f' of- Lam x _A b₀ -> do-- let a₂ = shift 1 (V x 0) a'- let b₁ = subst (V x 0) a₂ b₀- let b₂ = shift (-1) (V x 0) b₁-- loop b₂- _ -> do- case App f' a' of- -- build/fold fusion for `List`- App (App ListBuild _) (App (App ListFold _) e') -> loop e'-- App NaturalFold (NaturalLit n) -> do- let natural = Var (V "natural" 0)- let go 0 x = x- go n' x = go (n'-1) (App (Var (V "succ" 0)) x)- let n' = go n (Var (V "zero" 0))- pure- (Lam "natural"- (Const Type)- (Lam "succ"- (Pi "_" natural natural)- (Lam "zero"- natural- n')))-- -- build/fold fusion for `Natural`- App NaturalBuild (App NaturalFold e') -> loop e'-- -- build/fold fusion for `Optional`- App (App OptionalBuild _) (App (App OptionalFold _) e') -> loop e'-- App (App (App (App NaturalFold (NaturalLit n0)) t) succ') zero -> do- t' <- loop t- if boundedType t' then strict else lazy- where- strict = strictLoop (fromIntegral n0 :: Integer)- lazy = loop ( lazyLoop (fromIntegral n0 :: Integer))-- strictLoop !0 = loop zero- strictLoop !n = App succ' <$> strictLoop (n - 1) >>= loop-- lazyLoop !0 = zero- lazyLoop !n = App succ' (lazyLoop (n - 1))- App NaturalBuild g -> loop (App (App (App g Natural) succ) zero)- where- succ = Lam "n" Natural (NaturalPlus "n" (NaturalLit 1))-- zero = NaturalLit 0- App NaturalIsZero (NaturalLit n) -> pure (BoolLit (n == 0))- App NaturalEven (NaturalLit n) -> pure (BoolLit (even n))- App NaturalOdd (NaturalLit n) -> pure (BoolLit (odd n))- App NaturalToInteger (NaturalLit n) -> pure (IntegerLit (toInteger n))- App NaturalShow (NaturalLit n) ->- pure (TextLit (Chunks [] (Data.Text.pack (show n))))- App (App NaturalSubtract (NaturalLit x)) (NaturalLit y)- | y >= x -> pure (NaturalLit (subtract x y))- | otherwise -> pure (NaturalLit 0)- App (App NaturalSubtract (NaturalLit 0)) y -> pure y- App (App NaturalSubtract _) (NaturalLit 0) -> pure (NaturalLit 0)- App (App NaturalSubtract x) y | Eval.judgmentallyEqual x y -> pure (NaturalLit 0)- App IntegerShow (IntegerLit n)- | 0 <= n -> pure (TextLit (Chunks [] ("+" <> Data.Text.pack (show n))))- | otherwise -> pure (TextLit (Chunks [] (Data.Text.pack (show n))))- -- `(read . show)` is used instead of `fromInteger` because `read` uses the correct rounding rule- App IntegerToDouble (IntegerLit n) -> pure (DoubleLit ((read . show) n))- App DoubleShow (DoubleLit n) ->- pure (TextLit (Chunks [] (Data.Text.pack (show n))))- App (App OptionalBuild _A₀) g ->- loop (App (App (App g optional) just) nothing)- where- optional = App Optional _A₀-- just = Lam "a" _A₀ (Some "a")-- nothing = App None _A₀- App (App ListBuild _A₀) g -> loop (App (App (App g list) cons) nil)- where- _A₁ = shift 1 "a" _A₀-- list = App List _A₀-- cons =- Lam "a" _A₀- (Lam "as"- (App List _A₁)- (ListAppend (ListLit Nothing (pure "a")) "as")- )-- nil = ListLit (Just (App List _A₀)) empty- App (App ListFold t) (ListLit _ xs) -> do- t' <- loop t- let list = Var (V "list" 0)- let lam term =- Lam "list" (Const Type)- (Lam "cons" (Pi "_" t' (Pi "_" list list))- (Lam "nil" list term))- term <- foldrM- (\x acc -> do- x' <- loop x- pure (App (App (Var (V "cons" 0)) x') acc))- (Var (V "nil" 0))- xs- pure (lam term)- App (App (App (App (App ListFold _) (ListLit _ xs)) t) cons) nil -> do- t' <- loop t- if boundedType t' then strict else lazy- where- strict = foldr strictCons strictNil xs- lazy = loop (foldr lazyCons lazyNil xs)-- strictNil = loop nil- lazyNil = nil-- strictCons y ys = do- App (App cons y) <$> ys >>= loop- lazyCons y ys = App (App cons y) ys- App (App ListLength _) (ListLit _ ys) ->- pure (NaturalLit (fromIntegral (Data.Sequence.length ys)))- App (App ListHead t) (ListLit _ ys) -> loop o- where- o = case Data.Sequence.viewl ys of- y :< _ -> Some y- _ -> App None t- App (App ListLast t) (ListLit _ ys) -> loop o- where- o = case Data.Sequence.viewr ys of- _ :> y -> Some y- _ -> App None t- App (App ListIndexed _A₀) (ListLit _ as₀) -> loop (ListLit t as₁)- where- as₁ = Data.Sequence.mapWithIndex adapt as₀-- _A₂ = Record (Dhall.Map.fromList kts)- where- kts = [ ("index", Natural)- , ("value", _A₀)- ]-- t | null as₀ = Just (App List _A₂)- | otherwise = Nothing-- adapt n a_ =- RecordLit (Dhall.Map.fromList kvs)- where- kvs = [ ("index", NaturalLit (fromIntegral n))- , ("value", a_)- ]- App (App ListReverse _) (ListLit t xs) ->- loop (ListLit t (Data.Sequence.reverse xs))-- App (App OptionalFold t0) x0 -> do- t1 <- loop t0- let optional = Var (V "optional" 0)- let lam term = (Lam "optional"- (Const Type)- (Lam "some"- (Pi "_" t1 optional)- (Lam "none" optional term)))- x1 <- loop x0- pure $ case x1 of- App None _ -> lam (Var (V "none" 0))- Some x' -> lam (App (Var (V "some" 0)) x')- _ -> App (App OptionalFold t1) x1-- App TextShow (TextLit (Chunks [] oldText)) ->- loop (TextLit (Chunks [] newText))- where- newText = textShow oldText- _ -> do- res2 <- ctx (App f' a')- case res2 of- Nothing -> pure (App f' a')- Just app' -> loop app'- Let (Binding _ f _ _ _ r) b -> loop b''- where- r' = shift 1 (V f 0) r- b' = subst (V f 0) r' b- b'' = shift (-1) (V f 0) b'- Annot x _ -> loop x- Bool -> pure Bool- BoolLit b -> pure (BoolLit b)- BoolAnd x y -> decide <$> loop x <*> loop y- where- decide (BoolLit True ) r = r- decide (BoolLit False) _ = BoolLit False- decide l (BoolLit True ) = l- decide _ (BoolLit False) = BoolLit False- decide l r- | Eval.judgmentallyEqual l r = l- | otherwise = BoolAnd l r- BoolOr x y -> decide <$> loop x <*> loop y- where- decide (BoolLit False) r = r- decide (BoolLit True ) _ = BoolLit True- decide l (BoolLit False) = l- decide _ (BoolLit True ) = BoolLit True- decide l r- | Eval.judgmentallyEqual l r = l- | otherwise = BoolOr l r- BoolEQ x y -> decide <$> loop x <*> loop y- where- decide (BoolLit True ) r = r- decide l (BoolLit True ) = l- decide l r- | Eval.judgmentallyEqual l r = BoolLit True- | otherwise = BoolEQ l r- BoolNE x y -> decide <$> loop x <*> loop y- where- decide (BoolLit False) r = r- decide l (BoolLit False) = l- decide l r- | Eval.judgmentallyEqual l r = BoolLit False- | otherwise = BoolNE l r- BoolIf bool true false -> decide <$> loop bool <*> loop true <*> loop false- where- decide (BoolLit True ) l _ = l- decide (BoolLit False) _ r = r- decide b (BoolLit True) (BoolLit False) = b- decide b l r- | Eval.judgmentallyEqual l r = l- | otherwise = BoolIf b l r- Natural -> pure Natural- NaturalLit n -> pure (NaturalLit n)- NaturalFold -> pure NaturalFold- NaturalBuild -> pure NaturalBuild- NaturalIsZero -> pure NaturalIsZero- NaturalEven -> pure NaturalEven- NaturalOdd -> pure NaturalOdd- NaturalToInteger -> pure NaturalToInteger- NaturalShow -> pure NaturalShow- NaturalSubtract -> pure NaturalSubtract- NaturalPlus x y -> decide <$> loop x <*> loop y- where- decide (NaturalLit 0) r = r- decide l (NaturalLit 0) = l- decide (NaturalLit m) (NaturalLit n) = NaturalLit (m + n)- decide l r = NaturalPlus l r- NaturalTimes x y -> decide <$> loop x <*> loop y- where- decide (NaturalLit 1) r = r- decide l (NaturalLit 1) = l- decide (NaturalLit 0) _ = NaturalLit 0- decide _ (NaturalLit 0) = NaturalLit 0- decide (NaturalLit m) (NaturalLit n) = NaturalLit (m * n)- decide l r = NaturalTimes l r- Integer -> pure Integer- IntegerLit n -> pure (IntegerLit n)- IntegerShow -> pure IntegerShow- IntegerToDouble -> pure IntegerToDouble- Double -> pure Double- DoubleLit n -> pure (DoubleLit n)- DoubleShow -> pure DoubleShow- Text -> pure Text- TextLit (Chunks xys z) -> do- chunks' <- mconcat <$> chunks- case chunks' of- Chunks [("", x)] "" -> pure x- c -> pure (TextLit c)- where- chunks =- ((++ [Chunks [] z]) . concat) <$> traverse process xys-- process (x, y) = do- y' <- loop y- case y' of- TextLit c -> pure [Chunks [] x, c]- _ -> pure [Chunks [(x, y')] mempty]- TextAppend x y -> loop (TextLit (Chunks [("", x), ("", y)] ""))- TextShow -> pure TextShow- List -> pure List- ListLit t es- | Data.Sequence.null es -> ListLit <$> t' <*> es'- | otherwise -> ListLit Nothing <$> es'- where- t' = traverse loop t- es' = traverse loop es- ListAppend x y -> decide <$> loop x <*> loop y- where- decide (ListLit _ m) r | Data.Sequence.null m = r- decide l (ListLit _ n) | Data.Sequence.null n = l- decide (ListLit t m) (ListLit _ n) = ListLit t (m <> n)- decide l r = ListAppend l r- ListBuild -> pure ListBuild- ListFold -> pure ListFold- ListLength -> pure ListLength- ListHead -> pure ListHead- ListLast -> pure ListLast- ListIndexed -> pure ListIndexed- ListReverse -> pure ListReverse- Optional -> pure Optional- Some a -> Some <$> a'- where- a' = loop a- None -> pure None- OptionalFold -> pure OptionalFold- OptionalBuild -> pure OptionalBuild- Record kts -> Record . Dhall.Map.sort <$> kts'- where- kts' = traverse loop kts- RecordLit kvs -> RecordLit . Dhall.Map.sort <$> kvs'- where- kvs' = traverse loop kvs- Union kts -> Union . Dhall.Map.sort <$> kts'- where- kts' = traverse (traverse loop) kts- Combine x y -> decide <$> loop x <*> loop y- where- decide (RecordLit m) r | Data.Foldable.null m =- r- decide l (RecordLit n) | Data.Foldable.null n =- l- decide (RecordLit m) (RecordLit n) =- RecordLit (Dhall.Map.unionWith decide m n)- decide l r =- Combine l r- CombineTypes x y -> decide <$> loop x <*> loop y- where- decide (Record m) r | Data.Foldable.null m =- r- decide l (Record n) | Data.Foldable.null n =- l- decide (Record m) (Record n) =- Record (Dhall.Map.unionWith decide m n)- decide l r =- CombineTypes l r- Prefer x y -> decide <$> loop x <*> loop y- where- decide (RecordLit m) r | Data.Foldable.null m =- r- decide l (RecordLit n) | Data.Foldable.null n =- l- decide (RecordLit m) (RecordLit n) =- RecordLit (Dhall.Map.union n m)- decide l r | Eval.judgmentallyEqual l r =- l- decide l r =- Prefer l r- Merge x y t -> do- x' <- loop x- y' <- loop y- case x' of- RecordLit kvsX ->- case y' of- Field (Union ktsY) kY ->- case Dhall.Map.lookup kY ktsY of- Just Nothing ->- case Dhall.Map.lookup kY kvsX of- Just vX -> return vX- Nothing -> Merge x' y' <$> t'- _ ->- Merge x' y' <$> t'- App (Field (Union ktsY) kY) vY ->- case Dhall.Map.lookup kY ktsY of- Just (Just _) ->- case Dhall.Map.lookup kY kvsX of- Just vX -> loop (App vX vY)- Nothing -> Merge x' y' <$> t'- _ ->- Merge x' y' <$> t'- _ -> Merge x' y' <$> t'- _ -> Merge x' y' <$> t'- where- t' = traverse loop t- ToMap x t -> do- x' <- loop x- t' <- traverse loop t- case x' of- RecordLit kvsX -> do- let entry (key, value) =- RecordLit- (Dhall.Map.fromList- [ ("mapKey" , TextLit (Chunks [] key))- , ("mapValue", value )- ]- )-- let keyValues = Data.Sequence.fromList (map entry (Dhall.Map.toList kvsX))-- let listType = case t' of- Just _ | null keyValues ->- t'- _ ->- Nothing-- return (ListLit listType keyValues)- _ -> do- return (ToMap x' t')- Field r x -> do- let singletonRecordLit v = RecordLit (Dhall.Map.singleton x v)-- r' <- loop r- case r' of- RecordLit kvs ->- case Dhall.Map.lookup x kvs of- Just v -> pure v- Nothing -> Field <$> (RecordLit <$> traverse loop kvs) <*> pure x- Project r_ _ -> loop (Field r_ x)- Prefer (RecordLit kvs) r_ -> case Dhall.Map.lookup x kvs of- Just v -> pure (Field (Prefer (singletonRecordLit v) r_) x)- Nothing -> loop (Field r_ x)- Prefer l (RecordLit kvs) -> case Dhall.Map.lookup x kvs of- Just v -> pure v- Nothing -> loop (Field l x)- Combine (RecordLit kvs) r_ -> case Dhall.Map.lookup x kvs of- Just v -> pure (Field (Combine (singletonRecordLit v) r_) x)- Nothing -> loop (Field r_ x)- Combine l (RecordLit kvs) -> case Dhall.Map.lookup x kvs of- Just v -> pure (Field (Combine l (singletonRecordLit v)) x)- Nothing -> loop (Field l x)- _ -> pure (Field r' x)- Project x (Left fields)-> do- x' <- loop x- let fieldsSet = Dhall.Set.toSet fields- case x' of- RecordLit kvs ->- pure (RecordLit (Dhall.Map.restrictKeys kvs fieldsSet))- Project y _ ->- loop (Project y (Left fields))- Prefer l (RecordLit rKvs) -> do- let rKs = Dhall.Map.keysSet rKvs- let l' = Project l (Left (Dhall.Set.fromSet (Data.Set.difference fieldsSet rKs)))- let r' = RecordLit (Dhall.Map.restrictKeys rKvs fieldsSet)- loop (Prefer l' r')- _ | null fields -> pure (RecordLit mempty)- | otherwise -> pure (Project x' (Left (Dhall.Set.sort fields)))- Project r (Right e1) -> do- e2 <- loop e1-- case e2 of- Record kts -> do- loop (Project r (Left (Dhall.Set.fromSet (Dhall.Map.keysSet kts))))- _ -> do- r' <- loop r- pure (Project r' (Right e2))- Assert t -> do- t' <- loop t-- pure (Assert t')- Equivalent l r -> do- l' <- loop l- r' <- loop r-- pure (Equivalent l' r')- Note _ e' -> loop e'- ImportAlt l _r -> loop l- Embed a -> pure (Embed a)--textShow :: Text -> Text-textShow text = "\"" <> Data.Text.concatMap f text <> "\""- where- f '"' = "\\\""- f '$' = "\\u0024"- f '\\' = "\\\\"- f '\b' = "\\b"- f '\n' = "\\n"- f '\r' = "\\r"- f '\t' = "\\t"- f '\f' = "\\f"- f c | c <= '\x1F' = Data.Text.pack (Text.Printf.printf "\\u%04x" (Data.Char.ord c))- | otherwise = Data.Text.singleton c---- | Use this to wrap you embedded functions (see `normalizeWith`) to make them--- polymorphic enough to be used.-type NormalizerM m a = forall s. Expr s a -> m (Maybe (Expr s a))--type Normalizer a = NormalizerM Identity a---- | A reified 'Normalizer', which can be stored in structures without--- running into impredicative polymorphism.-newtype ReifiedNormalizer a = ReifiedNormalizer- { getReifiedNormalizer :: Normalizer a }---- | Check if an expression is in a normal form given a context of evaluation.--- Unlike `isNormalized`, this will fully normalize and traverse through the expression.------ It is much more efficient to use `isNormalized`.------ `isNormalizedWith` can fail with an `error` if you check an ill-typed--- expression-isNormalizedWith :: (Eq s, Eq a) => Normalizer a -> Expr s a -> Bool-isNormalizedWith ctx e = e == normalizeWith (Just (ReifiedNormalizer ctx)) e---- | Quickly check if an expression is in normal form------ Given a well-typed expression @e@, @'isNormalized' e@ is equivalent to--- @e == 'normalize' e@.------ Given an ill-typed expression, 'isNormalized' may fail with an error, or--- evaluate to either False or True!-isNormalized :: Eq a => Expr s a -> Bool-isNormalized e0 = loop (denote e0)- where- loop e = case e of- Const _ -> True- Var _ -> True- Lam _ a b -> loop a && loop b- Pi _ a b -> loop a && loop b- App f a -> loop f && loop a && case App f a of- App (Lam _ _ _) _ -> False-- -- build/fold fusion for `List`- App (App ListBuild _) (App (App ListFold _) _) -> False-- -- build/fold fusion for `Natural`- App NaturalBuild (App NaturalFold _) -> False-- -- build/fold fusion for `Optional`- App (App OptionalBuild _) (App (App OptionalFold _) _) -> False-- App (App (App (App NaturalFold (NaturalLit _)) _) _) _ -> False- App NaturalFold (NaturalLit _) -> False- App NaturalBuild _ -> False- App NaturalIsZero (NaturalLit _) -> False- App NaturalEven (NaturalLit _) -> False- App NaturalOdd (NaturalLit _) -> False- App NaturalShow (NaturalLit _) -> False- App (App NaturalSubtract (NaturalLit _)) (NaturalLit _) -> False- App (App NaturalSubtract (NaturalLit 0)) _ -> False- App (App NaturalSubtract _) (NaturalLit 0) -> False- App (App NaturalSubtract x) y -> not (Eval.judgmentallyEqual x y)- App NaturalToInteger (NaturalLit _) -> False- App IntegerShow (IntegerLit _) -> False- App IntegerToDouble (IntegerLit _) -> False- App DoubleShow (DoubleLit _) -> False- App (App OptionalBuild _) _ -> False- App (App ListBuild _) _ -> False- App (App ListFold _) (ListLit _ _) -> False- App (App ListLength _) (ListLit _ _) -> False- App (App ListHead _) (ListLit _ _) -> False- App (App ListLast _) (ListLit _ _) -> False- App (App ListIndexed _) (ListLit _ _) -> False- App (App ListReverse _) (ListLit _ _) -> False- App (App OptionalFold _) (Some _) -> False- App (App OptionalFold _) (App None _) -> False- App TextShow (TextLit (Chunks [] _)) ->- False- _ -> True- Let _ _ -> False- Annot _ _ -> False- Bool -> True- BoolLit _ -> True- BoolAnd x y -> loop x && loop y && decide x y- where- decide (BoolLit _) _ = False- decide _ (BoolLit _) = False- decide l r = not (Eval.judgmentallyEqual l r)- BoolOr x y -> loop x && loop y && decide x y- where- decide (BoolLit _) _ = False- decide _ (BoolLit _) = False- decide l r = not (Eval.judgmentallyEqual l r)- BoolEQ x y -> loop x && loop y && decide x y- where- decide (BoolLit True) _ = False- decide _ (BoolLit True) = False- decide l r = not (Eval.judgmentallyEqual l r)- BoolNE x y -> loop x && loop y && decide x y- where- decide (BoolLit False) _ = False- decide _ (BoolLit False ) = False- decide l r = not (Eval.judgmentallyEqual l r)- BoolIf x y z ->- loop x && loop y && loop z && decide x y z- where- decide (BoolLit _) _ _ = False- decide _ (BoolLit True) (BoolLit False) = False- decide _ l r = not (Eval.judgmentallyEqual l r)- Natural -> True- NaturalLit _ -> True- NaturalFold -> True- NaturalBuild -> True- NaturalIsZero -> True- NaturalEven -> True- NaturalOdd -> True- NaturalShow -> True- NaturalSubtract -> True- NaturalToInteger -> True- NaturalPlus x y -> loop x && loop y && decide x y- where- decide (NaturalLit 0) _ = False- decide _ (NaturalLit 0) = False- decide (NaturalLit _) (NaturalLit _) = False- decide _ _ = True- NaturalTimes x y -> loop x && loop 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- IntegerToDouble -> True- Double -> True- DoubleLit _ -> True- DoubleShow -> True- Text -> True- TextLit (Chunks [("", _)] "") -> False- TextLit (Chunks xys _) -> all (all check) xys- where- check y = loop y && case y of- TextLit _ -> False- _ -> True- TextAppend _ _ -> False- TextShow -> True- List -> True- ListLit t es -> all loop t && all loop es- ListAppend x y -> loop x && loop 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- ListHead -> True- ListLast -> True- ListIndexed -> True- ListReverse -> True- Optional -> True- Some a -> loop a- None -> True- OptionalFold -> True- OptionalBuild -> True- Record kts -> Dhall.Map.isSorted kts && all loop kts- RecordLit kvs -> Dhall.Map.isSorted kvs && all loop kvs- Union kts -> Dhall.Map.isSorted kts && all (all loop) kts- Combine x y -> loop x && loop y && decide x y- where- decide (RecordLit m) _ | Data.Foldable.null m = False- decide _ (RecordLit n) | Data.Foldable.null n = False- decide (RecordLit _) (RecordLit _) = False- decide _ _ = True- CombineTypes x y -> loop x && loop y && decide x y- where- decide (Record m) _ | Data.Foldable.null m = False- decide _ (Record n) | Data.Foldable.null n = False- decide (Record _) (Record _) = False- decide _ _ = True- Prefer x y -> loop x && loop y && decide x y- where- decide (RecordLit m) _ | Data.Foldable.null m = False- decide _ (RecordLit n) | Data.Foldable.null n = False- decide (RecordLit _) (RecordLit _) = False- decide l r = not (Eval.judgmentallyEqual l r)- Merge x y t -> loop x && loop y && all loop t- ToMap x t -> case x of- RecordLit _ -> False- _ -> loop x && all loop t- Field r k -> case r of- RecordLit _ -> False- Project _ _ -> False- Prefer (RecordLit m) _ -> Dhall.Map.keys m == [k] && loop r- Prefer _ (RecordLit _) -> False- Combine (RecordLit m) _ -> Dhall.Map.keys m == [k] && loop r- Combine _ (RecordLit m) -> Dhall.Map.keys m == [k] && loop r- _ -> loop r- Project r p -> loop r &&- case p of- Left s -> case r of- RecordLit _ -> False- Project _ _ -> False- Prefer _ (RecordLit _) -> False- _ -> not (Dhall.Set.null s) && Dhall.Set.isSorted s- Right e' -> case e' of- Record _ -> False- _ -> loop e'- Assert t -> loop t- Equivalent l r -> loop l && loop r- Note _ e' -> loop e'- ImportAlt _ _ -> False- Embed _ -> True--{-| Detect if the given variable is free within the given expression-->>> "x" `freeIn` "x"-True->>> "x" `freeIn` "y"-False->>> "x" `freeIn` Lam "x" (Const Type) "x"-False--}-freeIn :: Eq a => Var -> Expr s a -> Bool-variable@(V var i) `freeIn` expression =- Dhall.Core.subst variable (Var (V var (i + 1))) strippedExpression- /= strippedExpression- where- denote' :: Expr t b -> Expr () b- denote' = denote-- strippedExpression = denote' expression--_ERROR :: String-_ERROR = "\ESC[1;31mError\ESC[0m"--{-| Utility function used to throw internal errors that should never happen- (in theory) but that are not enforced by the type system--}-internalError :: Data.Text.Text -> forall b . b-internalError text = error (unlines- [ _ERROR <> ": Compiler bug "- , " "- , "Explanation: This error message means that there is a bug in the Dhall compiler."- , "You didn't do anything wrong, but if you would like to see this problem fixed "- , "then you should report the bug at: "- , " "- , "https://github.com/dhall-lang/dhall-haskell/issues "- , " "- , "Please include the following text in your bug report: "- , " "- , "``` "- , Data.Text.unpack text <> " "- , "``` "- ] )---- | The set of reserved identifiers for the Dhall language-reservedIdentifiers :: HashSet Text-reservedIdentifiers =- Data.HashSet.fromList- [ "let"- , "in"- , "Type"- , "Kind"- , "Sort"- , "forall"- , "Bool"- , "True"- , "False"- , "merge"- , "toMap"- , "if"- , "then"- , "else"- , "as"- , "using"- , "Natural"- , "Natural/fold"- , "Natural/build"- , "Natural/isZero"- , "Natural/even"- , "Natural/odd"- , "Natural/toInteger"- , "Natural/show"- , "Natural/subtract"- , "Integer"- , "Integer/show"- , "Integer/toDouble"- , "Double"- , "Double/show"- , "Text"- , "Text/show"- , "List"- , "List/build"- , "List/fold"- , "List/length"- , "List/head"- , "List/last"- , "List/indexed"- , "List/reverse"- , "Optional"- , "Some"- , "None"- , "Optional/build"- , "Optional/fold"- , "NaN"- , "Infinity"- ]----- | A traversal over the immediate sub-expressions of an expression.-subExpressions :: Applicative f => (Expr s a -> f (Expr s a)) -> Expr s a -> f (Expr s a)-subExpressions _ (Const c) = pure (Const c)-subExpressions _ (Var v) = pure (Var v)-subExpressions f (Lam a b c) = Lam a <$> f b <*> f c-subExpressions f (Pi a b c) = Pi a <$> f b <*> f c-subExpressions f (App a b) = App <$> f a <*> f b-subExpressions f (Let a b) = Let <$> bindingExprs f a <*> f b-subExpressions f (Annot a b) = Annot <$> f a <*> f b-subExpressions _ Bool = pure Bool-subExpressions _ (BoolLit b) = pure (BoolLit b)-subExpressions f (BoolAnd a b) = BoolAnd <$> f a <*> f b-subExpressions f (BoolOr a b) = BoolOr <$> f a <*> f b-subExpressions f (BoolEQ a b) = BoolEQ <$> f a <*> f b-subExpressions f (BoolNE a b) = BoolNE <$> f a <*> f b-subExpressions f (BoolIf a b c) = BoolIf <$> f a <*> f b <*> f c-subExpressions _ Natural = pure Natural-subExpressions _ (NaturalLit n) = pure (NaturalLit n)-subExpressions _ NaturalFold = pure NaturalFold-subExpressions _ NaturalBuild = pure NaturalBuild-subExpressions _ NaturalIsZero = pure NaturalIsZero-subExpressions _ NaturalEven = pure NaturalEven-subExpressions _ NaturalOdd = pure NaturalOdd-subExpressions _ NaturalToInteger = pure NaturalToInteger-subExpressions _ NaturalShow = pure NaturalShow-subExpressions _ NaturalSubtract = pure NaturalSubtract-subExpressions f (NaturalPlus a b) = NaturalPlus <$> f a <*> f b-subExpressions f (NaturalTimes a b) = NaturalTimes <$> f a <*> f b-subExpressions _ Integer = pure Integer-subExpressions _ (IntegerLit n) = pure (IntegerLit n)-subExpressions _ IntegerShow = pure IntegerShow-subExpressions _ IntegerToDouble = pure IntegerToDouble-subExpressions _ Double = pure Double-subExpressions _ (DoubleLit n) = pure (DoubleLit n)-subExpressions _ DoubleShow = pure DoubleShow-subExpressions _ Text = pure Text-subExpressions f (TextLit chunks) =- TextLit <$> chunkExprs f chunks-subExpressions f (TextAppend a b) = TextAppend <$> f a <*> f b-subExpressions _ TextShow = pure TextShow-subExpressions _ List = pure List-subExpressions f (ListLit a b) = ListLit <$> traverse f a <*> traverse f b-subExpressions f (ListAppend a b) = ListAppend <$> f a <*> f b-subExpressions _ ListBuild = pure ListBuild-subExpressions _ ListFold = pure ListFold-subExpressions _ ListLength = pure ListLength-subExpressions _ ListHead = pure ListHead-subExpressions _ ListLast = pure ListLast-subExpressions _ ListIndexed = pure ListIndexed-subExpressions _ ListReverse = pure ListReverse-subExpressions _ Optional = pure Optional-subExpressions f (Some a) = Some <$> f a-subExpressions _ None = pure None-subExpressions _ OptionalFold = pure OptionalFold-subExpressions _ OptionalBuild = pure OptionalBuild-subExpressions f (Record a) = Record <$> traverse f a-subExpressions f ( RecordLit a ) = RecordLit <$> traverse f a-subExpressions f (Union a) = Union <$> traverse (traverse f) a-subExpressions f (Combine a b) = Combine <$> f a <*> f b-subExpressions f (CombineTypes a b) = CombineTypes <$> f a <*> f b-subExpressions f (Prefer a b) = Prefer <$> f a <*> f b-subExpressions f (Merge a b t) = Merge <$> f a <*> f b <*> traverse f t-subExpressions f (ToMap a t) = ToMap <$> f a <*> traverse f t-subExpressions f (Field a b) = Field <$> f a <*> pure b-subExpressions f (Project a b) = Project <$> f a <*> traverse f b-subExpressions f (Assert a) = Assert <$> f a-subExpressions f (Equivalent a b) = Equivalent <$> f a <*> f b-subExpressions f (Note a b) = Note a <$> f b-subExpressions f (ImportAlt l r) = ImportAlt <$> f l <*> f r-subExpressions _ (Embed a) = pure (Embed a)--{-| Utility used to implement the @--censor@ flag, by:-- * Replacing all `Src` text with spaces- * Replacing all `Text` literals inside type errors with spaces--}-censorExpression :: Expr Src a -> Expr Src a-censorExpression (TextLit chunks) = TextLit (censorChunks chunks)-censorExpression (Note src e) = Note (censorSrc src) (censorExpression e)-censorExpression e = over subExpressions censorExpression e--censorChunks :: Chunks Src a -> Chunks Src a-censorChunks (Chunks xys z) = Chunks xys' z'- where- z' = censorText z-- xys' = [ (censorText x, censorExpression y) | (x, y) <- xys ]---- | Utility used to censor `Text` by replacing all characters with a space-censorText :: Text -> Text-censorText = Data.Text.map (\_ -> ' ')--censorSrc :: Src -> Src-censorSrc (Src { srcText = oldText, .. }) = Src { srcText = newText, .. }- where- newText = censorText oldText---- | A traversal over the immediate sub-expressions in 'Chunks'.-chunkExprs- :: Applicative f- => (Expr s a -> f (Expr t b))- -> Chunks s a -> f (Chunks t b)-chunkExprs f (Chunks chunks final) =- flip Chunks final <$> traverse (traverse f) chunks--{-| Returns `True` if the given `Char` is valid within an unquoted path- component-- This is exported for reuse within the @"Dhall.Parser.Token"@ module--}-pathCharacter :: Char -> Bool-pathCharacter c =- '\x21' == c- || ('\x24' <= c && c <= '\x27')- || ('\x2A' <= c && c <= '\x2B')- || ('\x2D' <= c && c <= '\x2E')- || ('\x30' <= c && c <= '\x3B')- || c == '\x3D'- || ('\x40' <= c && c <= '\x5A')- || ('\x5E' <= c && c <= '\x7A')- || c == '\x7C'- || c == '\x7E'--prettyPathComponent :: Text -> Doc ann-prettyPathComponent text- | Data.Text.all pathCharacter text =- "/" <> Pretty.pretty text- | otherwise =- "/\"" <> Pretty.pretty text <> "\""--prettyURIComponent :: Text -> Doc ann-prettyURIComponent text =- Pretty.pretty $ URI.normalizeCase $ URI.normalizeEscape $ "/" <> Data.Text.unpack text--{-| Convenience utility for converting `Either`-based exceptions to `IO`-based- exceptions--}-throws :: (Exception e, MonadIO io) => Either e a -> io a-throws (Left e) = liftIO (Control.Exception.throwIO e)-throws (Right r) = return r--{--Instead of converting explicitly between 'Expr's and 'MultiLet', it might-be nicer to use a pattern synonym:--> pattern MultiLet' :: NonEmpty (Binding s a) -> Expr s a -> Expr s a-> pattern MultiLet' as b <- (multiLetFromExpr -> Just (MultiLet as b)) where-> MultiLet' as b = wrapInLets as b->-> multiLetFromExpr :: Expr s a -> Maybe (MultiLet s a)-> multiLetFromExpr = \case-> Let x mA a b -> Just (multiLet x mA a b)-> _ -> Nothing--This works in principle, but GHC as of v8.8.1 doesn't handle it well:-https://gitlab.haskell.org/ghc/ghc/issues/17096--This should be fixed by GHC-8.10, so it might be worth revisiting then.--}--{-| Generate a 'MultiLet' from the contents of a 'Let'.-- In the resulting @'MultiLet' bs e@, @e@ is guaranteed not to be a 'Let',- but it might be a @('Note' … ('Let' …))@.-- Given parser output, 'multiLet' consolidates @let@s that formed a- let-block in the original source.--}-multiLet :: Binding s a -> Expr s a -> MultiLet s a-multiLet b0 = \case- Let b1 e1 ->- let MultiLet bs e = multiLet b1 e1- in MultiLet (Data.List.NonEmpty.cons b0 bs) e- e -> MultiLet (b0 :| []) e--{-| Wrap let-'Binding's around an 'Expr'.--'wrapInLets' can be understood as an inverse for 'multiLet':--> let MultiLet bs e1 = multiLet b e0->-> wrapInLets bs e1 == Let b e0--}-wrapInLets :: Foldable f => f (Binding s a) -> Expr s a -> Expr s a-wrapInLets bs e = foldr Let e bs--data MultiLet s a = MultiLet (NonEmpty (Binding s a)) (Expr s a)--{- | Record the binding part of a @let@ expression.--For example,-> let {- A -} x {- B -} : {- C -} Bool = {- D -} True in x-will be instantiated as follows:--* @bindingSrc0@ corresponds to the @A@ comment.-* @variable@ is @"x"@-* @bindingSrc1@ corresponds to the @B@ comment.-* @annotation@ is 'Just' a pair, corresponding to the @C@ comment and @Bool@.-* @bindingSrc2@ corresponds to the @D@ comment.-* @value@ corresponds to @True@.--}-data Binding s a = Binding- { bindingSrc0 :: Maybe s- , variable :: Text- , bindingSrc1 :: Maybe s- , annotation :: Maybe (Maybe s, Expr s a)- , bindingSrc2 :: Maybe s- , value :: Expr s a- } deriving (Data, Eq, Foldable, Functor, Generic, NFData, Ord, Show, Traversable)--instance Bifunctor Binding where- first k (Binding src0 a src1 b src2 c) =- Binding (fmap k src0) a (fmap k src1) (fmap adapt0 b) (fmap k src2) (first k c)- where- adapt0 (src3, d) = (fmap k src3, first k d)-- second = fmap--{-| Traverse over the immediate 'Expr' children in a 'Binding'.--}-bindingExprs- :: (Applicative f)- => (Expr s a -> f (Expr s b))- -> Binding s a -> f (Binding s b)-bindingExprs f (Binding s0 n s1 t s2 v) =- Binding- <$> pure s0- <*> pure n- <*> pure s1- <*> traverse (traverse f) t- <*> pure s2- <*> f v--{-| Construct a 'Binding' with no source information and no type annotation.--}-makeBinding :: Text -> Expr s a -> Binding s a-makeBinding name = Binding Nothing name Nothing Nothing Nothing+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}++{-| This module contains the core calculus for the Dhall language.++ Dhall is essentially a fork of the @morte@ compiler but with more built-in+ functionality, better error messages, and Haskell integration+-}++module Dhall.Core (+ -- * Syntax+ Const(..)+ , Directory(..)+ , File(..)+ , FilePrefix(..)+ , Import(..)+ , ImportHashed(..)+ , ImportMode(..)+ , ImportType(..)+ , URL(..)+ , Scheme(..)+ , DhallDouble(..)+ , Var(..)+ , Binding(..)+ , makeBinding+ , Chunks(..)+ , Expr(..)++ -- * Normalization+ , alphaNormalize+ , normalize+ , normalizeWith+ , normalizeWithM+ , Normalizer+ , NormalizerM+ , ReifiedNormalizer (..)+ , judgmentallyEqual+ , subst+ , shift+ , isNormalized+ , isNormalizedWith+ , denote+ , renote+ , shallowDenote+ , freeIn++ -- * Pretty-printing+ , pretty++ -- * Optics+ , subExpressions+ , chunkExprs+ , bindingExprs++ -- * Let-blocks+ , multiLet+ , wrapInLets+ , MultiLet(..)++ -- * Miscellaneous+ , internalError+ , reservedIdentifiers+ , escapeText+ , pathCharacter+ , throws+ , Eval.textShow+ , censorExpression+ , censorText+ ) where++import Control.Applicative (empty)+import Control.Exception (Exception)+import Control.Monad.IO.Class (MonadIO(..))+import Data.Foldable+import Data.Functor.Identity (Identity(..))+import Data.Semigroup (Semigroup(..))+import Data.Sequence (ViewL(..), ViewR(..))+import Data.Text (Text)+import Data.Text.Prettyprint.Doc (Pretty)+import Data.Traversable+import Dhall.Src (Src(..))+import Dhall.Syntax+import Dhall.Pretty.Internal+import Instances.TH.Lift ()+import Lens.Family (over)+import Prelude hiding (succ)++import qualified Control.Exception+import qualified Dhall.Eval as Eval+import qualified Data.Sequence+import qualified Data.Set+import qualified Data.Text+import qualified Dhall.Map+import qualified Dhall.Set++{-| Returns `True` if two expressions are α-equivalent and β-equivalent and+ `False` otherwise++ `judgmentallyEqual` can fail with an `error` if you compare ill-typed+ expressions+-}+judgmentallyEqual :: Eq a => Expr s a -> Expr t a -> Bool+judgmentallyEqual = Eval.judgmentallyEqual+{-# INLINE judgmentallyEqual #-}++{-| `shift` is used by both normalization and type-checking to avoid variable+ capture by shifting variable indices++ For example, suppose that you were to normalize the following expression:++> λ(a : Type) → λ(x : a) → (λ(y : a) → λ(x : a) → y) x++ If you were to substitute @y@ with @x@ without shifting any variable+ indices, then you would get the following incorrect result:++> λ(a : Type) → λ(x : a) → λ(x : a) → x -- Incorrect normalized form++ In order to substitute @x@ in place of @y@ we need to `shift` @x@ by @1@ in+ order to avoid being misinterpreted as the @x@ bound by the innermost+ lambda. If we perform that `shift` then we get the correct result:++> λ(a : Type) → λ(x : a) → λ(x : a) → x@1++ As a more worked example, suppose that you were to normalize the following+ expression:++> λ(a : Type)+> → λ(f : a → a → a)+> → λ(x : a)+> → λ(x : a)+> → (λ(x : a) → f x x@1) x@1++ The correct normalized result would be:++> λ(a : Type)+> → λ(f : a → a → a)+> → λ(x : a)+> → λ(x : a)+> → f x@1 x++ The above example illustrates how we need to both increase and decrease+ variable indices as part of substitution:++ * We need to increase the index of the outer @x\@1@ to @x\@2@ before we+ substitute it into the body of the innermost lambda expression in order+ to avoid variable capture. This substitution changes the body of the+ lambda expression to @(f x\@2 x\@1)@++ * We then remove the innermost lambda and therefore decrease the indices of+ both @x@s in @(f x\@2 x\@1)@ to @(f x\@1 x)@ in order to reflect that one+ less @x@ variable is now bound within that scope++ Formally, @(shift d (V x n) e)@ modifies the expression @e@ by adding @d@ to+ the indices of all variables named @x@ whose indices are greater than+ @(n + m)@, where @m@ is the number of bound variables of the same name+ within that scope++ In practice, @d@ is always @1@ or @-1@ because we either:++ * increment variables by @1@ to avoid variable capture during substitution+ * decrement variables by @1@ when deleting lambdas after substitution++ @n@ starts off at @0@ when substitution begins and increments every time we+ descend into a lambda or let expression that binds a variable of the same+ name in order to avoid shifting the bound variables by mistake.+-}+shift :: Int -> Var -> Expr s a -> Expr s a+shift _ _ (Const a) = Const a+shift d (V x n) (Var (V x' n')) = Var (V x' n'')+ where+ n'' = if x == x' && n <= n' then n' + d else n'+shift d (V x n) (Lam x' _A b) = Lam x' _A' b'+ where+ _A' = shift d (V x n ) _A+ b' = shift d (V x n') b+ where+ n' = if x == x' then n + 1 else n+shift d (V x n) (Pi x' _A _B) = Pi x' _A' _B'+ where+ _A' = shift d (V x n ) _A+ _B' = shift d (V x n') _B+ where+ n' = if x == x' then n + 1 else n+shift d v (App f a) = App f' a'+ where+ f' = shift d v f+ a' = shift d v a+shift d (V x n) (Let (Binding src0 f src1 mt src2 r) e) =+ Let (Binding src0 f src1 mt' src2 r') e'+ where+ e' = shift d (V x n') e+ where+ n' = if x == f then n + 1 else n++ mt' = fmap (fmap (shift d (V x n))) mt+ r' = shift d (V x n) r+shift d v (Annot a b) = Annot a' b'+ where+ a' = shift d v a+ b' = shift d v b+shift _ _ Bool = Bool+shift _ _ (BoolLit a) = BoolLit a+shift d v (BoolAnd a b) = BoolAnd a' b'+ where+ a' = shift d v a+ b' = shift d v b+shift d v (BoolOr a b) = BoolOr a' b'+ where+ a' = shift d v a+ b' = shift d v b+shift d v (BoolEQ a b) = BoolEQ a' b'+ where+ a' = shift d v a+ b' = shift d v b+shift d v (BoolNE a b) = BoolNE a' b'+ where+ a' = shift d v a+ b' = shift d v b+shift d v (BoolIf a b c) = BoolIf a' b' c'+ where+ a' = shift d v a+ b' = shift d v b+ c' = shift d v c+shift _ _ Natural = Natural+shift _ _ (NaturalLit a) = NaturalLit a+shift _ _ NaturalFold = NaturalFold+shift _ _ NaturalBuild = NaturalBuild+shift _ _ NaturalIsZero = NaturalIsZero+shift _ _ NaturalEven = NaturalEven+shift _ _ NaturalOdd = NaturalOdd+shift _ _ NaturalToInteger = NaturalToInteger+shift _ _ NaturalShow = NaturalShow+shift _ _ NaturalSubtract = NaturalSubtract+shift d v (NaturalPlus a b) = NaturalPlus a' b'+ where+ a' = shift d v a+ b' = shift d v b+shift d v (NaturalTimes a b) = NaturalTimes a' b'+ where+ a' = shift d v a+ b' = shift d v b+shift _ _ Integer = Integer+shift _ _ (IntegerLit a) = IntegerLit a+shift _ _ IntegerShow = IntegerShow+shift _ _ IntegerToDouble = IntegerToDouble+shift _ _ Double = Double+shift _ _ (DoubleLit a) = DoubleLit a+shift _ _ DoubleShow = DoubleShow+shift _ _ Text = Text+shift d v (TextLit (Chunks a b)) = TextLit (Chunks a' b)+ where+ a' = fmap (fmap (shift d v)) a+shift d v (TextAppend a b) = TextAppend a' b'+ where+ a' = shift d v a+ b' = shift d v b+shift _ _ TextShow = TextShow+shift _ _ List = List+shift d v (ListLit a b) = ListLit a' b'+ where+ a' = fmap (shift d v) a+ b' = fmap (shift d v) b+shift _ _ ListBuild = ListBuild+shift d v (ListAppend a b) = ListAppend a' b'+ where+ a' = shift d v a+ b' = shift d v b+shift _ _ ListFold = ListFold+shift _ _ ListLength = ListLength+shift _ _ ListHead = ListHead+shift _ _ ListLast = ListLast+shift _ _ ListIndexed = ListIndexed+shift _ _ ListReverse = ListReverse+shift _ _ Optional = Optional+shift d v (Some a) = Some a'+ where+ a' = shift d v a+shift _ _ None = None+shift _ _ OptionalFold = OptionalFold+shift _ _ OptionalBuild = OptionalBuild+shift d v (Record a) = Record a'+ where+ a' = fmap (shift d v) a+shift d v (RecordLit a) = RecordLit a'+ where+ a' = fmap (shift d v) a+shift d v (Union a) = Union a'+ where+ a' = fmap (fmap (shift d v)) a+shift d v (Combine a b) = Combine a' b'+ where+ a' = shift d v a+ b' = shift d v b+shift d v (CombineTypes a b) = CombineTypes a' b'+ where+ a' = shift d v a+ b' = shift d v b+shift d v (Prefer a b) = Prefer a' b'+ where+ a' = shift d v a+ b' = shift d v b+shift d v (RecordCompletion a b) = RecordCompletion a' b'+ where+ a' = shift d v a+ b' = shift d v b+shift d v (Merge a b c) = Merge a' b' c'+ where+ a' = shift d v a+ b' = shift d v b+ c' = fmap (shift d v) c+shift d v (ToMap a b) = ToMap a' b'+ where+ a' = shift d v a+ b' = fmap (shift d v) b+shift d v (Field a b) = Field a' b+ where+ a' = shift d v a+shift d v (Assert a) = Assert a'+ where+ a' = shift d v a+shift d v (Equivalent a b) = Equivalent a' b'+ where+ a' = shift d v a+ b' = shift d v b+shift d v (Project a b) = Project a' b'+ where+ a' = shift d v a+ b' = fmap (shift d v) b+shift d v (Note a b) = Note a b'+ where+ b' = shift d v b+shift d v (ImportAlt a b) = ImportAlt a' b'+ where+ a' = shift d v a+ b' = shift d v b+-- The Dhall compiler enforces that all embedded values are closed expressions+-- and `shift` does nothing to a closed expression+shift _ _ (Embed p) = Embed p++{-| Substitute all occurrences of a variable with an expression++> subst x C B ~ B[x := C]+-}+subst :: Var -> Expr s a -> Expr s a -> Expr s a+subst _ _ (Const a) = Const a+subst (V x n) e (Lam y _A b) = Lam y _A' b'+ where+ _A' = subst (V x n ) e _A+ b' = subst (V x n') (shift 1 (V y 0) e) b+ n' = if x == y then n + 1 else n+subst (V x n) e (Pi y _A _B) = Pi y _A' _B'+ where+ _A' = subst (V x n ) e _A+ _B' = subst (V x n') (shift 1 (V y 0) e) _B+ n' = if x == y then n + 1 else n+subst v e (App f a) = App f' a'+ where+ f' = subst v e f+ a' = subst v e a+subst v e (Var v') = if v == v' then e else Var v'+subst (V x n) e (Let (Binding src0 f src1 mt src2 r) b) =+ Let (Binding src0 f src1 mt' src2 r') b'+ where+ b' = subst (V x n') (shift 1 (V f 0) e) b+ where+ n' = if x == f then n + 1 else n++ mt' = fmap (fmap (subst (V x n) e)) mt+ r' = subst (V x n) e r+subst x e (Annot a b) = Annot a' b'+ where+ a' = subst x e a+ b' = subst x e b+subst _ _ Bool = Bool+subst _ _ (BoolLit a) = BoolLit a+subst x e (BoolAnd a b) = BoolAnd a' b'+ where+ a' = subst x e a+ b' = subst x e b+subst x e (BoolOr a b) = BoolOr a' b'+ where+ a' = subst x e a+ b' = subst x e b+subst x e (BoolEQ a b) = BoolEQ a' b'+ where+ a' = subst x e a+ b' = subst x e b+subst x e (BoolNE a b) = BoolNE a' b'+ where+ a' = subst x e a+ b' = subst x e b+subst x e (BoolIf a b c) = BoolIf a' b' c'+ where+ a' = subst x e a+ b' = subst x e b+ c' = subst x e c+subst _ _ Natural = Natural+subst _ _ (NaturalLit a) = NaturalLit a+subst _ _ NaturalFold = NaturalFold+subst _ _ NaturalBuild = NaturalBuild+subst _ _ NaturalIsZero = NaturalIsZero+subst _ _ NaturalEven = NaturalEven+subst _ _ NaturalOdd = NaturalOdd+subst _ _ NaturalToInteger = NaturalToInteger+subst _ _ NaturalShow = NaturalShow+subst _ _ NaturalSubtract = NaturalSubtract+subst x e (NaturalPlus a b) = NaturalPlus a' b'+ where+ a' = subst x e a+ b' = subst x e b+subst x e (NaturalTimes a b) = NaturalTimes a' b'+ where+ a' = subst x e a+ b' = subst x e b+subst _ _ Integer = Integer+subst _ _ (IntegerLit a) = IntegerLit a+subst _ _ IntegerShow = IntegerShow+subst _ _ IntegerToDouble = IntegerToDouble+subst _ _ Double = Double+subst _ _ (DoubleLit a) = DoubleLit a+subst _ _ DoubleShow = DoubleShow+subst _ _ Text = Text+subst x e (TextLit (Chunks a b)) = TextLit (Chunks a' b)+ where+ a' = fmap (fmap (subst x e)) a+subst x e (TextAppend a b) = TextAppend a' b'+ where+ a' = subst x e a+ b' = subst x e b+subst _ _ TextShow = TextShow+subst _ _ List = List+subst x e (ListLit a b) = ListLit a' b'+ where+ a' = fmap (subst x e) a+ b' = fmap (subst x e) b+subst x e (ListAppend a b) = ListAppend a' b'+ where+ a' = subst x e a+ b' = subst x e b+subst _ _ ListBuild = ListBuild+subst _ _ ListFold = ListFold+subst _ _ ListLength = ListLength+subst _ _ ListHead = ListHead+subst _ _ ListLast = ListLast+subst _ _ ListIndexed = ListIndexed+subst _ _ ListReverse = ListReverse+subst _ _ Optional = Optional+subst x e (Some a) = Some a'+ where+ a' = subst x e a+subst _ _ None = None+subst _ _ OptionalFold = OptionalFold+subst _ _ OptionalBuild = OptionalBuild+subst x e (Record kts) = Record kts'+ where+ kts' = fmap (subst x e) kts+subst x e (RecordLit kvs) = RecordLit kvs'+ where+ kvs' = fmap (subst x e) kvs+subst x e (Union kts) = Union kts'+ where+ kts' = fmap (fmap (subst x e)) kts+subst x e (Combine a b) = Combine a' b'+ where+ a' = subst x e a+ b' = subst x e b+subst x e (CombineTypes a b) = CombineTypes a' b'+ where+ a' = subst x e a+ b' = subst x e b+subst x e (Prefer a b) = Prefer a' b'+ where+ a' = subst x e a+ b' = subst x e b+subst x e (RecordCompletion a b) = RecordCompletion a' b'+ where+ a' = subst x e a+ b' = subst x e b+subst x e (Merge a b c) = Merge a' b' c'+ where+ a' = subst x e a+ b' = subst x e b+ c' = fmap (subst x e) c+subst x e (ToMap a b) = ToMap a' b'+ where+ a' = subst x e a+ b' = fmap (subst x e) b+subst x e (Field a b) = Field a' b+ where+ a' = subst x e a+subst x e (Project a b) = Project a' b'+ where+ a' = subst x e a+ b' = fmap (subst x e) b+subst x e (Assert a) = Assert a'+ where+ a' = subst x e a+subst x e (Equivalent a b) = Equivalent a' b'+ where+ a' = subst x e a+ b' = subst x e b+subst x e (Note a b) = Note a b'+ where+ b' = subst x e b+subst x e (ImportAlt a b) = ImportAlt a' b'+ where+ a' = subst x e a+ b' = subst x e b+-- The Dhall compiler enforces that all embedded values are closed expressions+-- and `subst` does nothing to a closed expression+subst _ _ (Embed p) = Embed p++{-| This function is used to determine whether folds like @Natural/fold@ or+ @List/fold@ should be lazy or strict in their accumulator based on the type+ of the accumulator++ If this function returns `True`, then they will be strict in their+ accumulator since we can guarantee an upper bound on the amount of work to+ normalize the accumulator on each step of the loop. If this function+ returns `False` then they will be lazy in their accumulator and only+ normalize the final result at the end of the fold+-}+boundedType :: Expr s a -> Bool+boundedType Bool = True+boundedType Natural = True+boundedType Integer = True+boundedType Double = True+boundedType Text = True+boundedType (App List _) = False+boundedType (App Optional t) = boundedType t+boundedType (Record kvs) = all boundedType kvs+boundedType (Union kvs) = all (all boundedType) kvs+boundedType _ = False++{-| α-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 = Eval.alphaNormalize+{-# INLINE alphaNormalize #-}++{-| Reduce an expression to its normal form, performing beta reduction++ `normalize` does not type-check the expression. You may want to type-check+ expressions before normalizing them since normalization can convert an+ ill-typed expression into a well-typed expression.++ `normalize` can also fail with `error` if you normalize an ill-typed+ expression+-}+normalize :: Eq a => Expr s a -> Expr t a+normalize = Eval.normalize+{-# INLINE normalize #-}++{-| Reduce an expression to its normal form, performing beta reduction and applying+ any custom definitions.++ `normalizeWith` is designed to be used with function `typeWith`. The `typeWith`+ function allows typing of Dhall functions in a custom typing context whereas+ `normalizeWith` allows evaluating Dhall expressions in a custom context.++ To be more precise `normalizeWith` applies the given normalizer when it finds an+ application term that it cannot reduce by other means.++ Note that the context used in normalization will determine the properties of normalization.+ That is, if the functions in custom context are not total then the Dhall language, evaluated+ with those functions is not total either.++ `normalizeWith` can fail with an `error` if you normalize an ill-typed+ expression+-}+normalizeWith :: Eq a => Maybe (ReifiedNormalizer a) -> Expr s a -> Expr t a+normalizeWith (Just ctx) t = runIdentity (normalizeWithM (getReifiedNormalizer ctx) t)+normalizeWith _ t = Eval.normalize t++{-| This function generalizes `normalizeWith` by allowing the custom normalizer+ to use an arbitrary `Monad`++ `normalizeWithM` can fail with an `error` if you normalize an ill-typed+ expression+-}+normalizeWithM+ :: (Monad m, Eq a) => NormalizerM m a -> Expr s a -> m (Expr t a)+normalizeWithM ctx e0 = loop (denote e0)+ where+ loop e = case e of+ Const k -> pure (Const k)+ Var v -> pure (Var v)+ Lam x _A b -> Lam x <$> _A' <*> b'+ where+ _A' = loop _A+ b' = loop b+ Pi x _A _B -> Pi x <$> _A' <*> _B'+ where+ _A' = loop _A+ _B' = loop _B+ App f a -> do+ res <- ctx (App f a)+ case res of+ Just e1 -> loop e1+ Nothing -> do+ f' <- loop f+ a' <- loop a+ case f' of+ Lam x _A b₀ -> do++ let a₂ = shift 1 (V x 0) a'+ let b₁ = subst (V x 0) a₂ b₀+ let b₂ = shift (-1) (V x 0) b₁++ loop b₂+ _ -> do+ case App f' a' of+ -- build/fold fusion for `List`+ App (App ListBuild _) (App (App ListFold _) e') -> loop e'++ App NaturalFold (NaturalLit n) -> do+ let natural = Var (V "natural" 0)+ let go 0 x = x+ go n' x = go (n'-1) (App (Var (V "succ" 0)) x)+ let n' = go n (Var (V "zero" 0))+ pure+ (Lam "natural"+ (Const Type)+ (Lam "succ"+ (Pi "_" natural natural)+ (Lam "zero"+ natural+ n')))++ -- build/fold fusion for `Natural`+ App NaturalBuild (App NaturalFold e') -> loop e'++ -- build/fold fusion for `Optional`+ App (App OptionalBuild _) (App (App OptionalFold _) e') -> loop e'++ App (App (App (App NaturalFold (NaturalLit n0)) t) succ') zero -> do+ t' <- loop t+ if boundedType t' then strict else lazy+ where+ strict = strictLoop (fromIntegral n0 :: Integer)+ lazy = loop ( lazyLoop (fromIntegral n0 :: Integer))++ strictLoop !0 = loop zero+ strictLoop !n = App succ' <$> strictLoop (n - 1) >>= loop++ lazyLoop !0 = zero+ lazyLoop !n = App succ' (lazyLoop (n - 1))+ App NaturalBuild g -> loop (App (App (App g Natural) succ) zero)+ where+ succ = Lam "n" Natural (NaturalPlus "n" (NaturalLit 1))++ zero = NaturalLit 0+ App NaturalIsZero (NaturalLit n) -> pure (BoolLit (n == 0))+ App NaturalEven (NaturalLit n) -> pure (BoolLit (even n))+ App NaturalOdd (NaturalLit n) -> pure (BoolLit (odd n))+ App NaturalToInteger (NaturalLit n) -> pure (IntegerLit (toInteger n))+ App NaturalShow (NaturalLit n) ->+ pure (TextLit (Chunks [] (Data.Text.pack (show n))))+ App (App NaturalSubtract (NaturalLit x)) (NaturalLit y)+ | y >= x -> pure (NaturalLit (subtract x y))+ | otherwise -> pure (NaturalLit 0)+ App (App NaturalSubtract (NaturalLit 0)) y -> pure y+ App (App NaturalSubtract _) (NaturalLit 0) -> pure (NaturalLit 0)+ App (App NaturalSubtract x) y | Eval.judgmentallyEqual x y -> pure (NaturalLit 0)+ App IntegerShow (IntegerLit n)+ | 0 <= n -> pure (TextLit (Chunks [] ("+" <> Data.Text.pack (show n))))+ | otherwise -> pure (TextLit (Chunks [] (Data.Text.pack (show n))))+ -- `(read . show)` is used instead of `fromInteger` because `read` uses+ -- the correct rounding rule.+ -- See https://gitlab.haskell.org/ghc/ghc/issues/17231.+ App IntegerToDouble (IntegerLit n) -> pure (DoubleLit ((DhallDouble . read . show) n))+ App DoubleShow (DoubleLit (DhallDouble n)) ->+ pure (TextLit (Chunks [] (Data.Text.pack (show n))))+ App (App OptionalBuild _A₀) g ->+ loop (App (App (App g optional) just) nothing)+ where+ optional = App Optional _A₀++ just = Lam "a" _A₀ (Some "a")++ nothing = App None _A₀+ App (App ListBuild _A₀) g -> loop (App (App (App g list) cons) nil)+ where+ _A₁ = shift 1 "a" _A₀++ list = App List _A₀++ cons =+ Lam "a" _A₀+ (Lam "as"+ (App List _A₁)+ (ListAppend (ListLit Nothing (pure "a")) "as")+ )++ nil = ListLit (Just (App List _A₀)) empty+ App (App ListFold t) (ListLit _ xs) -> do+ t' <- loop t+ let list = Var (V "list" 0)+ let lam term =+ Lam "list" (Const Type)+ (Lam "cons" (Pi "_" t' (Pi "_" list list))+ (Lam "nil" list term))+ term <- foldrM+ (\x acc -> do+ x' <- loop x+ pure (App (App (Var (V "cons" 0)) x') acc))+ (Var (V "nil" 0))+ xs+ pure (lam term)+ App (App (App (App (App ListFold _) (ListLit _ xs)) t) cons) nil -> do+ t' <- loop t+ if boundedType t' then strict else lazy+ where+ strict = foldr strictCons strictNil xs+ lazy = loop (foldr lazyCons lazyNil xs)++ strictNil = loop nil+ lazyNil = nil++ strictCons y ys = do+ App (App cons y) <$> ys >>= loop+ lazyCons y ys = App (App cons y) ys+ App (App ListLength _) (ListLit _ ys) ->+ pure (NaturalLit (fromIntegral (Data.Sequence.length ys)))+ App (App ListHead t) (ListLit _ ys) -> loop o+ where+ o = case Data.Sequence.viewl ys of+ y :< _ -> Some y+ _ -> App None t+ App (App ListLast t) (ListLit _ ys) -> loop o+ where+ o = case Data.Sequence.viewr ys of+ _ :> y -> Some y+ _ -> App None t+ App (App ListIndexed _A₀) (ListLit _ as₀) -> loop (ListLit t as₁)+ where+ as₁ = Data.Sequence.mapWithIndex adapt as₀++ _A₂ = Record (Dhall.Map.fromList kts)+ where+ kts = [ ("index", Natural)+ , ("value", _A₀)+ ]++ t | null as₀ = Just (App List _A₂)+ | otherwise = Nothing++ adapt n a_ =+ RecordLit (Dhall.Map.fromList kvs)+ where+ kvs = [ ("index", NaturalLit (fromIntegral n))+ , ("value", a_)+ ]+ App (App ListReverse _) (ListLit t xs) ->+ loop (ListLit t (Data.Sequence.reverse xs))++ App (App OptionalFold t0) x0 -> do+ t1 <- loop t0+ let optional = Var (V "optional" 0)+ let lam term = (Lam "optional"+ (Const Type)+ (Lam "some"+ (Pi "_" t1 optional)+ (Lam "none" optional term)))+ x1 <- loop x0+ pure $ case x1 of+ App None _ -> lam (Var (V "none" 0))+ Some x' -> lam (App (Var (V "some" 0)) x')+ _ -> App (App OptionalFold t1) x1++ App TextShow (TextLit (Chunks [] oldText)) ->+ loop (TextLit (Chunks [] newText))+ where+ newText = Eval.textShow oldText+ _ -> do+ res2 <- ctx (App f' a')+ case res2 of+ Nothing -> pure (App f' a')+ Just app' -> loop app'+ Let (Binding _ f _ _ _ r) b -> loop b''+ where+ r' = shift 1 (V f 0) r+ b' = subst (V f 0) r' b+ b'' = shift (-1) (V f 0) b'+ Annot x _ -> loop x+ Bool -> pure Bool+ BoolLit b -> pure (BoolLit b)+ BoolAnd x y -> decide <$> loop x <*> loop y+ where+ decide (BoolLit True ) r = r+ decide (BoolLit False) _ = BoolLit False+ decide l (BoolLit True ) = l+ decide _ (BoolLit False) = BoolLit False+ decide l r+ | Eval.judgmentallyEqual l r = l+ | otherwise = BoolAnd l r+ BoolOr x y -> decide <$> loop x <*> loop y+ where+ decide (BoolLit False) r = r+ decide (BoolLit True ) _ = BoolLit True+ decide l (BoolLit False) = l+ decide _ (BoolLit True ) = BoolLit True+ decide l r+ | Eval.judgmentallyEqual l r = l+ | otherwise = BoolOr l r+ BoolEQ x y -> decide <$> loop x <*> loop y+ where+ decide (BoolLit True ) r = r+ decide l (BoolLit True ) = l+ decide l r+ | Eval.judgmentallyEqual l r = BoolLit True+ | otherwise = BoolEQ l r+ BoolNE x y -> decide <$> loop x <*> loop y+ where+ decide (BoolLit False) r = r+ decide l (BoolLit False) = l+ decide l r+ | Eval.judgmentallyEqual l r = BoolLit False+ | otherwise = BoolNE l r+ BoolIf bool true false -> decide <$> loop bool <*> loop true <*> loop false+ where+ decide (BoolLit True ) l _ = l+ decide (BoolLit False) _ r = r+ decide b (BoolLit True) (BoolLit False) = b+ decide b l r+ | Eval.judgmentallyEqual l r = l+ | otherwise = BoolIf b l r+ Natural -> pure Natural+ NaturalLit n -> pure (NaturalLit n)+ NaturalFold -> pure NaturalFold+ NaturalBuild -> pure NaturalBuild+ NaturalIsZero -> pure NaturalIsZero+ NaturalEven -> pure NaturalEven+ NaturalOdd -> pure NaturalOdd+ NaturalToInteger -> pure NaturalToInteger+ NaturalShow -> pure NaturalShow+ NaturalSubtract -> pure NaturalSubtract+ NaturalPlus x y -> decide <$> loop x <*> loop y+ where+ decide (NaturalLit 0) r = r+ decide l (NaturalLit 0) = l+ decide (NaturalLit m) (NaturalLit n) = NaturalLit (m + n)+ decide l r = NaturalPlus l r+ NaturalTimes x y -> decide <$> loop x <*> loop y+ where+ decide (NaturalLit 1) r = r+ decide l (NaturalLit 1) = l+ decide (NaturalLit 0) _ = NaturalLit 0+ decide _ (NaturalLit 0) = NaturalLit 0+ decide (NaturalLit m) (NaturalLit n) = NaturalLit (m * n)+ decide l r = NaturalTimes l r+ Integer -> pure Integer+ IntegerLit n -> pure (IntegerLit n)+ IntegerShow -> pure IntegerShow+ IntegerToDouble -> pure IntegerToDouble+ Double -> pure Double+ DoubleLit n -> pure (DoubleLit n)+ DoubleShow -> pure DoubleShow+ Text -> pure Text+ TextLit (Chunks xys z) -> do+ chunks' <- mconcat <$> chunks+ case chunks' of+ Chunks [("", x)] "" -> pure x+ c -> pure (TextLit c)+ where+ chunks =+ ((++ [Chunks [] z]) . concat) <$> traverse process xys++ process (x, y) = do+ y' <- loop y+ case y' of+ TextLit c -> pure [Chunks [] x, c]+ _ -> pure [Chunks [(x, y')] mempty]+ TextAppend x y -> loop (TextLit (Chunks [("", x), ("", y)] ""))+ TextShow -> pure TextShow+ List -> pure List+ ListLit t es+ | Data.Sequence.null es -> ListLit <$> t' <*> pure Data.Sequence.empty+ | otherwise -> ListLit Nothing <$> es'+ where+ t' = traverse loop t+ es' = traverse loop es+ ListAppend x y -> decide <$> loop x <*> loop y+ where+ decide (ListLit _ m) r | Data.Sequence.null m = r+ decide l (ListLit _ n) | Data.Sequence.null n = l+ decide (ListLit t m) (ListLit _ n) = ListLit t (m <> n)+ decide l r = ListAppend l r+ ListBuild -> pure ListBuild+ ListFold -> pure ListFold+ ListLength -> pure ListLength+ ListHead -> pure ListHead+ ListLast -> pure ListLast+ ListIndexed -> pure ListIndexed+ ListReverse -> pure ListReverse+ Optional -> pure Optional+ Some a -> Some <$> a'+ where+ a' = loop a+ None -> pure None+ OptionalFold -> pure OptionalFold+ OptionalBuild -> pure OptionalBuild+ Record kts -> Record . Dhall.Map.sort <$> kts'+ where+ kts' = traverse loop kts+ RecordLit kvs -> RecordLit . Dhall.Map.sort <$> kvs'+ where+ kvs' = traverse loop kvs+ Union kts -> Union . Dhall.Map.sort <$> kts'+ where+ kts' = traverse (traverse loop) kts+ Combine x y -> decide <$> loop x <*> loop y+ where+ decide (RecordLit m) r | Data.Foldable.null m =+ r+ decide l (RecordLit n) | Data.Foldable.null n =+ l+ decide (RecordLit m) (RecordLit n) =+ RecordLit (Dhall.Map.unionWith decide m n)+ decide l r =+ Combine l r+ CombineTypes x y -> decide <$> loop x <*> loop y+ where+ decide (Record m) r | Data.Foldable.null m =+ r+ decide l (Record n) | Data.Foldable.null n =+ l+ decide (Record m) (Record n) =+ Record (Dhall.Map.unionWith decide m n)+ decide l r =+ CombineTypes l r+ Prefer x y -> decide <$> loop x <*> loop y+ where+ decide (RecordLit m) r | Data.Foldable.null m =+ r+ decide l (RecordLit n) | Data.Foldable.null n =+ l+ decide (RecordLit m) (RecordLit n) =+ RecordLit (Dhall.Map.union n m)+ decide l r | Eval.judgmentallyEqual l r =+ l+ decide l r =+ Prefer l r+ RecordCompletion x y -> do+ loop (Annot (Prefer (Field x "default") y) (Field x "Type"))+ Merge x y t -> do+ x' <- loop x+ y' <- loop y+ case x' of+ RecordLit kvsX ->+ case y' of+ Field (Union ktsY) kY ->+ case Dhall.Map.lookup kY ktsY of+ Just Nothing ->+ case Dhall.Map.lookup kY kvsX of+ Just vX -> return vX+ Nothing -> Merge x' y' <$> t'+ _ ->+ Merge x' y' <$> t'+ App (Field (Union ktsY) kY) vY ->+ case Dhall.Map.lookup kY ktsY of+ Just (Just _) ->+ case Dhall.Map.lookup kY kvsX of+ Just vX -> loop (App vX vY)+ Nothing -> Merge x' y' <$> t'+ _ ->+ Merge x' y' <$> t'+ _ -> Merge x' y' <$> t'+ _ -> Merge x' y' <$> t'+ where+ t' = traverse loop t+ ToMap x t -> do+ x' <- loop x+ t' <- traverse loop t+ case x' of+ RecordLit kvsX -> do+ let entry (key, value) =+ RecordLit+ (Dhall.Map.fromList+ [ ("mapKey" , TextLit (Chunks [] key))+ , ("mapValue", value )+ ]+ )++ let keyValues = Data.Sequence.fromList (map entry (Dhall.Map.toList kvsX))++ let listType = case t' of+ Just _ | null keyValues ->+ t'+ _ ->+ Nothing++ return (ListLit listType keyValues)+ _ -> do+ return (ToMap x' t')+ Field r x -> do+ let singletonRecordLit v = RecordLit (Dhall.Map.singleton x v)++ r' <- loop r+ case r' of+ RecordLit kvs ->+ case Dhall.Map.lookup x kvs of+ Just v -> pure v+ Nothing -> Field <$> (RecordLit <$> traverse loop kvs) <*> pure x+ Project r_ _ -> loop (Field r_ x)+ Prefer (RecordLit kvs) r_ -> case Dhall.Map.lookup x kvs of+ Just v -> pure (Field (Prefer (singletonRecordLit v) r_) x)+ Nothing -> loop (Field r_ x)+ Prefer l (RecordLit kvs) -> case Dhall.Map.lookup x kvs of+ Just v -> pure v+ Nothing -> loop (Field l x)+ Combine (RecordLit kvs) r_ -> case Dhall.Map.lookup x kvs of+ Just v -> pure (Field (Combine (singletonRecordLit v) r_) x)+ Nothing -> loop (Field r_ x)+ Combine l (RecordLit kvs) -> case Dhall.Map.lookup x kvs of+ Just v -> pure (Field (Combine l (singletonRecordLit v)) x)+ Nothing -> loop (Field l x)+ _ -> pure (Field r' x)+ Project x (Left fields)-> do+ x' <- loop x+ let fieldsSet = Dhall.Set.toSet fields+ case x' of+ RecordLit kvs ->+ pure (RecordLit (Dhall.Map.restrictKeys kvs fieldsSet))+ Project y _ ->+ loop (Project y (Left fields))+ Prefer l (RecordLit rKvs) -> do+ let rKs = Dhall.Map.keysSet rKvs+ let l' = Project l (Left (Dhall.Set.fromSet (Data.Set.difference fieldsSet rKs)))+ let r' = RecordLit (Dhall.Map.restrictKeys rKvs fieldsSet)+ loop (Prefer l' r')+ _ | null fields -> pure (RecordLit mempty)+ | otherwise -> pure (Project x' (Left (Dhall.Set.sort fields)))+ Project r (Right e1) -> do+ e2 <- loop e1++ case e2 of+ Record kts -> do+ loop (Project r (Left (Dhall.Set.fromSet (Dhall.Map.keysSet kts))))+ _ -> do+ r' <- loop r+ pure (Project r' (Right e2))+ Assert t -> do+ t' <- loop t++ pure (Assert t')+ Equivalent l r -> do+ l' <- loop l+ r' <- loop r++ pure (Equivalent l' r')+ Note _ e' -> loop e'+ ImportAlt l _r -> loop l+ Embed a -> pure (Embed a)++-- | Use this to wrap you embedded functions (see `normalizeWith`) to make them+-- polymorphic enough to be used.+type NormalizerM m a = forall s. Expr s a -> m (Maybe (Expr s a))++-- | An variation on `NormalizerM` for pure normalizers+type Normalizer a = NormalizerM Identity a++-- | A reified 'Normalizer', which can be stored in structures without+-- running into impredicative polymorphism.+newtype ReifiedNormalizer a = ReifiedNormalizer+ { getReifiedNormalizer :: Normalizer a }++-- | Check if an expression is in a normal form given a context of evaluation.+-- Unlike `isNormalized`, this will fully normalize and traverse through the expression.+--+-- It is much more efficient to use `isNormalized`.+--+-- `isNormalizedWith` can fail with an `error` if you check an ill-typed+-- expression+isNormalizedWith :: (Eq s, Eq a) => Normalizer a -> Expr s a -> Bool+isNormalizedWith ctx e = e == normalizeWith (Just (ReifiedNormalizer ctx)) e++-- | Quickly check if an expression is in normal form+--+-- Given a well-typed expression @e@, @'isNormalized' e@ is equivalent to+-- @e == 'normalize' e@.+--+-- Given an ill-typed expression, 'isNormalized' may fail with an error, or+-- evaluate to either False or True!+isNormalized :: Eq a => Expr s a -> Bool+isNormalized e0 = loop (denote e0)+ where+ loop e = case e of+ Const _ -> True+ Var _ -> True+ Lam _ a b -> loop a && loop b+ Pi _ a b -> loop a && loop b+ App f a -> loop f && loop a && case App f a of+ App (Lam _ _ _) _ -> False++ -- build/fold fusion for `List`+ App (App ListBuild _) (App (App ListFold _) _) -> False++ -- build/fold fusion for `Natural`+ App NaturalBuild (App NaturalFold _) -> False++ -- build/fold fusion for `Optional`+ App (App OptionalBuild _) (App (App OptionalFold _) _) -> False++ App (App (App (App NaturalFold (NaturalLit _)) _) _) _ -> False+ App NaturalFold (NaturalLit _) -> False+ App NaturalBuild _ -> False+ App NaturalIsZero (NaturalLit _) -> False+ App NaturalEven (NaturalLit _) -> False+ App NaturalOdd (NaturalLit _) -> False+ App NaturalShow (NaturalLit _) -> False+ App (App NaturalSubtract (NaturalLit _)) (NaturalLit _) -> False+ App (App NaturalSubtract (NaturalLit 0)) _ -> False+ App (App NaturalSubtract _) (NaturalLit 0) -> False+ App (App NaturalSubtract x) y -> not (Eval.judgmentallyEqual x y)+ App NaturalToInteger (NaturalLit _) -> False+ App IntegerShow (IntegerLit _) -> False+ App IntegerToDouble (IntegerLit _) -> False+ App DoubleShow (DoubleLit _) -> False+ App (App OptionalBuild _) _ -> False+ App (App ListBuild _) _ -> False+ App (App ListFold _) (ListLit _ _) -> False+ App (App ListLength _) (ListLit _ _) -> False+ App (App ListHead _) (ListLit _ _) -> False+ App (App ListLast _) (ListLit _ _) -> False+ App (App ListIndexed _) (ListLit _ _) -> False+ App (App ListReverse _) (ListLit _ _) -> False+ App (App OptionalFold _) (Some _) -> False+ App (App OptionalFold _) (App None _) -> False+ App TextShow (TextLit (Chunks [] _)) ->+ False+ _ -> True+ Let _ _ -> False+ Annot _ _ -> False+ Bool -> True+ BoolLit _ -> True+ BoolAnd x y -> loop x && loop y && decide x y+ where+ decide (BoolLit _) _ = False+ decide _ (BoolLit _) = False+ decide l r = not (Eval.judgmentallyEqual l r)+ BoolOr x y -> loop x && loop y && decide x y+ where+ decide (BoolLit _) _ = False+ decide _ (BoolLit _) = False+ decide l r = not (Eval.judgmentallyEqual l r)+ BoolEQ x y -> loop x && loop y && decide x y+ where+ decide (BoolLit True) _ = False+ decide _ (BoolLit True) = False+ decide l r = not (Eval.judgmentallyEqual l r)+ BoolNE x y -> loop x && loop y && decide x y+ where+ decide (BoolLit False) _ = False+ decide _ (BoolLit False ) = False+ decide l r = not (Eval.judgmentallyEqual l r)+ BoolIf x y z ->+ loop x && loop y && loop z && decide x y z+ where+ decide (BoolLit _) _ _ = False+ decide _ (BoolLit True) (BoolLit False) = False+ decide _ l r = not (Eval.judgmentallyEqual l r)+ Natural -> True+ NaturalLit _ -> True+ NaturalFold -> True+ NaturalBuild -> True+ NaturalIsZero -> True+ NaturalEven -> True+ NaturalOdd -> True+ NaturalShow -> True+ NaturalSubtract -> True+ NaturalToInteger -> True+ NaturalPlus x y -> loop x && loop y && decide x y+ where+ decide (NaturalLit 0) _ = False+ decide _ (NaturalLit 0) = False+ decide (NaturalLit _) (NaturalLit _) = False+ decide _ _ = True+ NaturalTimes x y -> loop x && loop 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+ IntegerToDouble -> True+ Double -> True+ DoubleLit _ -> True+ DoubleShow -> True+ Text -> True+ TextLit (Chunks [("", _)] "") -> False+ TextLit (Chunks xys _) -> all (all check) xys+ where+ check y = loop y && case y of+ TextLit _ -> False+ _ -> True+ TextAppend _ _ -> False+ TextShow -> True+ List -> True+ ListLit t es -> all loop t && all loop es+ ListAppend x y -> loop x && loop 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+ ListHead -> True+ ListLast -> True+ ListIndexed -> True+ ListReverse -> True+ Optional -> True+ Some a -> loop a+ None -> True+ OptionalFold -> True+ OptionalBuild -> True+ Record kts -> Dhall.Map.isSorted kts && all loop kts+ RecordLit kvs -> Dhall.Map.isSorted kvs && all loop kvs+ Union kts -> Dhall.Map.isSorted kts && all (all loop) kts+ Combine x y -> loop x && loop y && decide x y+ where+ decide (RecordLit m) _ | Data.Foldable.null m = False+ decide _ (RecordLit n) | Data.Foldable.null n = False+ decide (RecordLit _) (RecordLit _) = False+ decide _ _ = True+ CombineTypes x y -> loop x && loop y && decide x y+ where+ decide (Record m) _ | Data.Foldable.null m = False+ decide _ (Record n) | Data.Foldable.null n = False+ decide (Record _) (Record _) = False+ decide _ _ = True+ Prefer x y -> loop x && loop y && decide x y+ where+ decide (RecordLit m) _ | Data.Foldable.null m = False+ decide _ (RecordLit n) | Data.Foldable.null n = False+ decide (RecordLit _) (RecordLit _) = False+ decide l r = not (Eval.judgmentallyEqual l r)+ RecordCompletion _ _ -> False+ Merge x y t -> loop x && loop y && all loop t+ ToMap x t -> case x of+ RecordLit _ -> False+ _ -> loop x && all loop t+ Field r k -> case r of+ RecordLit _ -> False+ Project _ _ -> False+ Prefer (RecordLit m) _ -> Dhall.Map.keys m == [k] && loop r+ Prefer _ (RecordLit _) -> False+ Combine (RecordLit m) _ -> Dhall.Map.keys m == [k] && loop r+ Combine _ (RecordLit m) -> Dhall.Map.keys m == [k] && loop r+ _ -> loop r+ Project r p -> loop r &&+ case p of+ Left s -> case r of+ RecordLit _ -> False+ Project _ _ -> False+ Prefer _ (RecordLit _) -> False+ _ -> not (Dhall.Set.null s) && Dhall.Set.isSorted s+ Right e' -> case e' of+ Record _ -> False+ _ -> loop e'+ Assert t -> loop t+ Equivalent l r -> loop l && loop r+ Note _ e' -> loop e'+ ImportAlt _ _ -> False+ Embed _ -> True++{-| Detect if the given variable is free within the given expression++>>> "x" `freeIn` "x"+True+>>> "x" `freeIn` "y"+False+>>> "x" `freeIn` Lam "x" (Const Type) "x"+False+-}+freeIn :: Eq a => Var -> Expr s a -> Bool+variable@(V var i) `freeIn` expression =+ Dhall.Core.subst variable (Var (V var (i + 1))) strippedExpression+ /= strippedExpression+ where+ denote' :: Expr t b -> Expr () b+ denote' = denote++ strippedExpression = denote' expression++-- | Pretty-print a value+pretty :: Pretty a => a -> Text+pretty = pretty_+{-# INLINE pretty #-}++_ERROR :: String+_ERROR = "\ESC[1;31mError\ESC[0m"++{-| Utility function used to throw internal errors that should never happen+ (in theory) but that are not enforced by the type system+-}+internalError :: Data.Text.Text -> forall b . b+internalError text = error (unlines+ [ _ERROR <> ": Compiler bug "+ , " "+ , "Explanation: This error message means that there is a bug in the Dhall compiler."+ , "You didn't do anything wrong, but if you would like to see this problem fixed "+ , "then you should report the bug at: "+ , " "+ , "https://github.com/dhall-lang/dhall-haskell/issues "+ , " "+ , "Please include the following text in your bug report: "+ , " "+ , "``` "+ , Data.Text.unpack text <> " "+ , "``` "+ ] )++{-| Escape a `Text` literal using Dhall's escaping rules++ Note that the result does not include surrounding quotes+-}+escapeText :: Text -> Text+escapeText = escapeText_+{-# INLINE escapeText #-}+++{-| Utility used to implement the @--censor@ flag, by:++ * Replacing all `Src` text with spaces+ * Replacing all `Text` literals inside type errors with spaces+-}+censorExpression :: Expr Src a -> Expr Src a+censorExpression (TextLit chunks) = TextLit (censorChunks chunks)+censorExpression (Note src e) = Note (censorSrc src) (censorExpression e)+censorExpression e = over subExpressions censorExpression e++censorChunks :: Chunks Src a -> Chunks Src a+censorChunks (Chunks xys z) = Chunks xys' z'+ where+ z' = censorText z++ xys' = [ (censorText x, censorExpression y) | (x, y) <- xys ]++-- | Utility used to censor `Text` by replacing all characters with a space+censorText :: Text -> Text+censorText = Data.Text.map (\_ -> ' ')++censorSrc :: Src -> Src+censorSrc (Src { srcText = oldText, .. }) = Src { srcText = newText, .. }+ where+ newText = censorText oldText++{-| Convenience utility for converting `Either`-based exceptions to `IO`-based+ exceptions+-}+throws :: (Exception e, MonadIO io) => Either e a -> io a+throws (Left e) = liftIO (Control.Exception.throwIO e)+throws (Right r) = return r++{- $setup+>>> import qualified Codec.Serialise+>>> import qualified Dhall.Binary+>>> import Data.SpecialValues+>>> import Test.QuickCheck (Arbitrary(..), oneof, elements)+>>> :{+ instance Arbitrary DhallDouble where+ arbitrary = fmap DhallDouble (oneof [ arbitrary, elements specialValues ])+:}+-}
− src/Dhall/Core.hs-boot
@@ -1,11 +0,0 @@-module Dhall.Core where--data Const--data Var--data Expr s a--data Import--denote :: Expr s a -> Expr t a
src/Dhall/Diff.hs view
@@ -25,7 +25,7 @@ import Data.Text (Text) import Data.Text.Prettyprint.Doc (Doc, Pretty) import Data.Void (Void)-import Dhall.Core (Binding(..), Chunks (..), Const(..), Expr(..), Var(..))+import Dhall.Core (Binding(..), Chunks (..), Const(..), DhallDouble(..), Expr(..), Var(..)) import Dhall.Binary (ToTerm) import Dhall.Map (Map) import Dhall.Set (Set)@@ -42,6 +42,9 @@ import qualified Dhall.Set import qualified Dhall.Pretty.Internal as Internal +{-| This type is a `Doc` enriched with a `same` flag to efficiently track if+ any difference was detected+-} data Diff = Diff { same :: Bool@@ -185,8 +188,8 @@ diffNatural :: Natural -> Natural -> Diff diffNatural = diffPrimitive (token . Internal.prettyNatural) -diffDouble :: Double -> Double -> Diff-diffDouble = diffPrimitive (token . Internal.prettyDouble)+diffDouble :: DhallDouble -> DhallDouble -> Diff+diffDouble = diffPrimitive (token . Internal.prettyDouble . getDhallDouble) diffConst :: Const -> Const -> Diff diffConst = diffPrimitive (token . Internal.prettyConst)@@ -583,6 +586,10 @@ <> operator "⫽" <> " " <> ignore+skeleton (RecordCompletion {}) =+ ignore+ <> operator "::"+ <> ignore skeleton (Merge {}) = keyword "merge" <> " "@@ -1005,6 +1012,16 @@ diffImportExpression l r@(Embed {}) = mismatch l r diffImportExpression l r =+ diffRecordCompletionExpression l r++diffRecordCompletionExpression :: (Eq a, Pretty a) => Expr Void a -> Expr Void a -> Diff+diffRecordCompletionExpression (RecordCompletion aL bL) (RecordCompletion aR bR) =+ diffSelectorExpression aL aR <> "::" <> diffSelectorExpression bL bR+diffRecordCompletionExpression l@(RecordCompletion {}) r =+ mismatch l r+diffRecordCompletionExpression l r@(RecordCompletion {}) =+ mismatch l r+diffRecordCompletionExpression l r = diffSelectorExpression l r diffSelectorExpression :: (Eq a, Pretty a) => Expr Void a -> Expr Void a -> Diff
src/Dhall/Eval.hs view
@@ -1,12 +1,16 @@+{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -O #-} @@ -45,6 +49,7 @@ , Environment(..) , Val(..) , (~>)+ , textShow ) where import Data.Foldable (foldr', toList)@@ -53,12 +58,12 @@ import Data.Text (Text) import Data.Void (Void) -import Dhall.Core+import Dhall.Syntax ( Binding(..) , Expr(..) , Chunks(..) , Const(..)- , Import+ , DhallDouble(..) , Var(..) ) @@ -67,20 +72,22 @@ import GHC.Natural (Natural) import Prelude hiding (succ) -import qualified Codec.Serialise as Serialise+import qualified Data.Char import qualified Data.Sequence as Sequence import qualified Data.Set import qualified Data.Text as Text-import qualified Dhall.Binary as Binary-import qualified Dhall.Core as Core+import qualified Dhall.Syntax as Syntax import qualified Dhall.Map as Map import qualified Dhall.Set+import qualified Text.Printf data Environment a = Empty | Skip !(Environment a) {-# UNPACK #-} !Text | Extend !(Environment a) {-# UNPACK #-} !Text (Val a) +deriving instance (Show a, Show (Val a -> Val a)) => Show (Environment a)+ errorMsg :: String errorMsg = unlines [ _ERROR <> ": Compiler bug "@@ -98,8 +105,13 @@ data Closure a = Closure !Text !(Environment a) !(Expr Void a)++deriving instance (Show a, Show (Val a -> Val a)) => Show (Closure a)+ data VChunks a = VChunks ![(Text, Val a)] !Text +deriving instance (Show a, Show (Val a -> Val a)) => Show (VChunks a)+ instance Semigroup (VChunks a) where VChunks xys z <> VChunks [] z' = VChunks xys (z <> z') VChunks xys z <> VChunks ((x', y'):xys') z' = VChunks (xys ++ (z <> x', y'):xys') z'@@ -135,6 +147,8 @@ -- this information in case the @Natural/subtract@ ends up not being fully -- saturated, in which case we need to recover the unsaturated built-in +deriving instance (Show a, Show (Val a -> Val a)) => Show (HLamInfo a)+ pattern VPrim :: (Val a -> Val a) -> Val a pattern VPrim f = VHLam Prim f @@ -182,7 +196,7 @@ | VIntegerToDouble !(Val a) | VDouble- | VDoubleLit !Double+ | VDoubleLit !DhallDouble | VDoubleShow !(Val a) | VText@@ -221,6 +235,9 @@ | VEquivalent !(Val a) !(Val a) | VEmbed a +-- | For use with "Text.Show.Functions".+deriving instance (Show a, Show (Val a -> Val a)) => Show (Val a)+ (~>) :: Val a -> Val a -> Val a (~>) a b = VHPi "_" a (\_ -> b) {-# INLINE (~>) #-}@@ -517,9 +534,10 @@ n -> VIntegerShow n IntegerToDouble -> VPrim $ \case- VIntegerLit n -> VDoubleLit (read (show n))+ VIntegerLit n -> VDoubleLit (DhallDouble (read (show n))) -- `(read . show)` is used instead of `fromInteger`- -- because `read` uses the correct rounding rule+ -- because `read` uses the correct rounding rule.+ -- See https://gitlab.haskell.org/ghc/ghc/issues/17231. n -> VIntegerToDouble n Double -> VDouble@@ -527,8 +545,8 @@ VDoubleLit n DoubleShow -> VPrim $ \case- VDoubleLit n -> VTextLit (VChunks [] (Text.pack (show n)))- n -> VDoubleShow n+ VDoubleLit (DhallDouble n) -> VTextLit (VChunks [] (Text.pack (show n)))+ n -> VDoubleShow n Text -> VText TextLit cs ->@@ -539,7 +557,7 @@ eval env (TextLit (Chunks [("", t), ("", u)] "")) TextShow -> VPrim $ \case- VTextLit (VChunks [] x) -> VTextLit (VChunks [] (Core.textShow x))+ VTextLit (VChunks [] x) -> VTextLit (VChunks [] (textShow x)) t -> VTextShow t List -> VPrim VList@@ -624,7 +642,7 @@ VPrim $ \ ~a -> VPrim $ \case VListLit t as | null as ->- VListLit t (Sequence.reverse as)+ VListLit t as VListLit _ as -> VListLit Nothing (Sequence.reverse as) t ->@@ -674,6 +692,8 @@ vCombineTypes (eval env t) (eval env u) Prefer t u -> vPrefer env (eval env t) (eval env u)+ RecordCompletion t u ->+ eval env (Annot (Prefer (Field t "default") u) (Field t "Type")) Merge x y ma -> case (eval env x, eval env y, fmap (eval env) ma) of (VRecordLit m, VInject _ k mt, _)@@ -754,6 +774,21 @@ go _ _ = False {-# INLINE eqMaybeBy #-} +-- | Utility that powers the @Text/show@ built-in+textShow :: Text -> Text+textShow text = "\"" <> Text.concatMap f text <> "\""+ where+ f '"' = "\\\""+ f '$' = "\\u0024"+ f '\\' = "\\\\"+ f '\b' = "\\b"+ f '\n' = "\\n"+ f '\r' = "\\r"+ f '\t' = "\\t"+ f '\f' = "\\f"+ f c | c <= '\x1F' = Text.pack (Text.Printf.printf "\\u%04x" (Data.Char.ord c))+ | otherwise = Text.singleton c+ conv :: forall a. Eq a => Environment a -> Val a -> Val a -> Bool conv !env t0 t0' = case (t0, t0') of@@ -836,8 +871,7 @@ (VDouble, VDouble) -> True (VDoubleLit n, VDoubleLit n') ->- Serialise.serialise (Binary.encode (DoubleLit n :: Expr Void Import))- == Serialise.serialise (Binary.encode (DoubleLit n' :: Expr Void Import))+ n == n' (VDoubleShow t, VDoubleShow t') -> conv env t t' (VText, VText) ->@@ -928,14 +962,8 @@ convSkip x = conv (Skip env x) {-# INLINE convSkip #-} -{-| Returns `True` if two expressions are α-equivalent and β-equivalent and- `False` otherwise-- `judgmentallyEqual` can fail with an `error` if you compare ill-typed- expressions--} judgmentallyEqual :: Eq a => Expr s a -> Expr t a -> Bool-judgmentallyEqual (Core.denote -> t) (Core.denote -> u) =+judgmentallyEqual (Syntax.denote -> t) (Syntax.denote -> u) = conv Empty (eval Empty t) (eval Empty u) data Names@@ -961,7 +989,7 @@ VConst k -> Const k VVar !x !i ->- Var (V x (fromIntegral (countNames x env - i - 1)))+ Var (V x (countNames x env - i - 1)) VApp t u -> quote env t `qApp` u VLam a (freshClosure -> (x, v, t)) ->@@ -1122,32 +1150,11 @@ -- | Normalize an expression in an environment of values. Any variable pointing out of -- the environment is treated as opaque free variable. nf :: Eq a => Environment a -> Expr s a -> Expr t a-nf !env = Core.renote . quote (envNames env) . eval env . Core.denote--{-| Reduce an expression to its normal form, performing beta reduction-- `normalize` does not type-check the expression. You may want to type-check- expressions before normalizing them since normalization can convert an- ill-typed expression into a well-typed expression.+nf !env = Syntax.renote . quote (envNames env) . eval env . Syntax.denote - `normalize` can also fail with `error` if you normalize an ill-typed- expression--} normalize :: Eq a => Expr s a -> Expr t a normalize = nf Empty -{-| α-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 = goEnv EmptyNames where@@ -1278,6 +1285,8 @@ CombineTypes (go t) (go u) Prefer t u -> Prefer (go t) (go u)+ RecordCompletion t u ->+ RecordCompletion (go t) (go u) Merge x y ma -> Merge (go x) (go y) (fmap go ma) ToMap x ma ->
− src/Dhall/Eval.hs-boot
@@ -1,8 +0,0 @@--module Dhall.Eval where--import {-# SOURCE #-} Dhall.Core--judgmentallyEqual :: Eq a => Expr s a -> Expr t a -> Bool-normalize :: Eq a => Expr s a -> Expr t a-alphaNormalize :: Expr s a -> Expr s a
src/Dhall/Format.hs view
@@ -54,8 +54,8 @@ case formatMode of Modify {..} -> case inplace of- File file -> do- (header, expr) <- Dhall.Util.getExpressionAndHeader censor (File file)+ InputFile file -> do+ (header, expr) <- Dhall.Util.getExpressionAndHeader censor (InputFile file) let doc = Pretty.pretty header <> Pretty.unAnnotate (Dhall.Pretty.prettyCharacterSet characterSet expr)@@ -83,8 +83,8 @@ (Pretty.layoutSmart layoutOpts (Pretty.unAnnotate doc)) Check {..} -> do originalText <- case path of- File file -> Data.Text.IO.readFile file- StandardInput -> Data.Text.IO.getContents+ InputFile file -> Data.Text.IO.readFile file+ StandardInput -> Data.Text.IO.getContents (header, expr) <- Dhall.Util.getExpressionAndHeader censor path
src/Dhall/Freeze.hs view
@@ -17,10 +17,10 @@ import Control.Exception (SomeException) import Data.Monoid ((<>)) import Data.Text+import Data.Void (Void) import Dhall.Core (Expr(..), Import(..), ImportHashed(..), ImportType(..)) import Dhall.Parser (Src) import Dhall.Pretty (CharacterSet, annToAnsiStyle, layoutOpts, prettyCharacterSet)-import Dhall.TypeCheck (X) import Dhall.Util (Censor, Input(..)) import System.Console.ANSI (hSupportsANSI) @@ -58,7 +58,7 @@ State.evalStateT (Dhall.Import.loadWith (Embed import_)) status -- Try again without the semantic integrity check if decoding fails- let handler :: SomeException -> IO (Expr Src X)+ let handler :: SomeException -> IO (Expr Src Void) handler _ = do State.evalStateT (Dhall.Import.loadWith (Embed unprotectedImport)) status @@ -101,7 +101,7 @@ let unAnnotated = Pretty.layoutSmart layoutOpts (Pretty.unAnnotate doc) case inplace of- File f ->+ InputFile f -> System.IO.withFile f System.IO.WriteMode (\handle -> do Pretty.renderIO handle unAnnotated Data.Text.IO.hPutStrLn handle "" )@@ -142,8 +142,8 @@ -> IO () freeze inplace scope intent characterSet censor = do let directory = case inplace of- StandardInput -> "."- File file -> System.FilePath.takeDirectory file+ StandardInput -> "."+ InputFile file -> System.FilePath.takeDirectory file (header, parsedExpression) <- Dhall.Util.getExpressionAndHeader censor inplace
− src/Dhall/Hash.hs
@@ -1,37 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}---- | This module contains the implementation of the @dhall hash@ subcommand--module Dhall.Hash- ( -- * Hash- hash- ) where--import Dhall.Import (hashExpressionToCode)-import Dhall.Util (Censor, Input(..))--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 Dhall.Util-import qualified Data.Text.IO---- | Implementation of the @dhall hash@ subcommand-hash :: Censor -> IO ()-hash censor = do- parsedExpression <- Dhall.Util.getExpression censor StandardInput-- let status = Dhall.Import.emptyStatus "."-- resolvedExpression <- State.evalStateT (Dhall.Import.loadWith parsedExpression) status-- 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 normalizedExpression)
src/Dhall/Import.hs view
@@ -150,7 +150,7 @@ import Data.List.NonEmpty (NonEmpty(..)) import Data.Semigroup (Semigroup(..)) import Data.Text (Text)-import Data.Void (absurd)+import Data.Void (Void, absurd) #if MIN_VERSION_base(4,8,0) #else import Data.Traversable (traverse)@@ -178,7 +178,6 @@ import Dhall.Import.Types import Dhall.Parser (Parser(..), ParseError(..), Src(..), SourcedException(..))-import Dhall.TypeCheck (X) import Lens.Family.State.Strict (zoom) import qualified Codec.Serialise@@ -462,7 +461,7 @@ chainedChangeMode mode (Chained (Import importHashed _)) = Chained (Import importHashed mode) --- Chain imports, also typecheck and normalize headers if applicable.+-- | Chain imports, also typecheck and normalize headers if applicable. chainImport :: Chained -> Import -> StateT Status IO Chained chainImport (Chained parent) child@(Import importHashed@(ImportHashed _ (Remote url)) _) = do url' <- normalizeHeaders url@@ -545,7 +544,7 @@ -- | Ensure that the given expression is present in the semantic cache. The -- given expression should be alpha-beta-normal.-writeExpressionToSemanticCache :: Expr Src X -> IO ()+writeExpressionToSemanticCache :: Expr Src Void -> IO () writeExpressionToSemanticCache expression = writeToSemanticCache hash bytes where bytes = encodeExpression NoVersion expression@@ -572,7 +571,7 @@ absolutePath <- Directory.makeAbsolute path return absolutePath Remote url -> do- let urlText = Dhall.Pretty.Internal.pretty (url { headers = Nothing })+ let urlText = Dhall.Core.pretty (url { headers = Nothing }) return (Text.unpack urlText) Env env -> return $ Text.unpack env Missing -> throwM (MissingImports [])@@ -646,13 +645,13 @@ Missing -> Field locationType "Missing" local@(Local _ _) -> App (Field locationType "Local")- (TextLit (Chunks [] (Dhall.Pretty.Internal.pretty local)))+ (TextLit (Chunks [] (Dhall.Core.pretty local))) remote_@(Remote _) -> App (Field locationType "Remote")- (TextLit (Chunks [] (Dhall.Pretty.Internal.pretty remote_)))+ (TextLit (Chunks [] (Dhall.Core.pretty remote_))) Env env -> App (Field locationType "Environment")- (TextLit (Chunks [] (Dhall.Pretty.Internal.pretty env)))+ (TextLit (Chunks [] (Dhall.Core.pretty env))) return (ImportSemantics {..}) @@ -660,7 +659,7 @@ -- AST (without normalising or type-checking it first). See -- https://github.com/dhall-lang/dhall-haskell/issues/1098 for further -- discussion.-computeSemisemanticHash :: Expr Src X -> Dhall.Crypto.SHA256Digest+computeSemisemanticHash :: Expr Src Void -> Dhall.Crypto.SHA256Digest computeSemisemanticHash resolvedExpr = hashExpression resolvedExpr -- Fetch encoded normal form from "semi-semantic cache"@@ -752,6 +751,7 @@ return cacheFile +-- | Warn if no cache directory is available warnAboutMissingCaches :: (MonadCatch m, Alternative m, MonadIO m) => m () warnAboutMissingCaches = warn <|> return () where warn = Data.Foldable.traverse_ (getOrCreateCacheDirectory True) ["dhall", "dhall-haskell"]@@ -926,7 +926,7 @@ loadedExpr <- loadWith headersExpression let go key₀ key₁ = do- let expected :: Expr Src X+ let expected :: Expr Src Void expected = App List ( Record@@ -976,7 +976,7 @@ You can configure the desired behavior through the initial `Status` that you supply -}-loadWith :: Expr Src Import -> StateT Status IO (Expr Src X)+loadWith :: Expr Src Import -> StateT Status IO (Expr Src Void) loadWith expr₀ = case expr₀ of Embed import₀ -> do Status {..} <- State.get@@ -1082,6 +1082,7 @@ Combine a b -> Combine <$> loadWith a <*> loadWith b CombineTypes a b -> CombineTypes <$> loadWith a <*> loadWith b Prefer a b -> Prefer <$> loadWith a <*> loadWith b+ RecordCompletion a b -> RecordCompletion <$> loadWith a <*> loadWith b Merge a b c -> Merge <$> loadWith a <*> loadWith b <*> mapM loadWith c ToMap a b -> ToMap <$> loadWith a <*> mapM loadWith b Field a b -> Field <$> loadWith a <*> pure b@@ -1094,12 +1095,12 @@ (Note <$> pure a <*> loadWith b) `catch` handler -- | Resolve all imports within an expression-load :: Expr Src Import -> IO (Expr Src X)+load :: Expr Src Import -> IO (Expr Src Void) load = loadRelativeTo "." UseSemanticCache -- | Resolve all imports within an expression, importing relative to the given -- directory.-loadRelativeTo :: FilePath -> SemanticCacheMode -> Expr Src Import -> IO (Expr Src X)+loadRelativeTo :: FilePath -> SemanticCacheMode -> Expr Src Import -> IO (Expr Src Void) loadRelativeTo rootDirectory semanticCacheMode expression = State.evalStateT (loadWith expression)@@ -1109,7 +1110,7 @@ :: forall s . StandardVersion -- ^ `NoVersion` means to encode without the version tag- -> Expr s X+ -> Expr s Void -> Data.ByteString.ByteString encodeExpression _standardVersion expression = bytesStrict where@@ -1132,7 +1133,7 @@ bytesStrict = Data.ByteString.Lazy.toStrict bytesLazy -- | Hash a fully resolved expression-hashExpression :: Expr s X -> Dhall.Crypto.SHA256Digest+hashExpression :: Expr s Void -> Dhall.Crypto.SHA256Digest hashExpression expression = Dhall.Crypto.sha256Hash (encodeExpression NoVersion expression) @@ -1142,7 +1143,7 @@ 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 s Void -> Text hashExpressionToCode expr = "sha256:" <> Text.pack (show (hashExpression expr)) @@ -1153,6 +1154,6 @@ show _ = "\nImport resolution is disabled" -- | Assert than an expression is import-free-assertNoImports :: MonadIO io => Expr Src Import -> io (Expr Src X)+assertNoImports :: MonadIO io => Expr Src Import -> io (Expr Src Void) assertNoImports expression = Dhall.Core.throws (traverse (\_ -> Left ImportResolutionDisabled) expression)
src/Dhall/Import/Types.hs view
@@ -11,6 +11,7 @@ import Data.Map (Map) import Data.Semigroup ((<>)) import Data.Text.Prettyprint.Doc (Pretty(..))+import Data.Void (Void) import Dhall.Context (Context) import Dhall.Core ( Directory (..)@@ -25,7 +26,6 @@ , URL ) import Dhall.Parser (Src)-import Dhall.TypeCheck (X) import Lens.Family (LensLike') import System.FilePath (isRelative, splitDirectories) @@ -38,21 +38,28 @@ -- those are well-typed (either of type `List { header : Text, value Text}` or -- `List { mapKey : Text, mapValue Text})` and in normal form. These -- invariants are preserved by the API exposed by @Dhall.Import@.-newtype Chained = Chained { chainedImport :: Import }+newtype Chained = Chained+ { chainedImport :: Import+ -- ^ The underlying import+ } deriving (Eq, Ord) instance Pretty Chained where pretty (Chained import_) = pretty import_ +-- | An import that has been fully interpeted data ImportSemantics = ImportSemantics- { importSemantics :: Expr Src X+ { importSemantics :: Expr Src Void -- ^ The fully resolved import, typechecked and beta-normal. } -- | `parent` imports (i.e. depends on) `child` data Depends = Depends { parent :: Chained, child :: Chained } -data SemanticCacheMode = IgnoreSemanticCache | UseSemanticCache+{-| This enables or disables the semantic cache for imports protected by+ integrity checks+-}+data SemanticCacheMode = IgnoreSemanticCache | UseSemanticCache deriving (Eq) -- | State threaded throughout the import process data Status = Status@@ -71,9 +78,9 @@ , _remote :: URL -> StateT Status IO Data.Text.Text -- ^ The remote resolver, fetches the content at the given URL. - , _normalizer :: Maybe (ReifiedNormalizer X)+ , _normalizer :: Maybe (ReifiedNormalizer Void) - , _startingContext :: Context (Expr Src X)+ , _startingContext :: Context (Expr Src Void) , _semanticCacheMode :: SemanticCacheMode }@@ -112,22 +119,29 @@ , importMode = Code } +-- | Lens from a `Status` to its `_stack` field stack :: Functor f => LensLike' f Status (NonEmpty Chained) stack k s = fmap (\x -> s { _stack = x }) (k (_stack s)) +-- | Lens from a `Status` to its `_graph` field graph :: Functor f => LensLike' f Status [Depends] graph k s = fmap (\x -> s { _graph = x }) (k (_graph s)) +-- | Lens from a `Status` to its `_cache` field cache :: Functor f => LensLike' f Status (Map Chained ImportSemantics) cache k s = fmap (\x -> s { _cache = x }) (k (_cache s)) -remote :: Functor f => LensLike' f Status (URL -> StateT Status IO Data.Text.Text)+-- | Lens from a `Status` to its `_remote` field+remote+ :: Functor f => LensLike' f Status (URL -> StateT Status IO Data.Text.Text) remote k s = fmap (\x -> s { _remote = x }) (k (_remote s)) -normalizer :: Functor f => LensLike' f Status (Maybe (ReifiedNormalizer X))+-- | Lens from a `Status` to its `_normalizer` field+normalizer :: Functor f => LensLike' f Status (Maybe (ReifiedNormalizer Void)) normalizer k s = fmap (\x -> s {_normalizer = x}) (k (_normalizer s)) -startingContext :: Functor f => LensLike' f Status (Context (Expr Src X))+-- | Lens from a `Status` to its `_startingContext` field+startingContext :: Functor f => LensLike' f Status (Context (Expr Src Void)) startingContext k s = fmap (\x -> s { _startingContext = x }) (k (_startingContext s))
src/Dhall/Lint.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE RecordWildCards #-}+ -- | This module contains the implementation of the @dhall lint@ command module Dhall.Lint@@ -6,6 +8,7 @@ , removeUnusedBindings ) where +import Control.Applicative ((<|>)) import Dhall.Core (Binding(..), Expr(..), Import, Var(..), subExpressions) import qualified Dhall.Core@@ -17,12 +20,17 @@ Currently this: * removes unused @let@ bindings with 'removeUnusedBindings'.+ * fixes @let a = x ≡ y@ to be @let a = assert : x ≡ y@ * consolidates nested @let@ bindings to use a multiple-@let@ binding with 'removeLetInLet' -} lint :: Expr s Import -> Expr t Import-lint = Dhall.Optics.rewriteOf subExpressions removeUnusedBindings . removeLetInLet+lint =+ Dhall.Optics.rewriteOf+ subExpressions+ (\e -> fixAsserts e <|> removeUnusedBindings e)+ . removeLetInLet --- Remove unused Let bindings.+-- | Remove unused `Let` bindings. removeUnusedBindings :: Eq a => Expr s a -> Maybe (Expr s a) -- Don't remove assertions! removeUnusedBindings (Let (Binding _ _ _ _ _ e) _)@@ -31,6 +39,13 @@ | not (V a 0 `Dhall.Core.freeIn` d) = Just (Dhall.Core.shift (-1) (V a 0) d) removeUnusedBindings _ = Nothing++-- Fix `Let` bindings that the user probably meant to be `assert`s+fixAsserts :: Expr s a -> Maybe (Expr s a)+fixAsserts (Let (Binding { value = Equivalent x y, ..}) body) =+ Just (Let (Binding { value = Assert (Equivalent x y), .. }) body)+fixAsserts _ =+ Nothing isOrContainsAssert :: Expr s a -> Bool isOrContainsAssert (Assert _) = True
src/Dhall/Main.hs view
@@ -22,17 +22,19 @@ import Control.Applicative (optional, (<|>)) import Control.Exception (Handler(..), SomeException)+import Control.Monad (when) import Data.List.NonEmpty (NonEmpty(..)) import Data.Monoid ((<>)) import Data.Text (Text) import Data.Text.Prettyprint.Doc (Doc, Pretty)+import Data.Void (Void) import Dhall.Core (Expr(Annot), Import, pretty) import Dhall.Freeze (Intent(..), Scope(..))-import Dhall.Import (Imported(..), Depends(..), SemanticCacheMode(..))+import Dhall.Import (Imported(..), Depends(..), SemanticCacheMode(..), _semanticCacheMode) import Dhall.Parser (Src) import Dhall.Pretty (Ann, CharacterSet(..), annToAnsiStyle, layoutOpts)-import Dhall.TypeCheck (Censored(..), DetailedTypeError(..), TypeError, X)-import Dhall.Util (Censor(..), Input(..))+import Dhall.TypeCheck (Censored(..), DetailedTypeError(..), TypeError)+import Dhall.Util (Censor(..), Input(..), Output(..)) import Dhall.Version (dhallVersionString) import Options.Applicative (Parser, ParserInfo) import System.Exit (ExitCode, exitFailure)@@ -59,10 +61,10 @@ import qualified Dhall.Diff import qualified Dhall.Format import qualified Dhall.Freeze-import qualified Dhall.Hash import qualified Dhall.Import import qualified Dhall.Import.Types import qualified Dhall.Lint+import qualified Dhall.Tags import qualified Dhall.Pretty import qualified Dhall.Repl import qualified Dhall.TypeCheck@@ -85,25 +87,46 @@ , censor :: Censor } +ignoreSemanticCache :: Mode -> Bool+ignoreSemanticCache Default {..} = semanticCacheMode == IgnoreSemanticCache+ignoreSemanticCache Resolve {..} = semanticCacheMode == IgnoreSemanticCache+ignoreSemanticCache Type {..} = semanticCacheMode == IgnoreSemanticCache+ignoreSemanticCache _ = False+ -- | The subcommands for the @dhall@ executable data Mode = Default { file :: Input+ , output :: Output , annotate :: Bool , alpha :: Bool , semanticCacheMode :: SemanticCacheMode , version :: Bool } | Version- | Resolve { file :: Input, resolveMode :: Maybe ResolveMode }- | Type { file :: Input, quiet :: Bool }+ | Resolve+ { file :: Input+ , resolveMode :: Maybe ResolveMode+ , semanticCacheMode :: SemanticCacheMode+ }+ | Type+ { file :: Input+ , quiet :: Bool+ , semanticCacheMode :: SemanticCacheMode+ } | Normalize { file :: Input , alpha :: Bool } | Repl | Format { formatMode :: Dhall.Format.FormatMode } | Freeze { inplace :: Input, all_ :: Bool, cache :: Bool }- | Hash+ | Hash { file :: Input } | Diff { expr1 :: Text, expr2 :: Text } | Lint { inplace :: Input }+ | Tags+ { input :: Input+ , output :: Output+ , suffixes :: Maybe [Text]+ , followSymlinks :: Bool+ } | Encode { file :: Input, json :: Bool } | Decode { file :: Input, json :: Bool } | Text { file :: Input }@@ -157,11 +180,11 @@ <|> subcommand "resolve" "Resolve an expression's imports"- (Resolve <$> parseFile <*> parseResolveMode)+ (Resolve <$> parseFile <*> parseResolveMode <*> parseSemanticCacheMode) <|> subcommand "type" "Infer an expression's type"- (Type <$> parseFile <*> parseQuiet)+ (Type <$> parseFile <*> parseQuiet <*> parseSemanticCacheMode) <|> subcommand "normalize" "Normalize an expression"@@ -177,12 +200,16 @@ <|> subcommand "hash" "Compute semantic hashes for Dhall expressions"- (pure Hash)+ (Hash <$> parseFile) <|> subcommand "lint" "Improve Dhall code by using newer language features and removing dead code" (Lint <$> parseInplace) <|> subcommand+ "tags"+ "Generate etags file"+ (Tags <$> parseInput <*> parseTagsOutput <*> parseSuffixes <*> parseFollowSymlinks)+ <|> subcommand "format" "Standard code formatter for the Dhall language" (Format <$> parseFormatMode)@@ -204,6 +231,7 @@ (Text <$> parseFile) <|> ( Default <$> parseFile+ <*> parseOutput <*> parseAnnotate <*> parseAlpha <*> parseSemanticCacheMode@@ -218,7 +246,7 @@ parseFile = fmap f (optional p) where f Nothing = StandardInput- f (Just file) = File file+ f (Just file) = InputFile file p = Options.Applicative.strOption ( Options.Applicative.long "file"@@ -226,6 +254,17 @@ <> Options.Applicative.metavar "FILE" ) + parseOutput = fmap f (optional p)+ where+ f Nothing = StandardOutput+ f (Just file) = OutputFile file++ p = Options.Applicative.strOption+ ( Options.Applicative.long "output"+ <> Options.Applicative.help "Write result to a file instead of standard output"+ <> Options.Applicative.metavar "FILE"+ )+ parseAlpha = Options.Applicative.switch ( Options.Applicative.long "alpha"@@ -282,7 +321,7 @@ parseInplace = fmap f (optional p) where f Nothing = StandardInput- f (Just file) = File file+ f (Just file) = InputFile file p = Options.Applicative.strOption ( Options.Applicative.long "inplace"@@ -290,6 +329,46 @@ <> Options.Applicative.metavar "FILE" ) + parseInput = fmap f (optional p)+ where+ f Nothing = StandardInput+ f (Just path) = InputFile path++ p = Options.Applicative.strOption+ ( Options.Applicative.long "path"+ <> Options.Applicative.help "Index all files in path recursively. Will get list of files from STDIN if omitted."+ <> Options.Applicative.metavar "PATH"+ )++ parseTagsOutput = fmap f (optional p)+ where+ f Nothing = OutputFile "tags"+ f (Just file) = OutputFile file++ p = Options.Applicative.strOption+ ( Options.Applicative.long "output"+ <> Options.Applicative.help "The name of the file that the tags are written to. Defaults to \"tags\""+ <> Options.Applicative.metavar "FILENAME"+ )++ parseSuffixes = fmap f (optional p)+ where+ f Nothing = Just [".dhall"]+ f (Just "") = Nothing+ f (Just line) = Just (Data.Text.splitOn " " line)++ p = Options.Applicative.strOption+ ( Options.Applicative.long "suffixes"+ <> Options.Applicative.help "Index only files with suffixes. \"\" to index all files."+ <> Options.Applicative.metavar "SUFFIXES"+ )++ parseFollowSymlinks =+ Options.Applicative.switch+ ( Options.Applicative.long "follow-symlinks"+ <> Options.Applicative.help "Follow symlinks when recursing directories"+ )+ parseJSONFlag = Options.Applicative.switch ( Options.Applicative.long "json"@@ -338,7 +417,7 @@ GHC.IO.Encoding.setLocaleEncoding System.IO.utf8 let rootDirectory = \case- File f -> System.FilePath.takeDirectory f+ InputFile f -> System.FilePath.takeDirectory f StandardInput -> "." let toStatus = Dhall.Import.emptyStatus . rootDirectory@@ -363,7 +442,7 @@ System.Exit.exitFailure handleTypeError e = Control.Exception.handle handleAll $ do- let _ = e :: TypeError Src X+ let _ = e :: TypeError Src Void System.IO.hPutStrLn System.IO.stderr "" if explain then@@ -378,7 +457,7 @@ NoCensor -> Control.Exception.throwIO e handleImported (Imported ps e) = Control.Exception.handle handleAll $ do- let _ = e :: TypeError Src X+ let _ = e :: TypeError Src Void System.IO.hPutStrLn System.IO.stderr "" if explain then Control.Exception.throwIO (Imported ps (DetailedTypeError e))@@ -407,9 +486,9 @@ let doc = Dhall.Pretty.prettyCharacterSet characterSet expression renderDoc h doc- - Dhall.Import.warnAboutMissingCaches + when (not $ ignoreSemanticCache mode) Dhall.Import.warnAboutMissingCaches+ handle $ case mode of Version -> do putStrLn dhallVersionString@@ -440,13 +519,16 @@ then Annot alphaNormalizedExpression inferredType else alphaNormalizedExpression - render System.IO.stdout annotatedExpression+ case output of+ StandardOutput -> render System.IO.stdout annotatedExpression+ OutputFile file_ ->+ System.IO.withFile file_ System.IO.WriteMode $ \h -> render h annotatedExpression Resolve { resolveMode = Just Dot, ..} -> do expression <- getExpression file (Dhall.Import.Types.Status { _graph, _stack }) <-- State.execStateT (Dhall.Import.loadWith expression) (toStatus file)+ State.execStateT (Dhall.Import.loadWith expression) (toStatus file) { _semanticCacheMode = semanticCacheMode } let (rootImport :| _) = _stack imports = rootImport : map parent _graph ++ map child _graph@@ -482,7 +564,7 @@ expression <- getExpression file (Dhall.Import.Types.Status { _cache }) <-- State.execStateT (Dhall.Import.loadWith expression) (toStatus file)+ State.execStateT (Dhall.Import.loadWith expression) (toStatus file) { _semanticCacheMode = semanticCacheMode } mapM_ print . fmap ( Pretty.pretty@@ -496,7 +578,7 @@ expression <- getExpression file resolvedExpression <-- Dhall.Import.loadRelativeTo (rootDirectory file) UseSemanticCache expression+ Dhall.Import.loadRelativeTo (rootDirectory file) semanticCacheMode expression render System.IO.stdout resolvedExpression @@ -520,7 +602,7 @@ expression <- getExpression file resolvedExpression <-- Dhall.Import.loadRelativeTo (rootDirectory file) UseSemanticCache expression+ Dhall.Import.loadRelativeTo (rootDirectory file) semanticCacheMode expression inferredType <- Dhall.Core.throws (Dhall.TypeCheck.typeOf resolvedExpression) @@ -554,14 +636,24 @@ Dhall.Freeze.freeze inplace scope intent characterSet censor - Hash -> do- Dhall.Hash.hash censor+ Hash {..} -> do+ expression <- getExpression file + resolvedExpression <-+ Dhall.Import.loadRelativeTo (rootDirectory file) UseSemanticCache expression++ _ <- Dhall.Core.throws (Dhall.TypeCheck.typeOf resolvedExpression)++ let normalizedExpression =+ Dhall.Core.alphaNormalize (Dhall.Core.normalize resolvedExpression)++ Data.Text.IO.putStrLn (Dhall.Import.hashExpressionToCode normalizedExpression)+ Lint {..} -> do (header, expression) <- getExpressionAndHeader inplace case inplace of- File file -> do+ InputFile file -> do let lintedExpression = Dhall.Lint.lint expression let doc = Pretty.pretty header@@ -601,7 +693,7 @@ Decode {..} -> do bytes <- do case file of- File f -> Data.ByteString.Lazy.readFile f+ InputFile f -> Data.ByteString.Lazy.readFile f StandardInput -> Data.ByteString.Lazy.getContents term <- do@@ -639,13 +731,23 @@ Dhall.Core.TextLit (Dhall.Core.Chunks [] text) -> do Data.Text.IO.putStr text _ -> do- let invalidTypeExpected :: Expr X X+ let invalidTypeExpected :: Expr Void Void invalidTypeExpected = Dhall.Core.Text - let invalidTypeExpression :: Expr X X+ let invalidTypeExpression :: Expr Void Void invalidTypeExpression = normalizedExpression Control.Exception.throwIO (Dhall.InvalidType {..})++ Tags {..} -> do+ tags <- Dhall.Tags.generate input suffixes followSymlinks++ case output of+ OutputFile file ->+ System.IO.withFile file System.IO.WriteMode (`Data.Text.IO.hPutStr` tags)++ StandardOutput -> Data.Text.IO.putStrLn tags+ -- | Entry point for the @dhall@ executable main :: IO ()
src/Dhall/Parser/Combinators.hs view
@@ -38,6 +38,7 @@ import qualified Text.Parser.Combinators import qualified Text.Parser.Token.Style +-- | An exception annotated with a `Src` span data SourcedException e = SourcedException Src e instance Exception e => Exception (SourcedException e)
src/Dhall/Parser/Expression.hs view
@@ -39,6 +39,7 @@ import Dhall.Parser.Combinators import Dhall.Parser.Token +-- | Get the current source position getSourcePos :: Text.Megaparsec.MonadParsec e s m => m Text.Megaparsec.SourcePos getSourcePos =@@ -49,6 +50,7 @@ #endif {-# INLINE getSourcePos #-} +-- | Get the current source offset (in tokens) getOffset :: Text.Megaparsec.MonadParsec e s m => m Int #if MIN_VERSION_megaparsec(7, 0, 0) getOffset = Text.Megaparsec.stateOffset <$> Text.Megaparsec.getParserState@@ -57,6 +59,7 @@ #endif {-# INLINE getOffset #-} +-- | Set the current source offset setOffset :: Text.Megaparsec.MonadParsec e s m => Int -> m () #if MIN_VERSION_megaparsec(7, 0, 0) setOffset o = Text.Megaparsec.updateParserState $ \(Text.Megaparsec.State s _ pst) ->@@ -67,6 +70,9 @@ #endif {-# INLINE setOffset #-} +{-| Wrap a `Parser` to still match the same text but return only the `Src`+ span+-} src :: Parser a -> Parser Src src parser = do before <- getSourcePos@@ -74,6 +80,9 @@ after <- getSourcePos return (Src before after tokens) +{-| Wrap a `Parser` to still match the same text, but to wrap the resulting+ `Expr` in a `Note` constructor containing the `Src` span+-} noted :: Parser (Expr Src a) -> Parser (Expr Src a) noted parser = do before <- getSourcePos@@ -84,21 +93,39 @@ Note src₁ _ | laxSrcEq src₀ src₁ -> return e _ -> return (Note src₀ e) +{-| Parse a complete expression (with leading and trailing whitespace)++ This corresponds to the @complete-expression@ rule from the official+ grammar+-} completeExpression :: Parser a -> Parser (Expr Src a) completeExpression embedded = completeExpression_ where Parsers {..} = parsers embedded +{-| Parse an \"import expression\"++ This is not the same thing as @`fmap` `Embed`@. This parses any+ expression of the same or higher precedence as an import expression (such+ as a selector expression). For example, this parses @(1)@++ This corresponds to the @import-expression@ rule from the official grammar+-} importExpression :: Parser a -> Parser (Expr Src a) importExpression embedded = importExpression_ where Parsers {..} = parsers embedded +{-| For efficiency (and simplicity) we only expose two parsers from the+ result of the `parsers` function, since these are the only parsers needed+ outside of this module+-} data Parsers a = Parsers { completeExpression_ :: Parser (Expr Src a) , importExpression_ :: Parser (Expr Src a) } +-- | Given a parser for imports, parsers :: Parser a -> Parsers a parsers embedded = Parsers {..} where@@ -293,8 +320,20 @@ a <- embedded return (Embed a) - alternative1 = selectorExpression+ alternative1 = completionExpression + completionExpression = noted (do+ a <- selectorExpression++ mb <- optional (do+ _doubleColon++ selectorExpression )++ case mb of+ Nothing -> return a+ Just b -> return (RecordCompletion a b) )+ selectorExpression = noted (do a <- primitiveExpression @@ -338,7 +377,7 @@ b <- if isInfinite a then setOffset n *> fail "double out of bounds" else return a- return (DoubleLit b)+ return (DoubleLit (DhallDouble b)) alternative01 = do a <- try naturalLiteral@@ -352,8 +391,13 @@ alternative04 = (do _openBrace++ _ <- optional _comma+ a <- recordTypeOrLiteral+ _closeBrace+ return a ) <?> "record type or literal" alternative05 = unionType@@ -373,7 +417,7 @@ alternative09 = do a <- try doubleInfinity- return (DoubleLit a)+ return (DoubleLit (DhallDouble a)) builtin = do let predicate c =@@ -388,7 +432,7 @@ || c == 'F' || c == 'K' - let nan = (0.0/0.0)+ let nan = DhallDouble (0.0/0.0) c <- Text.Megaparsec.lookAhead (Text.Megaparsec.satisfy predicate) @@ -673,6 +717,8 @@ unionType = (do _openAngle + _ <- optional _bar+ let unionTypeEntry = do a <- anyLabel b <- optional (do _colon; expression)@@ -688,10 +734,18 @@ listLiteral = (do _openBracket++ _ <- optional _comma+ a <- Text.Megaparsec.sepBy expression _comma+ _closeBracket return (ListLit Nothing (Data.Sequence.fromList a)) ) <?> "list literal" +{-| Parse an environment variable import++ This corresponds to the @env@ rule from the official grammar+-} env :: Parser ImportType env = do _ <- Text.Parser.Char.text "env:"@@ -707,8 +761,9 @@ _ <- Text.Parser.Char.char '"' return a -localRaw :: Parser ImportType-localRaw =+-- | Parse a local import without trailing whitespace+localOnly :: Parser ImportType+localOnly = choice [ parentPath , herePath@@ -739,12 +794,20 @@ return (Local Absolute file) +{-| Parse a local import++ This corresponds to the @local@ rule from the official grammar+-} local :: Parser ImportType local = do- a <- localRaw+ a <- localOnly whitespace return a +{-| Parse an HTTP(S) import++ This corresponds to the @http@ rule from the official grammar+-} http :: Parser ImportType http = do url <- httpRaw@@ -754,11 +817,19 @@ importExpression import_ ) return (Remote (url { headers })) +{-| Parse a `Missing` import++ This corresponds to the @missing@ rule from the official grammar+-} missing :: Parser ImportType missing = do _missing return Missing +{-| Parse an `ImportType`++ This corresponds to the @import-type@ rule from the official grammar+-} importType_ :: Parser ImportType importType_ = do let predicate c =@@ -768,6 +839,10 @@ choice [ local, http, env, missing ] +{-| Parse a `Dhall.Crypto.SHA256Digest`++ This corresponds to the @hash@ rule from the official grammar+-} importHash_ :: Parser Dhall.Crypto.SHA256Digest importHash_ = do _ <- Text.Parser.Char.text "sha256:"@@ -781,12 +856,20 @@ Nothing -> fail "Invalid sha256 hash" Just h -> pure h +{-| Parse an `ImportHashed`++ This corresponds to the @import-hashed@ rule from the official grammar+-} importHashed_ :: Parser ImportHashed importHashed_ = do importType <- importType_ hash <- optional importHash_ return (ImportHashed {..}) +{-| Parse an `Import`++ This corresponds to the @import@ rule from the official grammar+-} import_ :: Parser Import import_ = (do importHashed <- importHashed_@@ -797,25 +880,14 @@ _as (_Text >> pure RawText) <|> (_Location >> pure Location) --- | Similar to `Dhall.Core.renderChunks` except that this doesn't bother to--- render interpolated expressions to avoid a `Buildable a` constraint. The--- interpolated contents are not necessary for computing how much to dedent a--- multi-line string------ This also doesn't include the surrounding quotes since they would interfere--- with the whitespace detection-renderChunks :: Chunks s a -> Text-renderChunks (Chunks a b) = foldMap renderChunk a <> b- where- renderChunk :: (Text, Expr s a) -> Text- renderChunk (c, _) = c <> "${x}"-+-- | Same as @Data.Text.splitOn@, except always returning a `NonEmpty` result splitOn :: Text -> Text -> NonEmpty Text splitOn needle haystack = case Data.Text.splitOn needle haystack of [] -> "" :| [] t : ts -> t :| ts +-- | Split `Chunks` by lines linesLiteral :: Chunks s a -> NonEmpty (Chunks s a) linesLiteral (Chunks [] suffix) = fmap (Chunks []) (splitOn "\n" suffix)@@ -832,15 +904,18 @@ Chunks pairs₁ suffix₁ :| chunks = linesLiteral (Chunks pairs₀ suffix₀) +-- | Flatten several `Chunks` back into a single `Chunks` by inserting newlines unlinesLiteral :: NonEmpty (Chunks s a) -> Chunks s a unlinesLiteral chunks = Data.Foldable.fold (Data.List.NonEmpty.intersperse "\n" chunks) +-- | Returns `True` if the `Chunks` represents a blank line emptyLine :: Chunks s a -> Bool emptyLine (Chunks [] "" ) = True emptyLine (Chunks [] "\r") = True -- So that `\r\n` is treated as a blank line emptyLine _ = False +-- | Return the leading whitespace for a `Chunks` literal leadingSpaces :: Chunks s a -> Text leadingSpaces chunks = Data.Text.takeWhile isSpace firstText where@@ -851,12 +926,16 @@ Chunks [] suffix -> suffix Chunks ((prefix, _) : _ ) _ -> prefix +-- | Drop the first @n@ characters for a `Chunks` literal dropLiteral :: Int -> Chunks s a -> Chunks s a dropLiteral n (Chunks [] suffix) = Chunks [] (Data.Text.drop n suffix) dropLiteral n (Chunks ((prefix, interpolation) : rest) suffix) = Chunks ((Data.Text.drop n prefix, interpolation) : rest) suffix +{-| Convert a single-quoted `Chunks` literal to the equivalent double-quoted+ `Chunks` literal+-} toDoubleQuoted :: Chunks Src a -> Chunks Src a toDoubleQuoted literal = unlinesLiteral (fmap (dropLiteral indent) literals)
src/Dhall/Parser/Token.hs view
@@ -103,6 +103,7 @@ _lambda, _forall, _arrow,+ _doubleColon, ) where import Dhall.Parser.Combinators@@ -133,15 +134,24 @@ import qualified Text.Parser.Token +-- | Returns `True` if the given `Char` is a valid Unicode codepoint validCodepoint :: Char -> Bool validCodepoint c = not (category == Char.Surrogate || category == Char.NotAssigned) where category = Char.generalCategory c +{-| Parse 0 or more whitespace characters (including comments)++ This corresponds to the @whsp@ rule in the official grammar+-} whitespace :: Parser () whitespace = Text.Parser.Combinators.skipMany whitespaceChunk +{-| Parse 1 or more whitespace characters (including comments)++ This corresponds to the @whsp1@ rule in the official grammar+-} nonemptyWhitespace :: Parser () nonemptyWhitespace = Text.Parser.Combinators.skipSome whitespaceChunk @@ -154,6 +164,10 @@ alphaNum :: Char -> Bool alphaNum c = alpha c || digit c +{-| Parse a hex digit (uppercase or lowercase)++ This corresponds to the @HEXDIG@ rule in the official grammar+-} hexdig :: Char -> Bool hexdig c = ('0' <= c && c <= '9')@@ -166,12 +180,21 @@ let negative = fmap (\_ -> negate) (Text.Parser.Char.char '-') positive <|> negative ) <?> "sign" +{-| Parse a `Double` literal++ This corresponds to the @double-literal@ rule from the official grammar+-} doubleLiteral :: Parser Double doubleLiteral = (do sign <- signPrefix <|> pure id a <- Text.Parser.Token.double return (sign a) ) <?> "double literal" +{-| Parse a signed @Infinity@++ This corresponds to the @minus-infinity-literal@ and @plus-infinity-literal@+ rules from the official grammar+-} doubleInfinity :: Parser Double doubleInfinity = (do let negative = fmap (\_ -> negate) (Text.Parser.Char.char '-')@@ -179,6 +202,10 @@ a <- Text.Parser.Char.text "Infinity" >> whitespace >> return (1.0/0.0) return (sign a) ) <?> "double infinity" +{-| Parse an `Integer` literal++ This corresponds to the @integer-literal@ rule from the official grammar+-} integerLiteral :: Parser Integer integerLiteral = (do sign <- signPrefix@@ -186,12 +213,22 @@ whitespace return (sign a) ) <?> "integer literal" +{-| Parse a `Natural` literal ++ This corresponds to the @natural-literal@ rule from the official grammar+-} naturalLiteral :: Parser Natural naturalLiteral = (do a <- Text.Megaparsec.Char.Lexer.decimal whitespace return a ) <?> "natural literal" +{-| Parse an identifier (i.e. a variable or built-in)++ Variables can have an optional index to disambiguate shadowed variables++ This corresponds to the @identifier@ rule from the official grammar+-} identifier :: Parser Var identifier = do x <- label@@ -216,6 +253,7 @@ where predicate c = c == ' ' || c == '\t' || c == '\n' +-- | Parse a hexademical number and convert to the corresponding `Int` hexNumber :: Parser Int hexNumber = choice [ hexDigit, hexUpper, hexLower ] where@@ -312,6 +350,12 @@ '\x20' <= c && c <= '\x5F' || '\x61' <= c && c <= '\x7E' +{-| Parse a braced sequence of comma-separated labels++ For example, this is used to parse the record projection syntax++ This corresponds to the @labels@ rule in the official grammar+-} labels :: Parser (Set Text) labels = do _openBrace@@ -326,22 +370,37 @@ xs <- many (do _ <- _comma; anyLabel) noDuplicates (x : xs) -+-- | Parse a label without parsing trailing whitespace labelOnly :: Parser Text labelOnly = backtickLabel <|> simpleLabel False <?> "label" +{-| Parse a label (e.g. a variable\/field\/alternative name)++ Rejects labels that match built-in names (e.g. @Natural/even@)++ This corresponds to the @nonreserved-label@ rule in the official grammar+-} label :: Parser Text label = (do t <- backtickLabel <|> simpleLabel False whitespace return t ) <?> "label" +{-| Same as `label` except that built-in names are allowed++ This corresponds to the @any-label@ rule in the official grammar+-} anyLabel :: Parser Text anyLabel = (do t <- backtickLabel <|> simpleLabel True whitespace return t ) <?> "any label" +{-| Parse a valid Bash environment variable name++ This corresponds to the @bash-environment-variable@ rule in the official+ grammar+-} bashEnvironmentVariable :: Parser Text bashEnvironmentVariable = satisfy predicate0 <> star (satisfy predicate1) where@@ -349,6 +408,12 @@ predicate1 c = alphaNum c || c == '_' +{-| Parse a valid POSIX environment variable name, which permits a wider range+ of characters than a Bash environment variable name++ This corresponds to the @posix-environment-variable@ rule in the official+ grammar+-} posixEnvironmentVariable :: Parser Text posixEnvironmentVariable = plus posixEnvironmentVariableCharacter @@ -385,8 +450,12 @@ || ('\x23' <= c && c <= '\x2E') || ('\x30' <= c && c <= '\x10FFFF') +{-| The `pathComponent` function uses this type to distinguish whether to parse+ a URL path component or a file path component+-} data ComponentType = URLComponent | FileComponent +-- | Parse a path component pathComponent :: ComponentType -> Parser Text pathComponent componentType = do _ <- "/" :: Parser Text@@ -411,6 +480,7 @@ quotedPathData <|> pathData +-- | Parse a `File` file_ :: ComponentType -> Parser File file_ componentType = do let emptyPath =@@ -431,6 +501,10 @@ *> ((("s" :: Parser Text) *> pure HTTPS) <|> pure HTTP) <* ("://" :: Parser Text) +{-| Parse an HTTP(S) URL without trailing whitespace++ This corresponds to the @http-raw@ rule in the official grammar+-} httpRaw :: Parser URL httpRaw = do scheme <- scheme_@@ -590,248 +664,468 @@ keyword :: Data.Text.Text -> Parser () keyword x = try (do _ <- Text.Parser.Char.text x; nonemptyWhitespace) +{-| Parse the @if@ keyword++ This corresponds to the @if@ rule from the official grammar+-} _if :: Parser () _if = keyword "if" +{-| Parse the @then@ keyword++ This corresponds to the @then@ rule from the official grammar+-} _then :: Parser () _then = keyword "then" +{-| Parse the @else@ keyword++ This corresponds to the @else@ rule from the official grammar+-} _else :: Parser () _else = keyword "else" +-- | Parse the @let@ keyword without trailing whitespace _letOnly :: Parser () _letOnly = keywordOnly "let" +{-| Parse the @let@ keyword++ This corresponds to the @let@ rule from the official grammar+-} _let :: Parser () _let = keyword "let" +{-| Parse the @in@ keyword++ This corresponds to the @in@ rule from the official grammar+-} _in :: Parser () _in = keyword "in" +{-| Parse the @as@ keyword++ This corresponds to the @as@ rule from the official grammar+-} _as :: Parser () _as = keyword "as" +{-| Parse the @using@ keyword++ This corresponds to the @using@ rule from the official grammar+-} _using :: Parser () _using = keyword "using" +{-| Parse the @merge@ keyword++ This corresponds to the @merge@ rule from the official grammar+-} _merge :: Parser () _merge = keyword "merge" +{-| Parse the @toMap@ keyword++ This corresponds to the @toMap@ rule from the official grammar+-} _toMap :: Parser () _toMap = keyword "toMap" +{-| Parse the @assert@ keyword++ This corresponds to the @assert@ rule from the official grammar+-} _assert :: Parser () _assert = keyword "assert" +{-| Parse the @Some@ built-in++ This corresponds to the @Some@ rule from the official grammar+-} _Some :: Parser () _Some = keyword "Some" +{-| Parse the @None@ built-in++ This corresponds to the @None@ rule from the official grammar+-} _None :: Parser () _None = reserved "None" +{-| Parse the @Natural/fold@ built-in++ This corresponds to the @Natural-fold@ rule from the official grammar+-} _NaturalFold :: Parser () _NaturalFold = reserved "Natural/fold" +{-| Parse the @Natural/build@ built-in++ This corresponds to the @Natural-build@ rule from the official grammar+-} _NaturalBuild :: Parser () _NaturalBuild = reserved "Natural/build" +{-| Parse the @Natural/isZero@ built-in++ This corresponds to the @Natural-isZero@ rule from the official grammar+-} _NaturalIsZero :: Parser () _NaturalIsZero = reserved "Natural/isZero" +{-| Parse the @Natural/even@ built-in++ This corresponds to the @Natural-even@ rule from the official grammar+-} _NaturalEven :: Parser () _NaturalEven = reserved "Natural/even" +{-| Parse the @Natural/odd@ built-in++ This corresponds to the @Natural-odd@ rule from the official grammar+-} _NaturalOdd :: Parser () _NaturalOdd = reserved "Natural/odd" +{-| Parse the @Natural/toInteger@ built-in++ This corresponds to the @Natural-toInteger@ rule from the official grammar+-} _NaturalToInteger :: Parser () _NaturalToInteger = reserved "Natural/toInteger" +{-| Parse the @Natural/show@ built-in++ This corresponds to the @Natural-show@ rule from the official grammar+-} _NaturalShow :: Parser () _NaturalShow = reserved "Natural/show" +{-| Parse the @Natural/subtract@ built-in++ This corresponds to the @Natural-subtract@ rule from the official grammar+-} _NaturalSubtract :: Parser () _NaturalSubtract = reserved "Natural/subtract" +{-| Parse the @Integer/show@ built-in++ This corresponds to the @Integer-show@ rule from the official grammar+-} _IntegerShow :: Parser () _IntegerShow = reserved "Integer/show" +{-| Parse the @Integer/toDouble@ built-in++ This corresponds to the @Integer-toDouble@ rule from the official grammar+-} _IntegerToDouble :: Parser () _IntegerToDouble = reserved "Integer/toDouble" +{-| Parse the @Double/show@ built-in++ This corresponds to the @Double-show@ rule from the official grammar+-} _DoubleShow :: Parser () _DoubleShow = reserved "Double/show" +{-| Parse the @List/build@ built-in++ This corresponds to the @List-build@ rule from the official grammar+-} _ListBuild :: Parser () _ListBuild = reserved "List/build" +{-| Parse the @List/fold@ built-in++ This corresponds to the @List-fold@ rule from the official grammar+-} _ListFold :: Parser () _ListFold = reserved "List/fold" +{-| Parse the @List/length@ built-in++ This corresponds to the @List-length@ rule from the official grammar+-} _ListLength :: Parser () _ListLength = reserved "List/length" +{-| Parse the @List/head@ built-in++ This corresponds to the @List-head@ rule from the official grammar+-} _ListHead :: Parser () _ListHead = reserved "List/head" +{-| Parse the @List/last@ built-in++ This corresponds to the @List-last@ rule from the official grammar+-} _ListLast :: Parser () _ListLast = reserved "List/last" +{-| Parse the @List/indexed@ built-in++ This corresponds to the @List-indexed@ rule from the official grammar+-} _ListIndexed :: Parser () _ListIndexed = reserved "List/indexed" +{-| Parse the @List/reverse@ built-in++ This corresponds to the @List-reverse@ rule from the official grammar+-} _ListReverse :: Parser () _ListReverse = reserved "List/reverse" +{-| Parse the @Optional/fold@ built-in++ This corresponds to the @Optional-fold@ rule from the official grammar+-} _OptionalFold :: Parser () _OptionalFold = reserved "Optional/fold" +{-| Parse the @Optional/build@ built-in++ This corresponds to the @Optional-build@ rule from the official grammar+-} _OptionalBuild :: Parser () _OptionalBuild = reserved "Optional/build" +{-| Parse the @Bool@ built-in++ This corresponds to the @Bool@ rule from the official grammar+-} _Bool :: Parser () _Bool = reserved "Bool" +{-| Parse the @Optional@ built-in++ This corresponds to the @Optional@ rule from the official grammar+-} _Optional :: Parser () _Optional = reserved "Optional" +{-| Parse the @Natural@ built-in++ This corresponds to the @Natural@ rule from the official grammar+-} _Natural :: Parser () _Natural = reserved "Natural" +{-| Parse the @Integer@ built-in++ This corresponds to the @Integer@ rule from the official grammar+-} _Integer :: Parser () _Integer = reserved "Integer" +{-| Parse the @Double@ built-in++ This corresponds to the @Double@ rule from the official grammar+-} _Double :: Parser () _Double = reserved "Double" +{-| Parse the @Text@ built-in++ This corresponds to the @Text@ rule from the official grammar+-} _Text :: Parser () _Text = reserved "Text" +{-| Parse the @Text/show@ built-in++ This corresponds to the @Text-show@ rule from the official grammar+-} _TextShow :: Parser () _TextShow = reserved "Text/show" +{-| Parse the @List@ built-in++ This corresponds to the @List@ rule from the official grammar+-} _List :: Parser () _List = reserved "List" +{-| Parse the @True@ built-in++ This corresponds to the @True@ rule from the official grammar+-} _True :: Parser () _True = reserved "True" +{-| Parse the @False@ built-in++ This corresponds to the @False@ rule from the official grammar+-} _False :: Parser () _False = reserved "False" +{-| Parse a @NaN@ literal++ This corresponds to the @NaN@ rule from the official grammar+-} _NaN :: Parser () _NaN = reserved "NaN" +{-| Parse the @Type@ built-in++ This corresponds to the @Type@ rule from the official grammar+-} _Type :: Parser () _Type = reserved "Type" +{-| Parse the @Kind@ built-in++ This corresponds to the @Kind@ rule from the official grammar+-} _Kind :: Parser () _Kind = reserved "Kind" +{-| Parse the @Sort@ built-in++ This corresponds to the @Sort@ rule from the official grammar+-} _Sort :: Parser () _Sort = reserved "Sort" +{-| Parse the @Location@ keyword++ This corresponds to the @Location@ rule from the official grammar+-} _Location :: Parser () _Location = reserved "Location" +-- | Parse the @=@ symbol without trailing whitespace _equalOnly :: Parser () _equalOnly = reservedCharOnly '=' +-- | Parse the @=@ symbol _equal :: Parser () _equal = reservedChar '=' +-- | Parse the @||@ symbol _or :: Parser () _or = reserved "||" +-- | Parse the @+@ symbol _plus :: Parser () _plus = reservedChar '+' +-- | Parse the @++@ symbol _textAppend :: Parser () _textAppend = reserved "++" +-- | Parse the @#@ symbol _listAppend :: Parser () _listAppend = reservedChar '#' +-- | Parse the @&&@ symbol _and :: Parser () _and = reserved "&&" +-- | Parse the @*@ symbol _times :: Parser () _times = reservedChar '*' +-- | Parse the @==@ symbol _doubleEqual :: Parser () _doubleEqual = reserved "==" +-- | Parse the @!=@ symbol _notEqual :: Parser () _notEqual = reserved "!=" +-- | Parse the @.@ symbol _dot :: Parser () _dot = reservedChar '.' +-- | Parse the @{@ symbol _openBrace :: Parser () _openBrace = reservedChar '{' +-- | Parse the @}@ symbol _closeBrace :: Parser () _closeBrace = reservedChar '}' +-- | Parse the @[@] symbol _openBracket :: Parser () _openBracket = reservedChar '[' +-- | Parse the @]@ symbol _closeBracket :: Parser () _closeBracket = reservedChar ']' +-- | Parse the @<@ symbol _openAngle :: Parser () _openAngle = reservedChar '<' +-- | Parse the @>@ symbol _closeAngle :: Parser () _closeAngle = reservedChar '>' +-- | Parse the @|@ symbol _bar :: Parser () _bar = reservedChar '|' +-- | Parse the @,@ symbol _comma :: Parser () _comma = reservedChar ',' +-- | Parse the @(@ symbol _openParens :: Parser () _openParens = reservedChar '(' +-- | Parse the @)@ symbol _closeParens :: Parser () _closeParens = reservedChar ')' +-- | Parse the @:@ symbol without trailing whitespace _colonOnly :: Parser () _colonOnly = reservedCharOnly ':' +-- | Parse the @:@ symbol _colon :: Parser () _colon = reservedChar ':' +-- | Parse the @\@@ symbol _at :: Parser () _at = reservedChar '@' +-- | Parse the equivalence symbol (@===@ or @≡@) _equivalent :: Parser () _equivalent = do void (Text.Parser.Char.char '≡' <?> "\"≡\"") <|> void (Text.Parser.Char.text "===") whitespace +-- | Parse the @missing@ keyword _missing :: Parser ()-_missing = reserved "missing"+_missing = keyword "missing" +-- | Parse the @?@ symbol _importAlt :: Parser () _importAlt = reservedChar '?' +-- | Parse the record combine operator (@/\\@ or @∧@) _combine :: Parser () _combine = do void (Text.Parser.Char.char '∧' <?> "\"∧\"") <|> void (Text.Parser.Char.text "/\\") whitespace +-- | Parse the record type combine operator (@//\\\\@ or @⩓@) _combineTypes :: Parser () _combineTypes = do void (Text.Parser.Char.char '⩓' <?> "\"⩓\"") <|> void (Text.Parser.Char.text "//\\\\") whitespace +-- | Parse the record \"prefer\" operator (@//@ or @⫽@) _prefer :: Parser () _prefer = do void (Text.Parser.Char.char '⫽' <?> "\"⫽\"") <|> void (Text.Parser.Char.text "//") whitespace +-- | Parse a lambda (@\\@ or @λ@) _lambda :: Parser () _lambda = do _ <- Text.Parser.Char.satisfy predicate@@ -841,12 +1135,18 @@ predicate '\\' = True predicate _ = False +-- | Parse a forall (@forall@ or @∀@) _forall :: Parser () _forall = do void (Text.Parser.Char.char '∀' <?> "\"∀\"") <|> void (Text.Parser.Char.text "forall") whitespace +-- | Parse a right arrow (@->@ or @→@) _arrow :: Parser () _arrow = do void (Text.Parser.Char.char '→' <?> "\"→\"") <|> void (Text.Parser.Char.text "->") whitespace++-- | Parse a double colon (@::@)+_doubleColon :: Parser ()+_doubleColon = reserved "::"
src/Dhall/Pretty/Internal.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} @@ -18,8 +19,8 @@ , prettyCharacterSet , prettyVar- , pretty- , escapeText+ , pretty_+ , escapeText_ , prettyConst , prettyLabel@@ -58,8 +59,6 @@ , rparen ) where -import Dhall.Core- #if MIN_VERSION_base(4,8,0) #else import Control.Applicative (Applicative(..), (<$>))@@ -71,6 +70,7 @@ import Dhall.Map (Map) import Dhall.Set (Set) import Dhall.Src (Src(..))+import Dhall.Syntax import Numeric.Natural (Natural) import Prelude hiding (succ) import qualified Data.Text.Prettyprint.Doc.Render.Terminal as Terminal@@ -108,6 +108,7 @@ annToAnsiStyle Builtin = Terminal.underlined annToAnsiStyle Operator = Terminal.bold <> Terminal.colorDull Terminal.Green +-- | This type determines whether to render code as `ASCII` or `Unicode` data CharacterSet = ASCII | Unicode -- | Pretty print an expression@@ -138,29 +139,28 @@ >>> let unusedSourcePos = Text.Megaparsec.SourcePos "" (Text.Megaparsec.mkPos 1) (Text.Megaparsec.mkPos 1) >>> let nonEmptySrc = Src unusedSourcePos unusedSourcePos "-- Documentation for x\n"- >>> "let" <> renderSrc (Just nonEmptySrc) " " <> "x = 1 in x"+ >>> "let" <> " " <> renderSrc id (Just nonEmptySrc) <> "x = 1 in x" let -- Documentation for x x = 1 in x >>> let emptySrc = Src unusedSourcePos unusedSourcePos " "- >>> "let" <> renderSrc (Just emptySrc) " " <> "x = 1 in x"+ >>> "let" <> " " <> renderSrc id (Just emptySrc) <> "x = 1 in x" let x = 1 in x- >>> "let" <> renderSrc Nothing " " <> "x = 1 in x"+ >>> "let" <> " " <> renderSrc id Nothing <> "x = 1 in x" let x = 1 in x -} renderSrc- :: Maybe Src+ :: (Text -> Text)+ -- ^ Used to preprocess the comment string (e.g. to strip whitespace)+ -> Maybe Src -- ^ Source span to render (if present) -> Doc Ann- -- ^ Used as the prefix (when the source span contains a comment) and as a- -- fallback (when the source span is absent or comment-free)- -> Doc Ann-renderSrc (Just (Src {..})) prefix+renderSrc strip (Just (Src {..})) | not (Text.all isWhitespace srcText) =- prefix <> Pretty.align (Pretty.concatWith f newLines <> suffix)+ Pretty.align (Pretty.concatWith f newLines <> suffix) where horizontalSpace c = c == ' ' || c == '\t' - strippedText = Text.dropAround horizontalSpace srcText+ strippedText = strip srcText suffix = if Text.null strippedText@@ -195,8 +195,8 @@ in Pretty.pretty l0 : map perLine (l1 : ls) f x y = x <> Pretty.hardline <> y-renderSrc _ prefix =- prefix+renderSrc _ _ =+ mempty -- Annotation helpers keyword, syntax, label, literal, builtin, operator :: Doc Ann -> Doc Ann@@ -236,6 +236,9 @@ rarrow Unicode = syntax "→" rarrow ASCII = syntax "->" +doubleColon :: Doc Ann+doubleColon = syntax "::"+ -- | Pretty-print a list list :: [Doc Ann] -> Doc Ann list [] = lbracket <> rbracket@@ -275,6 +278,30 @@ rbrace docs +hangingBraces :: [(Doc Ann, Doc Ann)] -> Doc Ann+hangingBraces [] =+ lbrace <> rbrace+hangingBraces docs =+ Pretty.group+ (Pretty.flatAlt+ ( lbrace+ <> Pretty.hardline+ <> mconcat (zipWith combineLong (repeat separator) docsLong)+ <> rbrace+ )+ (mconcat (zipWith (<>) (beginShort : repeat separator) docsShort) <> space <> rbrace)+ )+ where+ separator = comma <> space++ docsShort = fmap fst docs++ docsLong = fmap snd docs++ beginShort = lbrace <> space++ combineLong x y = x <> y <> Pretty.hardline+ -- | Pretty-print anonymous functions and function types arrows :: CharacterSet -> [(Doc Ann, Doc Ann)] -> Doc Ann arrows ASCII =@@ -430,6 +457,17 @@ prettyVar (V x 0) = label (Pretty.unAnnotate (prettyLabel x)) prettyVar (V x n) = label (Pretty.unAnnotate (prettyLabel x <> "@" <> prettyInt n)) +{- There is a close correspondence between the pretty-printers in 'prettyCharacterSet'+ and the sub-parsers in 'Dhall.Parser.Expression.parsers'. Most pretty-printers are+ named after the corresponding parser and the relationship between pretty-printers+ exactly matches the relationship between parsers. This leads to the nice emergent+ property of automatically getting all the parentheses and precedences right.++ This approach has one major disadvantage: you can get an infinite loop if+ you add a new constructor to the syntax tree without adding a matching+ case the corresponding builder.+-}+ {-| Pretty-print an 'Expr' using the given 'CharacterSet'. 'prettyCharacterSet' largely ignores 'Note's. 'Note's do however matter for@@ -519,37 +557,40 @@ where MultiLet as b = multiLet a0 b0 + stripSpaces = Text.dropAround (\c -> c == ' ' || c == '\t')++ -- Strip a single newline character. Needed to ensure idempotency in+ -- cases where we add hard line breaks.+ stripNewline t =+ case Text.uncons t' of+ Just ('\n', t'') -> stripSpaces t''+ _ -> t'+ where t' = stripSpaces t+ docA (Binding src0 c src1 Nothing src2 e) = Pretty.group (Pretty.flatAlt long short) where long = keyword "let" <> space <> Pretty.align- ( renderSrc src0 mempty- <> prettyLabel c <> renderSrc src1 space- <> equals <> renderSrc src2 Pretty.hardline+ ( renderSrc stripSpaces src0+ <> prettyLabel c <> space <> renderSrc stripSpaces src1+ <> equals <> Pretty.hardline <> renderSrc stripNewline src2 <> " " <> prettyExpression e ) - short = keyword "let" <> renderSrc src0 space- <> prettyLabel c <> renderSrc src1 space- <> equals <> renderSrc src2 space+ short = keyword "let" <> space <> renderSrc stripSpaces src0+ <> prettyLabel c <> space <> renderSrc stripSpaces src1+ <> equals <> space <> renderSrc stripSpaces src2 <> prettyExpression e docA (Binding src0 c src1 (Just (src3, d)) src2 e) =- Pretty.group (Pretty.flatAlt long short)- where- long = keyword "let" <> space- <> Pretty.align- ( renderSrc src0 mempty- <> prettyLabel c <> renderSrc src1 Pretty.hardline- <> colon <> renderSrc src3 space <> prettyExpression d <> Pretty.hardline <> equals <> renderSrc src2 space- <> prettyExpression e- )-- short = keyword "let" <> renderSrc src0 space- <> prettyLabel c <> renderSrc src1 space- <> colon <> renderSrc src3 space- <> prettyExpression d <> space <> equals <> renderSrc src2 space+ keyword "let" <> space+ <> Pretty.align+ ( renderSrc stripSpaces src0+ <> prettyLabel c <> Pretty.hardline <> renderSrc stripNewline src1+ <> colon <> space <> renderSrc stripSpaces src3 <> prettyExpression d <> Pretty.hardline+ <> equals <> space <> renderSrc stripSpaces src2 <> prettyExpression e+ ) docB = ( keyword "in" <> " " <> prettyExpression b@@ -616,22 +657,6 @@ <> prettyImportExpression b <> space <> colon <> space <> prettyApplicationExpression c- prettyAnnotatedExpression (Merge a b Nothing) =- Pretty.group (Pretty.flatAlt long short)- where- long =- Pretty.align- ( keyword "merge"- <> Pretty.hardline- <> Pretty.indent 2 (prettyImportExpression a)- <> Pretty.hardline- <> Pretty.indent 2 (prettyImportExpression b)- )-- short = keyword "merge" <> space- <> prettyImportExpression a- <> " "- <> prettyImportExpression b prettyAnnotatedExpression (ToMap a (Just b)) = Pretty.group (Pretty.flatAlt long short) where@@ -649,18 +674,6 @@ <> prettyImportExpression a <> space <> colon <> space <> prettyApplicationExpression b- prettyAnnotatedExpression (ToMap a Nothing) =- Pretty.group (Pretty.flatAlt long short)- where- long =- Pretty.align- ( keyword "toMap"- <> Pretty.hardline- <> Pretty.indent 2 (prettyImportExpression a)- )-- short = keyword "toMap" <> space- <> prettyImportExpression a prettyAnnotatedExpression a0@(Annot _ _) = enclose' ""@@ -854,18 +867,23 @@ prettyApplicationExpression a0 prettyApplicationExpression :: Pretty a => Expr Src a -> Doc Ann- prettyApplicationExpression a0 = case a0 of- App _ _ -> result- Some _ -> result- Note _ b -> prettyApplicationExpression b- _ -> prettyImportExpression a0+ prettyApplicationExpression = go [] where- result = enclose' "" "" " " "" (reverse (docs a0))+ go args = \case+ App a b -> go (b : args) a+ Some a -> app (builtin "Some") (a : args)+ Merge a b Nothing -> app (keyword "merge") (a : b : args)+ ToMap a Nothing -> app (keyword "toMap") (a : args)+ Note _ b -> go args b+ e | null args -> prettyImportExpression e -- just a performance optimization+ | otherwise -> app (prettyImportExpression e) args - docs (App a b) = ( prettyImportExpression b, Pretty.indent 2 (prettyImportExpression b) ) : docs a- docs (Some a) = map duplicate [ prettyImportExpression a , builtin "Some" ]- docs (Note _ b) = docs b- docs b = map duplicate [ prettyImportExpression b ]+ app f args =+ enclose'+ "" "" " " ""+ ( duplicate f+ : map (fmap (Pretty.indent 2) . duplicate . prettyImportExpression) args+ ) prettyImportExpression :: Pretty a => Expr Src a -> Doc Ann prettyImportExpression (Embed a) =@@ -873,6 +891,24 @@ prettyImportExpression (Note _ a) = prettyImportExpression a prettyImportExpression a0 =+ prettyCompletionExpression a0++ prettyCompletionExpression :: Pretty a => Expr Src a -> Doc Ann+ prettyCompletionExpression (RecordCompletion a b) =+ case shallowDenote b of+ RecordLit kvs ->+ Pretty.align+ ( prettySelectorExpression a+ <> doubleColon+ <> prettyCompletionLit kvs+ )+ _ -> prettySelectorExpression a+ <> doubleColon+ <> prettySelectorExpression b++ prettyCompletionExpression (Note _ a) =+ prettyCompletionExpression a+ prettyCompletionExpression a0 = prettySelectorExpression a0 prettySelectorExpression :: Pretty a => Expr Src a -> Doc Ann@@ -963,7 +999,7 @@ | otherwise = prettyNumber a prettyPrimitiveExpression (NaturalLit a) = prettyNatural a- prettyPrimitiveExpression (DoubleLit a) =+ prettyPrimitiveExpression (DoubleLit (DhallDouble a)) = prettyDouble a prettyPrimitiveExpression (TextLit a) = prettyChunks a@@ -1011,9 +1047,17 @@ prettyRecordLit a | Data.Foldable.null a = lbrace <> equals <> rbrace- | otherwise- = braces (map (prettyKeyValue equals) (Dhall.Map.toList a))+ | otherwise =+ braces (map (prettyKeyValue equals) (Dhall.Map.toList a)) + prettyCompletionLit+ :: Pretty a => Map Text (Expr Src a) -> Doc Ann+ prettyCompletionLit a+ | Data.Foldable.null a =+ lbrace <> equals <> rbrace+ | otherwise =+ hangingBraces (map (prettyKeyValue equals) (Dhall.Map.toList a))+ prettyAlternative (key, Just val) = prettyKeyValue colon (key, val) prettyAlternative (key, Nothing ) = duplicate (prettyAnyLabel key) @@ -1060,11 +1104,11 @@ <> prettyExpression d <> syntax rbrace - prettyText t = literal (Pretty.pretty (escapeText t))+ prettyText t = literal (Pretty.pretty (escapeText_ t)) -- | Pretty-print a value-pretty :: Pretty a => a -> Text-pretty = Pretty.renderStrict . Pretty.layoutPretty options . Pretty.pretty+pretty_ :: Pretty a => a -> Text+pretty_ = Pretty.renderStrict . Pretty.layoutPretty options . Pretty.pretty where options = Pretty.LayoutOptions { Pretty.layoutPageWidth = Pretty.Unbounded } @@ -1083,8 +1127,8 @@ Note that the result does not include surrounding quotes -}-escapeText :: Text -> Text-escapeText text = Text.concatMap adapt text+escapeText_ :: Text -> Text+escapeText_ text = Text.concatMap adapt text where adapt c | '\x20' <= c && c <= '\x21' = Text.singleton c
src/Dhall/Pretty/Internal.hs-boot view
@@ -1,9 +1,8 @@ module Dhall.Pretty.Internal where -import Data.Text (Text) import Data.Text.Prettyprint.Doc (Pretty, Doc) -import {-# SOURCE #-} Dhall.Core+import {-# SOURCE #-} Dhall.Syntax data Ann @@ -12,7 +11,3 @@ prettyConst :: Const -> Doc Ann prettyExpr :: Pretty a => Expr s a -> Doc Ann--pretty :: Pretty a => a -> Text--escapeText :: Text -> Text
src/Dhall/Repl.hs view
@@ -22,6 +22,7 @@ import Data.Maybe ( mapMaybe ) import Data.Semigroup ((<>)) import Data.Text ( Text )+import Data.Void (Void) import Dhall.Context (Context) import Dhall.Import (hashExpressionToCode) import Dhall.Src (Src)@@ -100,8 +101,8 @@ data Binding = Binding- { bindingExpr :: Dhall.Expr Dhall.Src Dhall.X- , bindingType :: Dhall.Expr Dhall.Src Dhall.X+ { bindingExpr :: Dhall.Expr Dhall.Src Void+ , bindingType :: Dhall.Expr Dhall.Src Void } @@ -116,7 +117,7 @@ parseAndLoad- :: MonadIO m => String -> m ( Dhall.Expr Dhall.Src Dhall.X )+ :: MonadIO m => String -> m ( Dhall.Expr Dhall.Src Void) parseAndLoad src = do parsed <- case Dhall.exprFromText "(stdin)" ( Text.pack src ) of@@ -158,16 +159,13 @@ exprType <- typeCheck loaded - exprType' <-- normalize exprType-- output exprType'+ output exprType applyContext :: Context Binding- -> Dhall.Expr Dhall.Src Dhall.X- -> Dhall.Expr Dhall.Src Dhall.X+ -> Dhall.Expr Dhall.Src Void+ -> Dhall.Expr Dhall.Src Void applyContext context expression = Dhall.Core.wrapInLets bindings expression where@@ -180,7 +178,7 @@ normalize :: MonadState Env m- => Dhall.Expr Dhall.Src Dhall.X -> m ( Dhall.Expr t Dhall.X )+ => Dhall.Expr Dhall.Src Void -> m ( Dhall.Expr t Void ) normalize e = do env <- get @@ -189,7 +187,7 @@ typeCheck :: ( MonadIO m, MonadState Env m )- => Dhall.Expr Dhall.Src Dhall.X -> m ( Dhall.Expr Dhall.Src Dhall.X )+ => Dhall.Expr Dhall.Src Void -> m ( Dhall.Expr Dhall.Src Void ) typeCheck expression = do env <- get @@ -546,7 +544,7 @@ listCompletion = map simpleCompletion . filter (word `isPrefixOf`) algebraicComplete- :: [Text.Text] -> Dhall.Expr Dhall.Src Dhall.X -> [Text.Text]+ :: [Text.Text] -> Dhall.Expr Dhall.Src Void -> [Text.Text] algebraicComplete subFields expr = let keys = fmap ("." <>) . Map.keys
src/Dhall/Set.hs view
@@ -35,9 +35,13 @@ import qualified Data.Sequence import qualified Data.Foldable --- Invariant: In @Set set seq@, @toAscList set == sort (toList seq)@.+{-| This is a variation on @"Data.Set".`Data.Set.Set`@ that remembers the+ original order of elements. This ensures that ordering is not lost when+ formatting Dhall code+-} data Set a = Set (Data.Set.Set a) (Seq a) deriving (Generic, Show, Data, NFData)+-- Invariant: In @Set set seq@, @toAscList set == sort (toList seq)@. instance Eq a => Eq (Set a) where (Set _ x) == (Set _ y) = x == y@@ -53,30 +57,37 @@ foldMap f (Set _ x) = foldMap f x {-# INLINABLE foldMap #-} +-- | Convert to an unordered @"Data.Set".`Data.Set.Set`@ toSet :: Set a -> Data.Set.Set a toSet (Set s _) = s +-- | Convert to an ordered `Seq` toSeq :: Set a -> Seq a toSeq (Set _ xs) = xs +-- | Convert a `Set` to a list, preserving the original order of the elements toList :: Set a -> [a] toList = Data.Foldable.toList +-- | Convert a list to a `Set`, remembering the element order+fromList :: Ord a => [a] -> Set a+fromList = foldl' (flip append) empty -- O(n log n) time complexity, O(n) space complexity. -- Implementing it this way is a little silly, but is faster than (nub xs). -- n.b. toList . fromList = id, only if the list elements are unique-fromList :: Ord a => [a] -> Set a-fromList = foldl' (flip append) empty +-- | Convert a @"Data.Set".`Data.Set.Set`@ to a sorted `Set` fromSet :: Data.Set.Set a -> Set a fromSet s = Set s (Data.Sequence.fromList (Data.Set.elems s)) --- O(log n) time complexity.+-- | Append an element to the end of a `Set` append :: Ord a => a -> Set a -> Set a append x os@(Set s xs) | Data.Set.member x s = os | otherwise = Set (Data.Set.insert x s) (xs |> x)+-- O(log n) time complexity. +-- | The empty `Set` empty :: Set a empty = Set Data.Set.empty Data.Sequence.empty
+ src/Dhall/Syntax.hs view
@@ -0,0 +1,1182 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE UnicodeSyntax #-}++{-| This module contains the core syntax types and optics for them.++'reservedIdentifiers', 'denote' and friends are included because they are+involved in a dependency circle with "Dhall.Pretty.Internal".+-}++module Dhall.Syntax (+ -- * 'Expr'+ Const(..)+ , Var(..)+ , Binding(..)+ , makeBinding+ , Chunks(..)+ , DhallDouble(..)+ , Expr(..)++ -- ** 'Let'-blocks+ , MultiLet(..)+ , multiLet+ , wrapInLets++ -- ** Optics+ , subExpressions+ , chunkExprs+ , bindingExprs++ -- ** Handling 'Note's+ , denote+ , renote+ , shallowDenote++ -- * 'Import'+ , Directory(..)+ , File(..)+ , FilePrefix(..)+ , Import(..)+ , ImportHashed(..)+ , ImportMode(..)+ , ImportType(..)+ , URL(..)+ , Scheme(..)+ , pathCharacter++ -- * Reserved identifiers+ , reservedIdentifiers+ ) where++import Control.DeepSeq (NFData)+import Data.Bifunctor (Bifunctor(..))+import Data.Bits (xor)+import Data.Data (Data)+import Data.Foldable+import Data.HashSet (HashSet)+import Data.List.NonEmpty (NonEmpty(..))+import Data.String (IsString(..))+import Data.Semigroup (Semigroup(..))+import Data.Sequence (Seq)+import Data.Text (Text)+import Data.Text.Prettyprint.Doc (Doc, Pretty)+import Data.Traversable+import Data.Void (Void)+import Dhall.Map (Map)+import Dhall.Set (Set)+import Dhall.Src (Src(..))+import {-# SOURCE #-} Dhall.Pretty.Internal+import GHC.Generics (Generic)+import Instances.TH.Lift ()+import Language.Haskell.TH.Syntax (Lift)+import Numeric.Natural (Natural)+import Prelude hiding (succ)+import Unsafe.Coerce (unsafeCoerce)++import qualified Control.Monad+import qualified Data.HashSet+import qualified Data.List.NonEmpty+import qualified Data.Text+import qualified Data.Text.Prettyprint.Doc as Pretty+import qualified Dhall.Crypto+import qualified Network.URI as URI++{-| Constants for a pure type system++ The axioms are:++> ⊦ Type : Kind+> ⊦ Kind : Sort++ ... and the valid rule pairs are:++> ⊦ Type ↝ Type : Type -- Functions from terms to terms (ordinary functions)+> ⊦ Kind ↝ Type : Type -- Functions from types to terms (type-polymorphic functions)+> ⊦ Sort ↝ Type : Type -- Functions from kinds to terms+> ⊦ Kind ↝ Kind : Kind -- Functions from types to types (type-level functions)+> ⊦ Sort ↝ Kind : Sort -- Functions from kinds to types (kind-polymorphic functions)+> ⊦ Sort ↝ Sort : Sort -- Functions from kinds to kinds (kind-level functions)++ Note that Dhall does not support functions from terms to types and therefore+ Dhall is not a dependently typed language+-}+data Const = Type | Kind | Sort+ deriving (Show, Eq, Ord, Data, Bounded, Enum, Generic, NFData)++instance Lift Const++instance Pretty Const where+ pretty = Pretty.unAnnotate . prettyConst++{-| Label for a bound variable++ The `Text` field is the variable's name (i.e. \"@x@\").++ The `Int` field disambiguates variables with the same name if there are+ multiple bound variables of the same name in scope. Zero refers to the+ nearest bound variable and the index increases by one for each bound+ variable of the same name going outward. The following diagram may help:++> ┌──refers to──┐+> │ │+> v │+> λ(x : Type) → λ(y : Type) → λ(x : Type) → x@0+>+> ┌─────────────────refers to─────────────────┐+> │ │+> v │+> λ(x : Type) → λ(y : Type) → λ(x : Type) → x@1++ This `Int` behaves like a De Bruijn index in the special case where all+ variables have the same name.++ You can optionally omit the index if it is @0@:++> ┌─refers to─┐+> │ │+> v │+> λ(x : Type) → λ(y : Type) → λ(x : Type) → x++ Zero indices are omitted when pretty-printing `Var`s and non-zero indices+ appear as a numeric suffix.+-}+data Var = V Text !Int+ deriving (Data, Generic, Eq, Ord, Show, NFData)++instance Lift Var++instance IsString Var where+ fromString str = V (fromString str) 0++instance Pretty Var where+ pretty = Pretty.unAnnotate . prettyVar++{- | Record the binding part of a @let@ expression.++For example,+> let {- A -} x {- B -} : {- C -} Bool = {- D -} True in x+will be instantiated as follows:++* @bindingSrc0@ corresponds to the @A@ comment.+* @variable@ is @"x"@+* @bindingSrc1@ corresponds to the @B@ comment.+* @annotation@ is 'Just' a pair, corresponding to the @C@ comment and @Bool@.+* @bindingSrc2@ corresponds to the @D@ comment.+* @value@ corresponds to @True@.+-}+data Binding s a = Binding+ { bindingSrc0 :: Maybe s+ , variable :: Text+ , bindingSrc1 :: Maybe s+ , annotation :: Maybe (Maybe s, Expr s a)+ , bindingSrc2 :: Maybe s+ , value :: Expr s a+ } deriving (Data, Eq, Foldable, Functor, Generic, NFData, Ord, Show, Traversable)++instance Bifunctor Binding where+ first k (Binding src0 a src1 b src2 c) =+ Binding (fmap k src0) a (fmap k src1) (fmap adapt0 b) (fmap k src2) (first k c)+ where+ adapt0 (src3, d) = (fmap k src3, first k d)++ second = fmap++{-| Construct a 'Binding' with no source information and no type annotation.+-}+makeBinding :: Text -> Expr s a -> Binding s a+makeBinding name = Binding Nothing name Nothing Nothing Nothing++-- | This wrapper around 'Prelude.Double' exists for its 'Eq' instance which is+-- defined via the binary encoding of Dhall @Double@s.+newtype DhallDouble = DhallDouble { getDhallDouble :: Double }+ deriving (Show, Data, NFData, Generic)++-- | This instance satisfies all the customary 'Eq' laws except substitutivity.+--+-- In particular:+--+-- >>> nan = DhallDouble (0/0)+-- >>> nan == nan+-- True+--+-- This instance is also consistent with with the binary encoding of Dhall @Double@s:+--+-- >>> toBytes n = Codec.Serialise.serialise (Dhall.Binary.encode (n :: DhallDouble))+--+-- prop> \a b -> (a == b) == (toBytes a == toBytes b)+instance Eq DhallDouble where+ DhallDouble a == DhallDouble b+ | isNaN a && isNaN b = True+ | isNegativeZero a `xor` isNegativeZero b = False+ | otherwise = a == b++-- | This instance relies on the 'Eq' instance for 'DhallDouble' but cannot+-- satisfy the customary 'Ord' laws when @NaN@ is involved.+instance Ord DhallDouble where+ compare a@(DhallDouble a') b@(DhallDouble b') =+ if a == b+ then EQ+ else compare a' b'++-- | The body of an interpolated @Text@ literal+data Chunks s a = Chunks [(Text, Expr s a)] Text+ deriving (Functor, Foldable, Generic, Traversable, Show, Eq, Ord, Data, NFData)++instance (Lift s, Lift a, Data s, Data a) => Lift (Chunks s a)++instance Data.Semigroup.Semigroup (Chunks s a) where+ Chunks xysL zL <> Chunks [] zR =+ Chunks xysL (zL <> zR)+ Chunks xysL zL <> Chunks ((x, y):xysR) zR =+ Chunks (xysL ++ (zL <> x, y):xysR) zR++instance Monoid (Chunks s a) where+ mempty = Chunks [] mempty++#if !(MIN_VERSION_base(4,11,0))+ mappend = (<>)+#endif++instance IsString (Chunks s a) where+ fromString str = Chunks [] (fromString str)++{-| Syntax tree for expressions++ The @s@ type parameter is used to track the presence or absence of `Src`+ spans:++ * If @s = `Src`@ then the code may contains `Src` spans (either in a `Noted`+ constructor or inline within another constructor, like `Let`)+ * If @s = `Void`@ then the code has no `Src` spans++ The @a@ type parameter is used to track the presence or absence of imports++ * If @a = `Import`@ then the code may contain unresolved `Import`s+ * If @a = `Void`@ then the code has no `Import`s+-}+data Expr s a+ -- | > Const c ~ c+ = Const Const+ -- | > Var (V x 0) ~ x+ -- > Var (V x n) ~ x@n+ | Var Var+ -- | > Lam x A b ~ λ(x : A) -> b+ | Lam Text (Expr s a) (Expr s a)+ -- | > Pi "_" A B ~ A -> B+ -- > Pi x A B ~ ∀(x : A) -> B+ | Pi Text (Expr s a) (Expr s a)+ -- | > App f a ~ f a+ | App (Expr s a) (Expr s a)+ -- | > Let (Binding _ x _ Nothing _ r) e ~ let x = r in e+ -- > Let (Binding _ x _ (Just t ) _ r) e ~ let x : t = r in e+ --+ -- The difference between+ --+ -- > let x = a let y = b in e+ --+ -- and+ --+ -- > let x = a in let y = b in e+ --+ -- is only an additional 'Note' around @'Let' "y" …@ in the second+ -- example.+ --+ -- See 'MultiLet' for a representation of let-blocks that mirrors the+ -- source code more closely.+ | Let (Binding s a) (Expr s a)+ -- | > Annot x t ~ x : t+ | Annot (Expr s a) (Expr s a)+ -- | > Bool ~ Bool+ | Bool+ -- | > BoolLit b ~ b+ | BoolLit Bool+ -- | > BoolAnd x y ~ x && y+ | BoolAnd (Expr s a) (Expr s a)+ -- | > BoolOr x y ~ x || y+ | BoolOr (Expr s a) (Expr s a)+ -- | > BoolEQ x y ~ x == y+ | BoolEQ (Expr s a) (Expr s a)+ -- | > BoolNE x y ~ x != y+ | BoolNE (Expr s a) (Expr s a)+ -- | > BoolIf x y z ~ if x then y else z+ | BoolIf (Expr s a) (Expr s a) (Expr s a)+ -- | > Natural ~ Natural+ | Natural+ -- | > NaturalLit n ~ n+ | NaturalLit Natural+ -- | > NaturalFold ~ Natural/fold+ | NaturalFold+ -- | > NaturalBuild ~ Natural/build+ | NaturalBuild+ -- | > NaturalIsZero ~ Natural/isZero+ | NaturalIsZero+ -- | > NaturalEven ~ Natural/even+ | NaturalEven+ -- | > NaturalOdd ~ Natural/odd+ | NaturalOdd+ -- | > NaturalToInteger ~ Natural/toInteger+ | NaturalToInteger+ -- | > NaturalShow ~ Natural/show+ | NaturalShow+ -- | > NaturalSubtract ~ Natural/subtract+ | NaturalSubtract+ -- | > NaturalPlus x y ~ x + y+ | NaturalPlus (Expr s a) (Expr s a)+ -- | > NaturalTimes x y ~ x * y+ | NaturalTimes (Expr s a) (Expr s a)+ -- | > Integer ~ Integer+ | Integer+ -- | > IntegerLit n ~ ±n+ | IntegerLit Integer+ -- | > IntegerShow ~ Integer/show+ | IntegerShow+ -- | > IntegerToDouble ~ Integer/toDouble+ | IntegerToDouble+ -- | > Double ~ Double+ | Double+ -- | > DoubleLit n ~ n+ | DoubleLit DhallDouble+ -- | > DoubleShow ~ Double/show+ | DoubleShow+ -- | > Text ~ Text+ | Text+ -- | > TextLit (Chunks [(t1, e1), (t2, e2)] t3) ~ "t1${e1}t2${e2}t3"+ | TextLit (Chunks s a)+ -- | > TextAppend x y ~ x ++ y+ | TextAppend (Expr s a) (Expr s a)+ -- | > TextShow ~ Text/show+ | TextShow+ -- | > List ~ List+ | List+ -- | > ListLit (Just t ) [] ~ [] : t+ -- > ListLit Nothing [x, y, z] ~ [x, y, z]+ --+ -- Invariant: A non-empty list literal is always represented as+ -- @ListLit Nothing xs@.+ --+ -- When an annotated, non-empty list literal is parsed, it is represented+ -- as+ --+ -- > Annot (ListLit Nothing [x, y, z]) t ~ [x, y, z] : t++ -- Eventually we should have separate constructors for empty and non-empty+ -- list literals. For now it's easier to check the invariant in @infer@.+ -- See https://github.com/dhall-lang/dhall-haskell/issues/1359#issuecomment-537087234.+ | ListLit (Maybe (Expr s a)) (Seq (Expr s a))+ -- | > ListAppend x y ~ x # y+ | ListAppend (Expr s a) (Expr s a)+ -- | > ListBuild ~ List/build+ | ListBuild+ -- | > ListFold ~ List/fold+ | ListFold+ -- | > ListLength ~ List/length+ | ListLength+ -- | > ListHead ~ List/head+ | ListHead+ -- | > ListLast ~ List/last+ | ListLast+ -- | > ListIndexed ~ List/indexed+ | ListIndexed+ -- | > ListReverse ~ List/reverse+ | ListReverse+ -- | > Optional ~ Optional+ | Optional+ -- | > Some e ~ Some e+ | Some (Expr s a)+ -- | > None ~ None+ | None+ -- | > OptionalFold ~ Optional/fold+ | OptionalFold+ -- | > OptionalBuild ~ Optional/build+ | OptionalBuild+ -- | > Record [(k1, t1), (k2, t2)] ~ { k1 : t1, k2 : t1 }+ | Record (Map Text (Expr s a))+ -- | > RecordLit [(k1, v1), (k2, v2)] ~ { k1 = v1, k2 = v2 }+ | RecordLit (Map Text (Expr s a))+ -- | > Union [(k1, Just t1), (k2, Nothing)] ~ < k1 : t1 | k2 >+ | Union (Map Text (Maybe (Expr s a)))+ -- | > Combine x y ~ x ∧ y+ | Combine (Expr s a) (Expr s a)+ -- | > CombineTypes x y ~ x ⩓ y+ | CombineTypes (Expr s a) (Expr s a)+ -- | > Prefer x y ~ x ⫽ y+ | Prefer (Expr s a) (Expr s a)+ -- | > RecordCompletion x y ~ x::y+ | RecordCompletion (Expr s a) (Expr s a)+ -- | > Merge x y (Just t ) ~ merge x y : t+ -- > Merge x y Nothing ~ merge x y+ | Merge (Expr s a) (Expr s a) (Maybe (Expr s a))+ -- | > ToMap x (Just t) ~ toMap x : t+ -- > ToMap x Nothing ~ toMap x+ | ToMap (Expr s a) (Maybe (Expr s a))+ -- | > Field e x ~ e.x+ | Field (Expr s a) Text+ -- | > Project e (Left xs) ~ e.{ xs }+ -- | > Project e (Right t) ~ e.(t)+ | Project (Expr s a) (Either (Set Text) (Expr s a))+ -- | > Assert e ~ assert : e+ | Assert (Expr s a)+ -- | > Equivalent x y ~ x ≡ y+ | Equivalent (Expr s a) (Expr s a)+ -- | > Note s x ~ e+ | Note s (Expr s a)+ -- | > ImportAlt ~ e1 ? e2+ | ImportAlt (Expr s a) (Expr s a)+ -- | > Embed import ~ import+ | Embed a+ deriving (Foldable, Generic, Traversable, Show, Data, NFData)+-- NB: If you add a constructor to Expr, please also update the Arbitrary+-- instance in Dhall.Test.QuickCheck.++-- | This instance encodes what the Dhall standard calls an \"exact match\"+-- between two expressions.+--+-- Note that+--+-- >>> nan = DhallDouble (0/0)+-- >>> DoubleLit nan == DoubleLit nan+-- True+deriving instance (Eq s, Eq a) => Eq (Expr s a)++-- | Note that this 'Ord' instance inherits `DhallDouble`'s defects.+deriving instance (Ord s, Ord a) => Ord (Expr s a)++instance (Lift s, Lift a, Data s, Data a) => Lift (Expr s a)++-- This instance is hand-written due to the fact that deriving+-- it does not give us an INLINABLE pragma. We annotate this fmap+-- implementation with this pragma below to allow GHC to, possibly,+-- inline the implementation for performance improvements.+instance Functor (Expr s) where+ fmap _ (Const c) = Const c+ fmap _ (Var v) = Var v+ fmap f (Lam v e1 e2) = Lam v (fmap f e1) (fmap f e2)+ fmap f (Pi v e1 e2) = Pi v (fmap f e1) (fmap f e2)+ fmap f (App e1 e2) = App (fmap f e1) (fmap f e2)+ fmap f (Let b e2) = Let (fmap f b) (fmap f e2)+ fmap f (Annot e1 e2) = Annot (fmap f e1) (fmap f e2)+ fmap _ Bool = Bool+ fmap _ (BoolLit b) = BoolLit b+ fmap f (BoolAnd e1 e2) = BoolAnd (fmap f e1) (fmap f e2)+ fmap f (BoolOr e1 e2) = BoolOr (fmap f e1) (fmap f e2)+ fmap f (BoolEQ e1 e2) = BoolEQ (fmap f e1) (fmap f e2)+ fmap f (BoolNE e1 e2) = BoolNE (fmap f e1) (fmap f e2)+ fmap f (BoolIf e1 e2 e3) = BoolIf (fmap f e1) (fmap f e2) (fmap f e3)+ fmap _ Natural = Natural+ fmap _ (NaturalLit n) = NaturalLit n+ fmap _ NaturalFold = NaturalFold+ fmap _ NaturalBuild = NaturalBuild+ fmap _ NaturalIsZero = NaturalIsZero+ fmap _ NaturalEven = NaturalEven+ fmap _ NaturalOdd = NaturalOdd+ fmap _ NaturalToInteger = NaturalToInteger+ fmap _ NaturalShow = NaturalShow+ fmap _ NaturalSubtract = NaturalSubtract+ fmap f (NaturalPlus e1 e2) = NaturalPlus (fmap f e1) (fmap f e2)+ fmap f (NaturalTimes e1 e2) = NaturalTimes (fmap f e1) (fmap f e2)+ fmap _ Integer = Integer+ fmap _ (IntegerLit i) = IntegerLit i+ fmap _ IntegerShow = IntegerShow+ fmap _ IntegerToDouble = IntegerToDouble+ fmap _ Double = Double+ fmap _ (DoubleLit d) = DoubleLit d+ fmap _ DoubleShow = DoubleShow+ fmap _ Text = Text+ fmap f (TextLit cs) = TextLit (fmap f cs)+ fmap f (TextAppend e1 e2) = TextAppend (fmap f e1) (fmap f e2)+ fmap _ TextShow = TextShow+ fmap _ List = List+ fmap f (ListLit maybeE seqE) = ListLit (fmap (fmap f) maybeE) (fmap (fmap f) seqE)+ fmap f (ListAppend e1 e2) = ListAppend (fmap f e1) (fmap f e2)+ fmap _ ListBuild = ListBuild+ fmap _ ListFold = ListFold+ fmap _ ListLength = ListLength+ fmap _ ListHead = ListHead+ fmap _ ListLast = ListLast+ fmap _ ListIndexed = ListIndexed+ fmap _ ListReverse = ListReverse+ fmap _ Optional = Optional+ fmap f (Some e) = Some (fmap f e)+ fmap _ None = None+ fmap _ OptionalFold = OptionalFold+ fmap _ OptionalBuild = OptionalBuild+ fmap f (Record r) = Record (fmap (fmap f) r)+ fmap f (RecordLit r) = RecordLit (fmap (fmap f) r)+ fmap f (Union u) = Union (fmap (fmap (fmap f)) u)+ fmap f (Combine e1 e2) = Combine (fmap f e1) (fmap f e2)+ fmap f (CombineTypes e1 e2) = CombineTypes (fmap f e1) (fmap f e2)+ fmap f (Prefer e1 e2) = Prefer (fmap f e1) (fmap f e2)+ fmap f (RecordCompletion e1 e2) = RecordCompletion (fmap f e1) (fmap f e2)+ fmap f (Merge e1 e2 maybeE) = Merge (fmap f e1) (fmap f e2) (fmap (fmap f) maybeE)+ fmap f (ToMap e maybeE) = ToMap (fmap f e) (fmap (fmap f) maybeE)+ fmap f (Field e1 v) = Field (fmap f e1) v+ fmap f (Project e1 vs) = Project (fmap f e1) (fmap (fmap f) vs)+ fmap f (Assert t) = Assert (fmap f t)+ fmap f (Equivalent e1 e2) = Equivalent (fmap f e1) (fmap f e2)+ fmap f (Note s e1) = Note s (fmap f e1)+ fmap f (ImportAlt e1 e2) = ImportAlt (fmap f e1) (fmap f e2)+ fmap f (Embed a) = Embed (f a)+ {-# INLINABLE fmap #-}++instance Applicative (Expr s) where+ pure = Embed++ (<*>) = Control.Monad.ap++instance Monad (Expr s) where+ return = pure++ Const a >>= _ = Const a+ Var a >>= _ = Var a+ Lam a b c >>= k = Lam a (b >>= k) (c >>= k)+ Pi a b c >>= k = Pi a (b >>= k) (c >>= k)+ App a b >>= k = App (a >>= k) (b >>= k)+ Let a b >>= k = Let (adapt0 a) (b >>= k)+ where+ adapt0 (Binding src0 c src1 d src2 e) =+ Binding src0 c src1 (fmap adapt1 d) src2 (e >>= k)++ adapt1 (src3, f) = (src3, f >>= k)+ Annot a b >>= k = Annot (a >>= k) (b >>= k)+ Bool >>= _ = Bool+ BoolLit a >>= _ = BoolLit a+ BoolAnd a b >>= k = BoolAnd (a >>= k) (b >>= k)+ BoolOr a b >>= k = BoolOr (a >>= k) (b >>= k)+ BoolEQ a b >>= k = BoolEQ (a >>= k) (b >>= k)+ BoolNE a b >>= k = BoolNE (a >>= k) (b >>= k)+ BoolIf a b c >>= k = BoolIf (a >>= k) (b >>= k) (c >>= k)+ Natural >>= _ = Natural+ NaturalLit a >>= _ = NaturalLit a+ NaturalFold >>= _ = NaturalFold+ NaturalBuild >>= _ = NaturalBuild+ NaturalIsZero >>= _ = NaturalIsZero+ NaturalEven >>= _ = NaturalEven+ NaturalOdd >>= _ = NaturalOdd+ NaturalToInteger >>= _ = NaturalToInteger+ NaturalShow >>= _ = NaturalShow+ NaturalSubtract >>= _ = NaturalSubtract+ NaturalPlus a b >>= k = NaturalPlus (a >>= k) (b >>= k)+ NaturalTimes a b >>= k = NaturalTimes (a >>= k) (b >>= k)+ Integer >>= _ = Integer+ IntegerLit a >>= _ = IntegerLit a+ IntegerShow >>= _ = IntegerShow+ IntegerToDouble >>= _ = IntegerToDouble+ Double >>= _ = Double+ DoubleLit a >>= _ = DoubleLit a+ DoubleShow >>= _ = DoubleShow+ Text >>= _ = Text+ TextLit (Chunks a b) >>= k = TextLit (Chunks (fmap (fmap (>>= k)) a) b)+ TextAppend a b >>= k = TextAppend (a >>= k) (b >>= k)+ TextShow >>= _ = TextShow+ List >>= _ = List+ ListLit a b >>= k = ListLit (fmap (>>= k) a) (fmap (>>= k) b)+ ListAppend a b >>= k = ListAppend (a >>= k) (b >>= k)+ ListBuild >>= _ = ListBuild+ ListFold >>= _ = ListFold+ ListLength >>= _ = ListLength+ ListHead >>= _ = ListHead+ ListLast >>= _ = ListLast+ ListIndexed >>= _ = ListIndexed+ ListReverse >>= _ = ListReverse+ Optional >>= _ = Optional+ Some a >>= k = Some (a >>= k)+ None >>= _ = None+ OptionalFold >>= _ = OptionalFold+ OptionalBuild >>= _ = OptionalBuild+ Record a >>= k = Record (fmap (>>= k) a)+ RecordLit a >>= k = RecordLit (fmap (>>= k) a)+ Union a >>= k = Union (fmap (fmap (>>= k)) a)+ Combine a b >>= k = Combine (a >>= k) (b >>= k)+ CombineTypes a b >>= k = CombineTypes (a >>= k) (b >>= k)+ Prefer a b >>= k = Prefer (a >>= k) (b >>= k)+ RecordCompletion a b >>= k = RecordCompletion (a >>= k) (b >>= k)+ Merge a b c >>= k = Merge (a >>= k) (b >>= k) (fmap (>>= k) c)+ ToMap a b >>= k = ToMap (a >>= k) (fmap (>>= k) b)+ Field a b >>= k = Field (a >>= k) b+ Project a b >>= k = Project (a >>= k) (fmap (>>= k) b)+ Assert a >>= k = Assert (a >>= k)+ Equivalent a b >>= k = Equivalent (a >>= k) (b >>= k)+ Note a b >>= k = Note a (b >>= k)+ ImportAlt a b >>= k = ImportAlt (a >>= k) (b >>= k)+ Embed a >>= k = k a++instance Bifunctor Expr where+ first _ (Const a ) = Const a+ first _ (Var a ) = Var a+ first k (Lam a b c ) = Lam a (first k b) (first k c)+ first k (Pi a b c ) = Pi a (first k b) (first k c)+ first k (App a b ) = App (first k a) (first k b)+ first k (Let a b ) = Let (first k a) (first k b)+ first k (Annot a b ) = Annot (first k a) (first k b)+ first _ Bool = Bool+ first _ (BoolLit a ) = BoolLit a+ first k (BoolAnd a b ) = BoolAnd (first k a) (first k b)+ first k (BoolOr a b ) = BoolOr (first k a) (first k b)+ first k (BoolEQ a b ) = BoolEQ (first k a) (first k b)+ first k (BoolNE a b ) = BoolNE (first k a) (first k b)+ first k (BoolIf a b c ) = BoolIf (first k a) (first k b) (first k c)+ first _ Natural = Natural+ first _ (NaturalLit a ) = NaturalLit a+ first _ NaturalFold = NaturalFold+ first _ NaturalBuild = NaturalBuild+ first _ NaturalIsZero = NaturalIsZero+ first _ NaturalEven = NaturalEven+ first _ NaturalOdd = NaturalOdd+ first _ NaturalToInteger = NaturalToInteger+ first _ NaturalShow = NaturalShow+ first _ NaturalSubtract = NaturalSubtract+ first k (NaturalPlus a b ) = NaturalPlus (first k a) (first k b)+ first k (NaturalTimes a b ) = NaturalTimes (first k a) (first k b)+ first _ Integer = Integer+ first _ (IntegerLit a ) = IntegerLit a+ first _ IntegerShow = IntegerShow+ first _ IntegerToDouble = IntegerToDouble+ first _ Double = Double+ first _ (DoubleLit a ) = DoubleLit a+ first _ DoubleShow = DoubleShow+ first _ Text = Text+ first k (TextLit (Chunks a b)) = TextLit (Chunks (fmap (fmap (first k)) a) b)+ first k (TextAppend a b ) = TextAppend (first k a) (first k b)+ first _ TextShow = TextShow+ first _ List = List+ first k (ListLit a b ) = ListLit (fmap (first k) a) (fmap (first k) b)+ first k (ListAppend a b ) = ListAppend (first k a) (first k b)+ first _ ListBuild = ListBuild+ first _ ListFold = ListFold+ first _ ListLength = ListLength+ first _ ListHead = ListHead+ first _ ListLast = ListLast+ first _ ListIndexed = ListIndexed+ first _ ListReverse = ListReverse+ first _ Optional = Optional+ first k (Some a ) = Some (first k a)+ first _ None = None+ first _ OptionalFold = OptionalFold+ first _ OptionalBuild = OptionalBuild+ first k (Record a ) = Record (fmap (first k) a)+ first k (RecordLit a ) = RecordLit (fmap (first k) a)+ first k (Union a ) = Union (fmap (fmap (first k)) a)+ first k (Combine a b ) = Combine (first k a) (first k b)+ first k (CombineTypes a b ) = CombineTypes (first k a) (first k b)+ first k (Prefer a b ) = Prefer (first k a) (first k b)+ first k (RecordCompletion a b) = RecordCompletion (first k a) (first k b)+ first k (Merge a b c ) = Merge (first k a) (first k b) (fmap (first k) c)+ first k (ToMap a b ) = ToMap (first k a) (fmap (first k) b)+ first k (Field a b ) = Field (first k a) b+ first k (Assert a ) = Assert (first k a)+ first k (Equivalent a b ) = Equivalent (first k a) (first k b)+ first k (Project a b ) = Project (first k a) (fmap (first k) b)+ first k (Note a b ) = Note (k a) (first k b)+ first k (ImportAlt a b ) = ImportAlt (first k a) (first k b)+ first _ (Embed a ) = Embed a++ second = fmap++instance IsString (Expr s a) where+ fromString str = Var (fromString str)++-- | Generates a syntactically valid Dhall program+instance Pretty a => Pretty (Expr s a) where+ pretty = Pretty.unAnnotate . prettyExpr++{-+Instead of converting explicitly between 'Expr's and 'MultiLet', it might+be nicer to use a pattern synonym:++> pattern MultiLet' :: NonEmpty (Binding s a) -> Expr s a -> Expr s a+> pattern MultiLet' as b <- (multiLetFromExpr -> Just (MultiLet as b)) where+> MultiLet' as b = wrapInLets as b+>+> multiLetFromExpr :: Expr s a -> Maybe (MultiLet s a)+> multiLetFromExpr = \case+> Let x mA a b -> Just (multiLet x mA a b)+> _ -> Nothing++This works in principle, but GHC as of v8.8.1 doesn't handle it well:+https://gitlab.haskell.org/ghc/ghc/issues/17096++This should be fixed by GHC-8.10, so it might be worth revisiting then.+-}++{-| Generate a 'MultiLet' from the contents of a 'Let'.++ In the resulting @'MultiLet' bs e@, @e@ is guaranteed not to be a 'Let',+ but it might be a @('Note' … ('Let' …))@.++ Given parser output, 'multiLet' consolidates @let@s that formed a+ let-block in the original source.+-}+multiLet :: Binding s a -> Expr s a -> MultiLet s a+multiLet b0 = \case+ Let b1 e1 ->+ let MultiLet bs e = multiLet b1 e1+ in MultiLet (Data.List.NonEmpty.cons b0 bs) e+ e -> MultiLet (b0 :| []) e++{-| Wrap let-'Binding's around an 'Expr'.++'wrapInLets' can be understood as an inverse for 'multiLet':++> let MultiLet bs e1 = multiLet b e0+>+> wrapInLets bs e1 == Let b e0+-}+wrapInLets :: Foldable f => f (Binding s a) -> Expr s a -> Expr s a+wrapInLets bs e = foldr Let e bs++{-| This type represents 1 or more nested `Let` bindings that have been+ coalesced together for ease of manipulation+-}+data MultiLet s a = MultiLet (NonEmpty (Binding s a)) (Expr s a)++-- | A traversal over the immediate sub-expressions of an expression.+subExpressions :: Applicative f => (Expr s a -> f (Expr s a)) -> Expr s a -> f (Expr s a)+subExpressions _ (Const c) = pure (Const c)+subExpressions _ (Var v) = pure (Var v)+subExpressions f (Lam a b c) = Lam a <$> f b <*> f c+subExpressions f (Pi a b c) = Pi a <$> f b <*> f c+subExpressions f (App a b) = App <$> f a <*> f b+subExpressions f (Let a b) = Let <$> bindingExprs f a <*> f b+subExpressions f (Annot a b) = Annot <$> f a <*> f b+subExpressions _ Bool = pure Bool+subExpressions _ (BoolLit b) = pure (BoolLit b)+subExpressions f (BoolAnd a b) = BoolAnd <$> f a <*> f b+subExpressions f (BoolOr a b) = BoolOr <$> f a <*> f b+subExpressions f (BoolEQ a b) = BoolEQ <$> f a <*> f b+subExpressions f (BoolNE a b) = BoolNE <$> f a <*> f b+subExpressions f (BoolIf a b c) = BoolIf <$> f a <*> f b <*> f c+subExpressions _ Natural = pure Natural+subExpressions _ (NaturalLit n) = pure (NaturalLit n)+subExpressions _ NaturalFold = pure NaturalFold+subExpressions _ NaturalBuild = pure NaturalBuild+subExpressions _ NaturalIsZero = pure NaturalIsZero+subExpressions _ NaturalEven = pure NaturalEven+subExpressions _ NaturalOdd = pure NaturalOdd+subExpressions _ NaturalToInteger = pure NaturalToInteger+subExpressions _ NaturalShow = pure NaturalShow+subExpressions _ NaturalSubtract = pure NaturalSubtract+subExpressions f (NaturalPlus a b) = NaturalPlus <$> f a <*> f b+subExpressions f (NaturalTimes a b) = NaturalTimes <$> f a <*> f b+subExpressions _ Integer = pure Integer+subExpressions _ (IntegerLit n) = pure (IntegerLit n)+subExpressions _ IntegerShow = pure IntegerShow+subExpressions _ IntegerToDouble = pure IntegerToDouble+subExpressions _ Double = pure Double+subExpressions _ (DoubleLit n) = pure (DoubleLit n)+subExpressions _ DoubleShow = pure DoubleShow+subExpressions _ Text = pure Text+subExpressions f (TextLit chunks) =+ TextLit <$> chunkExprs f chunks+subExpressions f (TextAppend a b) = TextAppend <$> f a <*> f b+subExpressions _ TextShow = pure TextShow+subExpressions _ List = pure List+subExpressions f (ListLit a b) = ListLit <$> traverse f a <*> traverse f b+subExpressions f (ListAppend a b) = ListAppend <$> f a <*> f b+subExpressions _ ListBuild = pure ListBuild+subExpressions _ ListFold = pure ListFold+subExpressions _ ListLength = pure ListLength+subExpressions _ ListHead = pure ListHead+subExpressions _ ListLast = pure ListLast+subExpressions _ ListIndexed = pure ListIndexed+subExpressions _ ListReverse = pure ListReverse+subExpressions _ Optional = pure Optional+subExpressions f (Some a) = Some <$> f a+subExpressions _ None = pure None+subExpressions _ OptionalFold = pure OptionalFold+subExpressions _ OptionalBuild = pure OptionalBuild+subExpressions f (Record a) = Record <$> traverse f a+subExpressions f ( RecordLit a ) = RecordLit <$> traverse f a+subExpressions f (Union a) = Union <$> traverse (traverse f) a+subExpressions f (Combine a b) = Combine <$> f a <*> f b+subExpressions f (CombineTypes a b) = CombineTypes <$> f a <*> f b+subExpressions f (Prefer a b) = Prefer <$> f a <*> f b+subExpressions f (RecordCompletion a b) = RecordCompletion <$> f a <*> f b+subExpressions f (Merge a b t) = Merge <$> f a <*> f b <*> traverse f t+subExpressions f (ToMap a t) = ToMap <$> f a <*> traverse f t+subExpressions f (Field a b) = Field <$> f a <*> pure b+subExpressions f (Project a b) = Project <$> f a <*> traverse f b+subExpressions f (Assert a) = Assert <$> f a+subExpressions f (Equivalent a b) = Equivalent <$> f a <*> f b+subExpressions f (Note a b) = Note a <$> f b+subExpressions f (ImportAlt l r) = ImportAlt <$> f l <*> f r+subExpressions _ (Embed a) = pure (Embed a)++{-| Traverse over the immediate 'Expr' children in a 'Binding'.+-}+bindingExprs+ :: (Applicative f)+ => (Expr s a -> f (Expr s b))+ -> Binding s a -> f (Binding s b)+bindingExprs f (Binding s0 n s1 t s2 v) =+ Binding+ <$> pure s0+ <*> pure n+ <*> pure s1+ <*> traverse (traverse f) t+ <*> pure s2+ <*> f v++-- | A traversal over the immediate sub-expressions in 'Chunks'.+chunkExprs+ :: Applicative f+ => (Expr s a -> f (Expr t b))+ -> Chunks s a -> f (Chunks t b)+chunkExprs f (Chunks chunks final) =+ flip Chunks final <$> traverse (traverse f) chunks++{-| Internal representation of a directory that stores the path components in+ reverse order++ In other words, the directory @\/foo\/bar\/baz@ is encoded as+ @Directory { components = [ "baz", "bar", "foo" ] }@+-}+newtype Directory = Directory { components :: [Text] }+ deriving (Eq, Generic, Ord, Show, NFData)++instance Semigroup Directory where+ Directory components₀ <> Directory components₁ =+ Directory (components₁ <> components₀)++instance Pretty Directory where+ pretty (Directory {..}) = foldMap prettyPathComponent (reverse components)++prettyPathComponent :: Text -> Doc ann+prettyPathComponent text+ | Data.Text.all pathCharacter text =+ "/" <> Pretty.pretty text+ | otherwise =+ "/\"" <> Pretty.pretty text <> "\""++{-| A `File` is a `directory` followed by one additional path component+ representing the `file` name+-}+data File = File+ { directory :: Directory+ , file :: Text+ } deriving (Eq, Generic, Ord, Show, NFData)++instance Pretty File where+ pretty (File {..}) =+ Pretty.pretty directory+ <> prettyPathComponent file++instance Semigroup File where+ 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+ | Here+ -- ^ Path relative to @.@+ | Parent+ -- ^ Path relative to @..@+ | Home+ -- ^ Path relative to @~@+ deriving (Eq, Generic, Ord, Show, NFData)++instance Pretty FilePrefix where+ pretty Absolute = ""+ pretty Here = "."+ pretty Parent = ".."+ pretty Home = "~"++-- | The URI scheme+data Scheme = HTTP | HTTPS deriving (Eq, Generic, Ord, Show, NFData)++-- | This type stores all of the components of a remote import+data URL = URL+ { scheme :: Scheme+ , authority :: Text+ , path :: File+ , query :: Maybe Text+ , headers :: Maybe (Expr Src Import)+ } deriving (Eq, Generic, Ord, Show, NFData)++instance Pretty URL where+ pretty (URL {..}) =+ schemeDoc+ <> "://"+ <> Pretty.pretty authority+ <> pathDoc+ <> queryDoc+ <> foldMap prettyHeaders headers+ where+ prettyHeaders h = " using " <> Pretty.pretty h++ File {..} = path++ Directory {..} = directory++ pathDoc =+ foldMap prettyURIComponent (reverse components)+ <> prettyURIComponent file++ schemeDoc = case scheme of+ HTTP -> "http"+ HTTPS -> "https"++ queryDoc = case query of+ Nothing -> ""+ Just q -> "?" <> Pretty.pretty q++prettyURIComponent :: Text -> Doc ann+prettyURIComponent text =+ Pretty.pretty $ URI.normalizeCase $ URI.normalizeEscape $ "/" <> Data.Text.unpack text++-- | The type of import (i.e. local vs. remote vs. environment)+data ImportType+ = Local FilePrefix File+ -- ^ Local path+ | Remote URL+ -- ^ URL of remote resource and optional headers stored in an import+ | Env Text+ -- ^ Environment variable+ | Missing+ deriving (Eq, Generic, Ord, Show, NFData)++parent :: File+parent = File { directory = Directory { components = [ ".." ] }, file = "" }++instance Semigroup ImportType where+ Local prefix file₀ <> Local Here file₁ = Local prefix (file₀ <> file₁)++ Remote (URL { path = path₀, ..}) <> Local Here path₁ =+ Remote (URL { path = path₀ <> path₁, ..})++ Local prefix file₀ <> Local Parent file₁ =+ Local prefix (file₀ <> parent <> file₁)++ Remote (URL { path = path₀, .. }) <> Local Parent path₁ =+ Remote (URL { path = path₀ <> parent <> path₁, .. })++ import₀ <> Remote (URL { headers = headers₀, .. }) =+ Remote (URL { headers = headers₁, .. })+ where+ importHashed₀ = Import (ImportHashed Nothing import₀) Code++ headers₁ = fmap (fmap (importHashed₀ <>)) headers₀++ _ <> import₁ =+ import₁++instance Pretty ImportType where+ pretty (Local prefix file) =+ Pretty.pretty prefix <> Pretty.pretty file++ pretty (Remote url) = Pretty.pretty url++ 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 | Location+ deriving (Eq, Generic, Ord, Show, NFData)++-- | A `ImportType` extended with an optional hash for semantic integrity checks+data ImportHashed = ImportHashed+ { hash :: Maybe Dhall.Crypto.SHA256Digest+ , importType :: ImportType+ } deriving (Eq, Generic, Ord, Show, NFData)++instance Semigroup ImportHashed where+ ImportHashed _ importType₀ <> ImportHashed hash importType₁ =+ ImportHashed hash (importType₀ <> importType₁)++instance Pretty ImportHashed where+ pretty (ImportHashed Nothing p) =+ Pretty.pretty p+ pretty (ImportHashed (Just h) p) =+ Pretty.pretty p <> " sha256:" <> Pretty.pretty (show h)++-- | Reference to an external resource+data Import = Import+ { importHashed :: ImportHashed+ , importMode :: ImportMode+ } deriving (Eq, Generic, Ord, Show, NFData)++instance Semigroup Import where+ Import importHashed₀ _ <> Import importHashed₁ code =+ Import (importHashed₀ <> importHashed₁) code++instance Pretty Import where+ pretty (Import {..}) = Pretty.pretty importHashed <> Pretty.pretty suffix+ where+ suffix :: Text+ suffix = case importMode of+ RawText -> " as Text"+ Location -> " as Location"+ Code -> ""++{-| Returns `True` if the given `Char` is valid within an unquoted path+ component++ This is exported for reuse within the @"Dhall.Parser.Token"@ module+-}+pathCharacter :: Char -> Bool+pathCharacter c =+ '\x21' == c+ || ('\x24' <= c && c <= '\x27')+ || ('\x2A' <= c && c <= '\x2B')+ || ('\x2D' <= c && c <= '\x2E')+ || ('\x30' <= c && c <= '\x3B')+ || c == '\x3D'+ || ('\x40' <= c && c <= '\x5A')+ || ('\x5E' <= c && c <= '\x7A')+ || c == '\x7C'+ || c == '\x7E'++-- | Remove all `Note` constructors from an `Expr` (i.e. de-`Note`)+denote :: Expr s a -> Expr t a+denote (Note _ b ) = denote b+denote (Const a ) = Const a+denote (Var a ) = Var a+denote (Lam a b c ) = Lam a (denote b) (denote c)+denote (Pi a b c ) = Pi a (denote b) (denote c)+denote (App a b ) = App (denote a) (denote b)+denote (Let a b ) = Let (adapt0 a) (denote b)+ where+ adapt0 (Binding _ c _ d _ e) =+ Binding Nothing c Nothing (fmap adapt1 d) Nothing (denote e)++ adapt1 (_, f) = (Nothing, denote f)+denote (Annot a b ) = Annot (denote a) (denote b)+denote Bool = Bool+denote (BoolLit a ) = BoolLit a+denote (BoolAnd a b ) = BoolAnd (denote a) (denote b)+denote (BoolOr a b ) = BoolOr (denote a) (denote b)+denote (BoolEQ a b ) = BoolEQ (denote a) (denote b)+denote (BoolNE a b ) = BoolNE (denote a) (denote b)+denote (BoolIf a b c ) = BoolIf (denote a) (denote b) (denote c)+denote Natural = Natural+denote (NaturalLit a ) = NaturalLit a+denote NaturalFold = NaturalFold+denote NaturalBuild = NaturalBuild+denote NaturalIsZero = NaturalIsZero+denote NaturalEven = NaturalEven+denote NaturalOdd = NaturalOdd+denote NaturalToInteger = NaturalToInteger+denote NaturalShow = NaturalShow+denote NaturalSubtract = NaturalSubtract+denote (NaturalPlus a b ) = NaturalPlus (denote a) (denote b)+denote (NaturalTimes a b ) = NaturalTimes (denote a) (denote b)+denote Integer = Integer+denote (IntegerLit a ) = IntegerLit a+denote IntegerShow = IntegerShow+denote IntegerToDouble = IntegerToDouble+denote Double = Double+denote (DoubleLit a ) = DoubleLit a+denote DoubleShow = DoubleShow+denote Text = Text+denote (TextLit (Chunks a b)) = TextLit (Chunks (fmap (fmap denote) a) b)+denote (TextAppend a b ) = TextAppend (denote a) (denote b)+denote TextShow = TextShow+denote List = List+denote (ListLit a b ) = ListLit (fmap denote a) (fmap denote b)+denote (ListAppend a b ) = ListAppend (denote a) (denote b)+denote ListBuild = ListBuild+denote ListFold = ListFold+denote ListLength = ListLength+denote ListHead = ListHead+denote ListLast = ListLast+denote ListIndexed = ListIndexed+denote ListReverse = ListReverse+denote Optional = Optional+denote (Some a ) = Some (denote a)+denote None = None+denote OptionalFold = OptionalFold+denote OptionalBuild = OptionalBuild+denote (Record a ) = Record (fmap denote a)+denote (RecordLit a ) = RecordLit (fmap denote a)+denote (Union a ) = Union (fmap (fmap denote) a)+denote (Combine a b ) = Combine (denote a) (denote b)+denote (CombineTypes a b ) = CombineTypes (denote a) (denote b)+denote (Prefer a b ) = Prefer (denote a) (denote b)+denote (RecordCompletion a b) = RecordCompletion (denote a) (denote b)+denote (Merge a b c ) = Merge (denote a) (denote b) (fmap denote c)+denote (ToMap a b ) = ToMap (denote a) (fmap denote b)+denote (Field a b ) = Field (denote a) b+denote (Project a b ) = Project (denote a) (fmap denote b)+denote (Assert a ) = Assert (denote a)+denote (Equivalent a b ) = Equivalent (denote a) (denote b)+denote (ImportAlt a b ) = ImportAlt (denote a) (denote b)+denote (Embed a ) = Embed a++-- | The \"opposite\" of `denote`, like @first absurd@ but faster+renote :: Expr Void a -> Expr s a+renote = unsafeCoerce+{-# INLINE renote #-}++{-| Remove any outermost `Note` constructors++ This is typically used when you want to get the outermost non-`Note`+ constructor without removing internal `Note` constructors+-}+shallowDenote :: Expr s a -> Expr s a+shallowDenote (Note _ e) = shallowDenote e+shallowDenote e = e++-- | The set of reserved identifiers for the Dhall language+reservedIdentifiers :: HashSet Text+reservedIdentifiers =+ Data.HashSet.fromList+ [ "let"+ , "in"+ , "Type"+ , "Kind"+ , "Sort"+ , "forall"+ , "Bool"+ , "True"+ , "False"+ , "merge"+ , "toMap"+ , "if"+ , "then"+ , "else"+ , "as"+ , "using"+ , "Natural"+ , "Natural/fold"+ , "Natural/build"+ , "Natural/isZero"+ , "Natural/even"+ , "Natural/odd"+ , "Natural/toInteger"+ , "Natural/show"+ , "Natural/subtract"+ , "Integer"+ , "Integer/show"+ , "Integer/toDouble"+ , "Double"+ , "Double/show"+ , "Text"+ , "Text/show"+ , "List"+ , "List/build"+ , "List/fold"+ , "List/length"+ , "List/head"+ , "List/last"+ , "List/indexed"+ , "List/reverse"+ , "Optional"+ , "Some"+ , "None"+ , "Optional/build"+ , "Optional/fold"+ , "NaN"+ , "Infinity"+ ]
+ src/Dhall/Syntax.hs-boot view
@@ -0,0 +1,7 @@+module Dhall.Syntax where++data Var++data Const++data Expr s a
+ src/Dhall/Tags.hs view
@@ -0,0 +1,279 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE LambdaCase #-}++-- | This module contains the implementation of the @dhall tags@ command++module Dhall.Tags+ ( generate+ ) where++import Control.Exception (handle, SomeException(..))+import Data.List (isSuffixOf)+import Data.Maybe (fromMaybe)+import Data.Semigroup (Semigroup(..))+import Dhall.Map (foldMapWithKey)+import Data.Text (Text)+import Data.Text.Encoding (encodeUtf8)+import Dhall.Util (Input(..))+import Dhall.Core (Expr(..), Binding(..))+import Dhall.Src (Src(srcStart))+import Dhall.Parser (exprFromText)+import System.FilePath ((</>), takeFileName)+import Text.Megaparsec (sourceLine, sourceColumn, unPos)++import qualified Data.ByteString as BS (length)+import qualified Data.Map as M+import qualified Data.Text as T+import qualified Data.Text.IO as TIO+import qualified System.Directory as SD++{- + Documentation for the etags format is not very informative and not very correct.+ You can find some documentation here:+ https://en.wikipedia.org/wiki/Ctags#Etags_2+ and you can also check the source code here:+ http://cvs.savannah.gnu.org/viewvc/vtags/vtags/vtags.el?view=markup+-}++data LineColumn = LC + { lcLine :: Int+ -- ^ line number, starting from 1, where to find the tag+ , lcColumn :: Int+ -- ^ column of line where tag is+ } deriving (Eq, Ord, Show)++data LineOffset = LO + { loLine :: Int+ -- ^ line number, starting from 1, where to find the tag+ , loOffset :: Int+ -- ^ byte offset from start of file. Not sure if any editor uses it+ } deriving (Eq, Ord, Show)++newtype Tags = Tags (M.Map FilePath [(LineOffset, Tag)])++instance Semigroup Tags where+ (Tags ts1) <> (Tags ts2) = Tags (M.unionWith (<>) ts1 ts2)++instance Monoid Tags where+ mempty = Tags M.empty+ mappend = (<>)++{-| For example, for the line: @let foo = \"foo\"@ the tag is:+ > Tag "let " "foo"+-}+data Tag = Tag+ { tagPattern :: Text+ -- ^ In vtags source code this field is named \"pattern\" and EMacs used it as+ -- a regex pattern to locate line with tag. It's looking for ^<tag pattern>.+ -- Looks like vi is not using it.+ , tagName :: Text+ -- ^ text, that editor compare with selected text. So it's really name of entity+ } deriving (Show)++type LineNumber = Int++type ByteOffset = Int++{-| Generate etags for Dhall expressions+-}+generate+ :: Input+ -- ^ Where to look for files. This can be a directory name (@.@ for example),+ -- a file name or `StandardInput`. If `StandardInput`, then this will wait for+ -- file names from @STDIN@.+ -- This way someone can combine tools in @bash@ to send, for example, output from+ -- @find@ to the input of @dhall tags@.+ -> Maybe [Text]+ -- ^ List of suffixes for dhall files or Nothing to check all files+ -> Bool+ -- ^ Flag if `generate` should follow symlinks+ -> IO Text+ -- ^ Content for tags file+generate inp sxs followSyms = do+ files <- inputToFiles followSyms (map T.unpack <$> sxs) inp+ tags <- traverse (\f -> handle (\(SomeException _) -> return mempty)+ (fileTags f <$> TIO.readFile f)) files+ return (showTags . mconcat $ tags)++{-| Find tags in Text (second argument) and generates a list of them+ To make tags for filenames that works in both emacs and vi, add two initial tags.+ First for @filename@ for vi and second with @/filename@ for emacs.+ Other tags are working for both.+-}+fileTags :: FilePath -> Text -> Tags+fileTags f t = Tags (M.singleton f+ (initialMap <> getTagsFromText t))+ where initialViTag = (LO 1 1, Tag "" (T.pack . takeFileName $ f))+ initialEmacsTag = (LO 1 1, Tag "" ("/" <> (T.pack . takeFileName) f))+ initialMap = [initialViTag, initialEmacsTag]++getTagsFromText :: Text -> [(LineOffset, Tag)]+getTagsFromText t = case exprFromText "" t of+ Right expr -> fixPosAndDefinition t (getTagsFromExpr expr)+ _ -> mempty++{-| Used to update tag position and to build tag from term.+ After getTagsFromExpr line and column in line are in `LineColumn` for each tag.+ And tagPattern is not added.+ Emacs use tag pattern to check if tag is on line. It compares line from start+ with tag pattern and in case they are the same, relocate user.+ fixPosAndDefinition change position to line and byte offset (`LineOffset`) and+ add tag pattern. For example, for Dhall string:++ >>> let dhallSource = "let foo = \"bar\"\nlet baz = \"qux\""++ Input for this function is:++ >>> foundTerms = [(LC 1 4, "foo"), (LC 2 4, "baz")]++ And:++ >>> fixPosAndDefinition dhallSource foundTerms+ [(LO {loLine = 1, loOffset = 5},Tag {tagPattern = "let foo ", tagName = "foo"}),(LO {loLine = 2, loOffset = 21},Tag {tagPattern = "let baz ", tagName = "baz"})]++ where 21 is byte offset from file start.+-}+fixPosAndDefinition :: Text -> [(LineColumn, Text)] -> [(LineOffset, Tag)]+fixPosAndDefinition t = foldMap (\(LC ln c, term) ->+ let (ln', offset, tPattern) = fromMaybe (fallbackInfoForText ln c)+ (infoForText term ln)+ in [(LO ln' offset, Tag tPattern term)])+ where mls :: M.Map Int (Text, Int) + -- ^ mls is map that for each line has length of file before this map and line content.+ -- In example above, first line is 15 bytes long and '\n', mls contain:+ -- (1, (16, "let foo = "bar"")+ -- That allow us to get byte offset easier.+ mls = M.fromList . fst . foldl processLine ([], 0) . zip [1..] $ T.lines t++ {-| processLine is a worker for `foldl` that generates the list of lines with+ byte offsets from the start of the first line from a list of lines+ -}+ processLine+ :: ([(LineNumber, (Text, ByteOffset))], ByteOffset)+ -- ^ previous result and byte offset for the start of current line+ -> (LineNumber, Text)+ -> ([(LineNumber, (Text, ByteOffset))], ByteOffset)+ -- ^ next result, where new line was added and byte offset for next line+ processLine (numberedLinesWithSizes, bytesBeforeLine) (n, line) =+ ((n, (line, bytesBeforeLine)): numberedLinesWithSizes, bytesBeforeNextLine)+ where bytesBeforeNextLine = bytesBeforeLine + lengthInBytes line + 1++ lineFromMap ln = fromMaybe ("", 0) (ln `M.lookup` mls)++ lengthInBytes = BS.length . encodeUtf8++ {-| get information about term from map of lines+ In most cases, `LineColumn` after `getTagsFromExpr` points to byte before term.+ It's better to have term in term pattern, so this function finds and updates+ line number and byte offset and generate pattern.+ -} + infoForText+ :: Text+ -- ^ term to find+ -> Int+ -- ^ line where to start+ -> Maybe (Int, Int, Text)+ -- ^ (Line number, byte offset, pattern to find term in file)+ infoForText term ln+ | ln <= 0 = Nothing+ | T.null part2 = infoForText term (ln - 1)+ | otherwise = Just (ln, lsl + 1 + lengthInBytes part1, part1 <> termAndNext)+ where (l, lsl) = lineFromMap ln+ (part1, part2) = T.breakOn term l+ termAndNext = T.take (T.length term + 1) part2++ fallbackInfoForText ln c = (ln, lsl + 1 + lengthInBytes pat, pat)+ where (l, lsl) = lineFromMap ln+ pat = T.take c l++getTagsFromExpr :: Expr Src a -> [(LineColumn, Text)]+getTagsFromExpr = go (LC 0 0) []+ where go lpos mts = \case+ (Let b e) -> go lpos (mts <> parseBinding lpos b) e+ (Annot e1 e2) -> go lpos (go lpos mts e1) e2+ (Record mr) -> mts <> tagsFromDhallMap lpos mr+ (RecordLit mr) -> mts <> tagsFromDhallMap lpos mr+ (Union mmr) -> mts <> tagsFromDhallMapMaybe lpos mmr+ (Note s e) -> go (srcToLineColumn s) mts e+ _ -> mts++ tagsFromDhallMap lpos = foldMapWithKey (tagsFromDhallMapElement lpos)++ tagsFromDhallMapMaybe lpos = foldMapWithKey (\k -> \case+ Just e -> tagsFromDhallMapElement lpos k e+ _ -> [(lpos, k)])++ tagsFromDhallMapElement lpos k e = go pos [(pos, k)] e+ where pos = firstPosFromExpr lpos e++ parseBinding :: LineColumn -> Binding Src a -> [(LineColumn, Text)]+ parseBinding lpos b = go p2 [(p0, variable b)] (value b)+ where p0 = posFromBinding (bindingSrc0 b) lpos+ p1 = posFromBinding (bindingSrc1 b) p0+ p2 = posFromBinding (bindingSrc2 b) p1+ posFromBinding src startPos = maybe startPos srcToLineColumn src++srcToLineColumn :: Src -> LineColumn+srcToLineColumn s = LC line column+ where ssp = srcStart s+ line = unPos . sourceLine $ ssp+ column = unPos . sourceColumn $ ssp++firstPosFromExpr :: LineColumn -> Expr Src a -> LineColumn+firstPosFromExpr lpos = \case+ (Note s _) -> srcToLineColumn s+ _ -> lpos++showTags :: Tags -> Text+showTags (Tags ts) = T.concat . map (uncurry showFileTags) . M.toList $ ts++showFileTags :: FilePath -> [(LineOffset, Tag)] -> T.Text+showFileTags f ts = "\x0c\n" <> T.pack f <> "," <> (showInt . T.length) cs <> "\n" <> cs+ where cs = T.concat . map (uncurry showPosTag) $ ts++showPosTag :: LineOffset -> Tag -> Text+showPosTag lo tag = def <>"\x7f" <> name <> "\x01" <> showInt line <>+ "," <> showInt offset <> "\n"+ where line = loLine lo+ offset = loOffset lo+ def = tagPattern tag+ name = tagName tag++showInt :: Int -> Text+showInt = T.pack . show++{-| Generate list of files for a given `Input`+-}+inputToFiles+ :: Bool+ -- ^ If `True`, this function will follow symbolic links+ -> Maybe [String]+ -- ^ List of suffixes. If `Nothing`, all files will be returned.+ -- This parameter only works when the `Input` is an `InputFile` and point to a directory.+ -> Input+ -> IO [ FilePath ]+ -- List of files.+inputToFiles _ _ StandardInput = lines <$> getContents+inputToFiles followSyms suffixes (InputFile path) = go path+ where go p = do+ isD <- SD.doesDirectoryExist p+ isSL <- isSymLink+ if isD+ then if isSL && not followSyms+ then return []+ else do+ -- filter . .. and hidden files .*+ contents <- fmap (filter ((/=) '.' . head))+ (SD.getDirectoryContents p)+ concat <$> mapM (go . (</>) p) contents+ else return [p | matchingSuffix || p == path]+ where matchingSuffix = maybe True (any (`isSuffixOf` p)) suffixes+ isSymLink = +#if MIN_VERSION_directory(1,3,0)+ SD.pathIsSymbolicLink p+#elif MIN_VERSION_directory(1,2,6)+ SD.isSymbolicLink pa+#else+ return False+#endif
src/Dhall/Tutorial.hs view
@@ -1,2986 +1,2175 @@ {-# OPTIONS_GHC -fno-warn-unused-imports #-} {-| Dhall is a programming language specialized for configuration files. This- module contains a tutorial explaining how to author configuration files using- this language--}-module Dhall.Tutorial (- -- * Introduction- -- $introduction-- -- * Types- -- $types-- -- * Imports- -- $imports-- -- * Lists- -- $lists-- -- * Optional values- -- $optional0-- -- * Records- -- $records-- -- * Functions- -- $functions-- -- * Compiler- -- $compiler-- -- * Strings- -- $strings-- -- * Combine- -- $combine-- -- * Let expressions- -- $let-- -- * Unions- -- $unions-- -- * Polymorphic functions- -- $polymorphic-- -- * Total- -- $total-- -- * Assertions- -- $assertions-- -- * Headers- -- $headers-- -- * Import integrity- -- $integrity-- -- * Raw text- -- $rawText-- -- * Formatting code- -- $format-- -- * Built-in functions- -- $builtins-- -- ** Caveats- -- $caveats-- -- ** Extending the language- -- $extending-- -- ** Overview- -- $builtinOverview-- -- ** @Bool@- -- $bool-- -- *** @(||)@- -- $or-- -- *** @(&&)@- -- $and-- -- *** @(==)@- -- $equal-- -- *** @(!=)@- -- $unequal-- -- *** @if@\/@then@\/@else@- -- $ifthenelse-- -- ** @Natural@- -- $natural-- -- *** @(+)@- -- $plus-- -- *** @(*)@- -- $times-- -- *** @Natural/even@- -- $even-- -- *** @Natural/odd@- -- $odd-- -- *** @Natural/isZero@- -- $isZero-- -- *** @Natural/fold@- -- $naturalFold-- -- *** @Natural/build@- -- $naturalBuild-- -- ** @Integer@- -- $integer-- -- ** @Double@- -- $double-- -- ** @Text@- -- $text-- -- *** @(++)@- -- $textAppend-- -- ** @List@- -- $list-- -- *** @(#)@- -- $listAppend-- -- *** @List/fold@- -- $listFold-- -- *** @List/build@- -- $listBuild-- -- *** @List/length@- -- $listLength-- -- *** @List/head@- -- $listHead-- -- *** @List/last@- -- $listLast-- -- *** @List/indexed@- -- $listIndexed-- -- *** @List/reverse@- -- $listReverse-- -- ** @Optional@- -- $optional1-- -- *** @Optional/fold@- -- $optionalFold-- -- * Prelude- -- $prelude-- -- * Conclusion- -- $conclusion-- -- * Frequently Asked Questions (FAQ)- -- $faq- ) where--import Data.Vector (Vector)-import Dhall---- $setup------ >>> :set -XOverloadedStrings---- $introduction------ The simplest way to use Dhall is to ignore the programming language features--- and use it as a strongly typed configuration format. For example, suppose--- that you create the following configuration file:--- --- > $ cat ./config--- > { foo = 1--- > , bar = [3.0, 4.0, 5.0]--- > }--- --- You can read the above configuration file into Haskell using the following--- code:--- --- > -- example.hs--- > --- > {-# LANGUAGE DeriveGeneric #-}--- > {-# LANGUAGE OverloadedStrings #-}--- > --- > import Dhall--- > --- > data Example = Example { foo :: Natural, bar :: Vector Double }--- > deriving (Generic, Show)--- > --- > instance Interpret Example--- > --- > main :: IO ()--- > main = do--- > x <- input auto "./config"--- > print (x :: Example)--- --- If you compile and run the above example, the program prints the corresponding--- Haskell record:--- --- > $ ./example--- > Example {foo = 1, bar = [3.0,4.0,5.0]}------ You can also load some types directly into Haskell without having to define a--- record, like this:------ >>> :set -XOverloadedStrings--- >>> input auto "True" :: IO Bool--- True------ The `input` function can decode any value if we specify the value's expected--- `Type`:------ > input--- > :: Type a -- Expected type--- > -> Text -- Dhall program--- > -> IO a -- Decoded expression------ ... and we can either specify an explicit type like `bool`:------ > bool :: Type Bool--- > --- > input bool :: Text -> IO Bool--- >--- > input bool "True" :: IO Bool------ >>> input bool "True"--- True------ ... or we can use `auto` to let the compiler infer what type to decode from--- the expected return type:------ > auto :: Interpret a => Type a--- >--- > input auto :: Interpret a => Text -> IO a------ >>> input auto "True" :: IO Bool--- True------ You can see what types `auto` supports \"out-of-the-box\" by browsing the--- instances for the `Interpret` class. For example, the following instance--- says that we can directly decode any Dhall expression that evaluates to a--- @Bool@ into a Haskell `Bool`:------ > instance Interpret Bool------ ... which is why we could directly decode the string @\"True\"@ into the--- value `True`.------ There is also another instance that says that if we can decode a value of--- type @a@, then we can also decode a @List@ of values as a `Vector` of @a@s:------ > instance Interpret a => Interpret (Vector a)------ Therefore, since we can decode a @Bool@, we must also be able to decode a--- @List@ of @Bool@s, like this:------ >>> input auto "[True, False]" :: IO (Vector Bool)--- [True,False]------ We could also specify what type to decode by providing an explicit `Type`--- instead of using `auto` with a type annotation:------ >>> input (vector bool) "[True, False]"--- [True,False]------ __Exercise:__ Create a @./config@ file that the following program can decode:------ > {-# LANGUAGE DeriveGeneric #-}--- > {-# LANGUAGE OverloadedStrings #-}--- > --- > import Dhall--- > --- > data Person = Person { age :: Natural, name :: Text }--- > deriving (Generic, Show)--- > --- > instance Interpret Person--- > --- > main :: IO ()--- > main = do--- > x <- input auto "./config"--- > print (x :: Person)------ __Exercise:__ Create a @./config@ file that the following program can decode:------ > {-# LANGUAGE OverloadedStrings #-}--- > --- > import Data.Functor.Identity--- > import Dhall--- > --- > instance Interpret a => Interpret (Identity a)--- > --- > main :: IO ()--- > main = do--- > x <- input auto "./config"--- > print (x :: Identity Double)---- $types------ Suppose that we try to decode a value of the wrong type, like this:------ >>> input auto "1" :: IO Bool--- *** Exception:--- ...Error...: Expression doesn't match annotation--- ...--- - Bool--- + Natural--- ...--- 1│ 1 : Bool--- ...--- (input):1:1--- ...------ The interpreter complains because the string @\"1\"@ cannot be decoded into a--- Haskell value of type `Bool`. This part of the type error:------ > - Bool--- > + Natural------ ... means that the expected type was @Bool@ but the inferred type of the--- expression @1@ was @Natural@.------ The code excerpt from the above error message has two components:------ * the expression being type checked (i.e. @1@)--- * the expression's expected type (i.e. @Bool@)------ > Expression--- > ⇩--- > 1 : Bool--- > ⇧--- > Expected type------ The @(:)@ symbol is how Dhall annotates values with their expected types.--- This notation is equivalent to type annotations in Haskell using the @(::)@--- symbol. Whenever you see:------ > x : t------ ... you should read that as \"we expect the expression @x@ to have type--- @t@\". However, we might be wrong and if our expected type does not match the--- expression's actual type then the type checker will complain.------ In this case, the expression @1@ does not have type @Bool@ so type checking--- fails with an exception.------ __Exercise:__ Load the Dhall library into @ghci@ and run these commands to get--- get a more detailed error message:------ > >>> import Dhall--- > >>> :set -XOverloadedStrings--- > >>> detailed (input auto "1") :: IO Bool--- > ...------ ... then read the entire error message------ __Exercise:__ Fix the type error, either by changing the value to decode or--- changing the expected type---- $imports------ You might wonder why in some cases we can decode a configuration file:------ >>> writeFile "bool" "True"--- >>> input auto "./bool" :: IO Bool--- True------ ... and in other cases we can decode a value directly:------ >>> input auto "True" :: IO Bool--- True------ This is because importing a configuration from a file is a special case of a--- more general language feature: Dhall expressions can reference other--- expressions by their file path.------ To illustrate this, let's create three files:--- --- > $ echo "True" > bool1--- > $ echo "False" > bool2--- > $ echo "./bool1 && ./bool2" > both------ ... and read in all three files in a single expression:--- --- >>> input auto "[ ./bool1 , ./bool2 , ./both ]" :: IO (Vector Bool)--- [True,False,False]------ Each file path is replaced with the Dhall expression contained within that--- file. If that file contains references to other files then those references--- are transitively resolved.------ In other words: configuration files can reference other configuration files,--- either by their relative or absolute paths. This means that we can split a--- configuration file into multiple files, like this:------ > $ cat > ./config <<EOF--- > { foo = 1--- > , bar = ./bar--- > }--- > EOF------ > $ echo "[3.0, 4.0, 5.0]" > ./bar------ > $ ./example--- > Example {foo = 1, bar = [3.0,4.0,5.0]}------ However, the Dhall language will forbid cycles in these file references. For--- example, if we create the following cycle:------ > $ echo './file1' > file2--- > $ echo './file2' > file1------ ... then the interpreter will reject the import:------ >>> input auto "./file1" :: IO Natural--- *** Exception: --- ↳ ./file1--- ↳ ./file2--- ...--- Cyclic import: ./file1--- ...------ You can also import expressions by URL. For example, you can find a Dhall--- expression hosted at this GitHub URL:------ <https://raw.githubusercontent.com/dhall-lang/dhall-haskell/18e4e9a18dc53271146df3ccf5b4177c3552236b/examples/True>------ > $ curl https://raw.githubusercontent.com/dhall-lang/dhall-haskell/18e4e9a18dc53271146df3ccf5b4177c3552236b/examples/True--- > True------ ... and you can reference that expression either directly:------ >>> input auto "https://raw.githubusercontent.com/dhall-lang/dhall-haskell/18e4e9a18dc53271146df3ccf5b4177c3552236b/examples/True" :: IO Bool--- True--- --- ... or inside of a larger expression:------ >>> input auto "False == https://raw.githubusercontent.com/dhall-lang/dhall-haskell/18e4e9a18dc53271146df3ccf5b4177c3552236b/examples/True" :: IO Bool--- False------ You're not limited to hosting Dhall expressions on GitHub. You can host a--- Dhall expression anywhere that you can host UTF8-encoded text on the web,--- as a pastebin, or your own web server.------ You can also import Dhall expressions from environment variables, too:------ >>> System.Environment.setEnv "FOO" "1"--- >>> input auto "env:FOO" :: IO Natural--- 1------ You can import types, too. For example, we can change our @./bar@ file to:------ > $ echo "[3.0, 4.0, 5.0] : List ./type" > ./bar------ ... then specify the @./type@ in a separate file:------ > $ echo "Double" > ./type------ ... and everything still type checks:------ > $ ./example--- > Example {foo = 1, bar = [3.0,4.0,5.0]}------ Note that all imports must be terminated by whitespace or you will get either--- an import error or a parse error, like this:------ >>> writeFile "baz" "2.0"--- >>> input auto "./baz: Double" :: IO Double--- *** Exception: --- ↳ ./baz: --- ...--- ...Error...: Missing file ...baz:--- ...------ This is because the parser thinks that @./baz:@ is a single token due to--- the missing whitespace before the colon and tries to import a file named--- @./baz:@, which does not exist. To fix the problem we have to add a space--- after @./baz@:------ >>> input auto "./baz : Double" :: IO Double--- 2.0------ __Exercise:__ There is a @not@ function hosted online here:------ <https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/Bool/not>------ Visit that link and read the documentation. Then try to guess what this--- code returns:------ > >>> input auto "https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/Bool/not https://raw.githubusercontent.com/dhall-lang/dhall-haskell/18e4e9a18dc53271146df3ccf5b4177c3552236b/examples/True" :: IO Bool--- > ???------ Run the code to test your guess---- $lists------ You can store 0 or more values of the same type in a list, like this:------ > [1, 2, 3]------ Every list can be followed by the type of the list. The type annotation is--- required for empty lists but optional for non-empty lists. You will get a--- type error if you provide an empty list without a type annotation:------ >>> input auto "[]" :: IO (Vector Natural)--- *** Exception:--- ...Error...: An empty list requires a type annotation--- ...--- 1│ []--- ...--- (input):1:1------ Also, list elements must all have the same type. You will get an error if--- you try to store elements of different types in a list:------ >>> input auto "[1, True, 3]" :: IO (Vector Natural)--- *** Exception:--- ...Error...: List elements should all have the same type--- ...--- - Natural--- + Bool--- ...--- 1│ True--- ...--- (input):1:5--- ...------ __Exercise:__ What is the shortest @./config@ file that you can decode using--- this command:------ > >>> input auto "./config" :: IO (Vector (Vector Natural))---- $optional0------ @Optional@ values are either of the form @Some aValue@ or @None aType@.------ For example, these are valid @Optional@ values:------ > Some 1--- >--- > None Natural------ ... which both have type @Optional Natural@------ An @Optional@ corresponds to Haskell's `Maybe` type for decoding purposes:------ >>> input auto "Some 1" :: IO (Maybe Natural)--- Just 1--- >>> input auto "None Natural" :: IO (Maybe Natural)--- Nothing------ __Exercise:__ Author a @./config@ file that you can decode like this:------ > >>> input auto "./config" :: IO (Maybe (Maybe (Maybe Natural)))--- > ???---- $records------ Record literals are delimited by curly braces and their fields are separated--- by commas. For example, this is a valid record literal:------ > { foo = True--- > , bar = 2--- > , baz = 4.2--- > }------ A record type is like a record literal except instead of specifying each--- field's value we specify each field's type. For example, the preceding--- record literal has the following record type:------ > { foo : Bool--- > , bar : Natural--- > , baz : Double--- > }------ If you want to specify an empty record literal, you must use @{=}@, which is--- special syntax reserved for empty records. If you want to specify the empty--- record type, then you use @{}@. If you forget which is which you can always--- ask the @dhall@ compiler to remind you of the type for each one:------ > $ dhall--- > {=}--- > <Ctrl-D>--- > {}--- > --- > {=}------ > $ dhall--- > {}--- > <Ctrl-D>--- > Type--- > --- > {}------ You can access a field of a record using the following syntax:------ > record.fieldName------ ... which means to access the value of the field named @fieldName@ from the--- @record@. For example:------ >>> input auto "{ foo = True, bar = 2, baz = 4.2 }.baz" :: IO Double--- 4.2------ ... and you can project out multiple fields into a new record using this--- syntax:------ > someRecord.{ field₀, field₁, … }------ For example:------ > $ dhall--- > { x = 1, y = True, z = "ABC" }.{ x, y }--- > <Ctrl-D>--- > { x : Natural, y : Bool }--- > --- > { x = 1, y = True }------ __Exercise__: What is the type of this record:------ > { foo = 1--- > , bar =--- > { baz = 2.0--- > , qux = True--- > }--- > }------ __Exercise__: Save the above code to a file named @./record@ and then try to--- access the value of the @baz@ field---- $functions------ The Dhall programming language also supports user-defined anonymous--- functions. For example, we can save the following anonymous function to a--- file:------ > $ cat > makeBools--- > \(n : Bool) ->--- > [ n && True, n && False, n || True, n || False ]--- > <Ctrl-D>------ ... or we can use Dhall's support for Unicode characters to use @λ@ (U+03BB)--- instead of @\\@ and @→@ (U+2192) instead of @->@ (for people who are into that--- sort of thing):------ > $ cat > makeBools--- > λ(n : Bool) →--- > [ n && True, n && False, n || True, n || False ]--- > <Ctrl-D>------ You can read this as a function of one argument named @n@ that has type--- @Bool@. This function returns a @List@ of @Bool@s. Each element of the--- @List@ depends on the input argument named @n@.------ The (ASCII) syntax for anonymous functions resembles the syntax for anonymous--- functions in Haskell. The only difference is that Dhall requires you to--- annotate the type of the function's input.------ You can import this function into Haskell, too:------ >>> makeBools <- input auto "./makeBools" :: IO (Bool -> Vector Bool)--- >>> makeBools True--- [True,False,True,True]------ The reason this works is that there is an `Interpret` instance for simple--- functions:------ > instance (Inject a, Interpret b) => Interpret (a -> b)------ Thanks to currying, this instance works for functions of multiple simple--- arguments:------ >>> dhallAnd <- input auto "λ(x : Bool) → λ(y : Bool) → x && y" :: IO (Bool -> Bool -> Bool)--- >>> dhallAnd True False--- False------ However, you can't convert anything more complex than that (like a polymorphic--- or higher-order function). You will need to apply those functions to their--- arguments within Dhall before converting their result to a Haskell value.------ Just like `Interpret`, you can derive `Inject` for user-defined data types:------ > {-# LANGUAGE DeriveAnyClass #-}--- > {-# LANGUAGE DeriveGeneric #-}--- > {-# LANGUAGE OverloadedStrings #-}--- > --- > module Main where--- > --- > import Dhall--- > --- > data Example0 = Example0 { foo :: Bool, bar :: Bool }--- > deriving (Generic, Inject)--- > --- > main = do--- > f <- input auto "λ(r : { foo : Bool, bar : Bool }) → r.foo && r.bar"--- > print (f (Example0 { foo = True, bar = False }) :: Bool)--- > print (f (Example0 { foo = True, bar = True }) :: Bool)------ The above program prints:------ > False--- > True---- $compiler------ We can also test our @makeBools@ function directly from the command line.--- This library comes with a command-line executable program named @dhall@ that--- you can use to both type-check files and convert them to a normal form. Our--- compiler takes a program on standard input and then prints the program's type--- to standard error followed by the program's normal form to standard output:------ > $ dhall--- > ./makeBools--- > <Ctrl-D>--- > ∀(n : Bool) → List Bool--- > --- > λ(n : Bool) → [ n, False, True, n ]------ The first line says that @makeBools@ is a function of one argument named @n@--- that has type @Bool@ and the function returns a @List@ of @Bool@s. The @∀@--- (U+2200) symbol is shorthand for the ASCII @forall@ keyword:------ > ∀(x : a) → b -- This type ...--- > --- > forall (x : a) → b -- ... is the same as this type------ ... and Dhall's @forall@ keyword behaves the same way as Haskell's @forall@--- keyword for input values that are @Type@s:------ > forall (x : Type) → b -- This Dhall type ...--- --- > forall x . b -- ... is the same as this Haskell type------ The part where Dhall differs from Haskell is that you can also use--- @∀@/@forall@ to give names to non-@Type@ arguments (such as the first--- argument to @makeBools@).------ The second line of Dhall's output is our program's normal form:------ > λ(n : Bool) → [ n, False, True, n ]------ ... and the interpreter was able to simplify our expression by noting that:------ * @n && True = n@--- * @n && False = False@--- * @n || True = True@--- * @n || False = n@------ To apply a function to an argument you separate the function and argument by--- whitespace (just like Haskell):------ @f x@------ You can read the above as \"apply the function @f@ to the argument @x@\". This--- means that we can \"apply\" our @./makeBools@ function to a @Bool@ argument--- like this:------ > $ dhall--- > ./makeBools True--- > <Ctrl-D>--- > List Bool--- > --- > [True, False, True, True]------ Remember that file paths are synonymous with their contents, so the above--- code is exactly equivalent to:--- --- > $ dhall--- > (λ(n : Bool) → [n && True, n && False, n || True, n || False]) True--- > <Ctrl-D>--- > List Bool--- > --- > [True, False, True, True]------ When you apply an anonymous function to an argument, you substitute the--- \"bound variable" with the function's argument:------ > Bound variable--- > ⇩--- > (λ(n : Bool) → ...) True--- > ⇧--- > Function argument------ So in our above example, we would replace all occurrences of @n@ with @True@,--- like this:------ > -- If we replace all of these `n`s with `True` ...--- > [n && True, n && False, n || True, n || False]--- >--- > -- ... then we get this:--- > [True && True, True && False, True || True, True || False]--- >--- > -- ... which reduces to the following normal form:--- > [True, False, True, True]------ Now that we've verified that our function type checks and works, we can use--- the same function within Haskell:------ >>> input auto "./makeBools True" :: IO (Vector Bool)--- [True,False,True,True]------ __Exercise__: Create a file named @getFoo@ that is a function of the following--- type:------ > ∀(r : { foo : Bool, bar : Text }) → Bool------ This function should take a single input argument named @r@ that is a record--- with two fields. The function should return the value of the @foo@ field.------ __Exercise__: Use the @dhall@ compiler to infer the type of the function you--- just created and verify that your function has the correct type------ __Exercise__: Use the @dhall@ compiler to apply your function to a sample--- record---- $strings--- Dhall supports ordinary string literals with Haskell-style escaping rules:------ > dhall--- > "Hello, \"world\"!"--- > <Ctrl-D>--- > Text--- >--- > "Hello, \"world\"!"------ ... and Dhall also supports Nix-style multi-line string literals:------ > dhall--- > ''--- > #!/bin/bash--- > --- > echo "Hi!"--- > ''--- > <Ctrl-D>--- > Text--- >--- > "\n#!/bin/bash\n\necho \"Hi!\"\n"------ These \"double single quote strings\" ignore all special characters, with one--- exception: if you want to include a @''@ in the string, you will need to--- escape it with a preceding @'@ (i.e. use @'''@ to insert @''@ into the final--- string).------ These strings also strip leading whitespace using the same rules as Nix.--- Specifically: \"it strips from each line a number of spaces equal to the--- minimal indentation of the string as a whole (disregarding the indentation--- of empty lines).\"------ You can also interpolate expressions into strings using @${...}@ syntax. For--- example:------ > $ dhall--- > let name = "John Doe"--- > in let age = 21--- > in "My name is ${name} and my age is ${Natural/show age}"--- > <Ctrl-D>--- > Text--- >--- > "My name is John Doe and my age is 21"------ Note that you can only interpolate expressions of type @Text@------ If you need to insert a @"${"@ into a string without interpolation then use--- @"''${"@ (same as Nix)------ > ''--- > for file in *; do--- > echo "Found ''${file}"--- > done--- > ''---- $combine------ You can combine two records, using either the @(//)@ operator or the--- @(/\\)@ operator.------ The @(//)@ operator (or @(⫽)@ U+2AFD) combines the fields of both records,--- preferring fields from the right record if they share fields in common:------ > $ dhall--- > { foo = 1, bar = "ABC" } // { baz = True }--- > <Ctrl-D>--- > { bar : Text, baz : Bool, foo : Natural }--- > --- > { bar = "ABC", baz = True, foo = 1 }--- > $ dhall--- > { foo = 1, bar = "ABC" } ⫽ { bar = True } -- Fancy unicode--- > <Ctrl-D>--- > { bar : Bool, foo : Natural }--- > --- > { bar = True, foo = 1 }------ Note that the order of record fields does not matter. The compiler--- automatically sorts the fields.------ The @(/\\)@ operator (or @(∧)@ U+2227) also lets you combine records, but--- behaves differently if the records share fields in common. The operator--- combines shared fields recursively if they are both records:------ > $ dhall--- > { foo = { bar = True }, baz = "ABC" } /\ { foo = { qux = 1.0 } }--- > <Ctrl-D>--- > { baz : Text, foo : { bar : Bool, qux : Double } }--- > --- > { baz = "ABC", foo = { bar = True, qux = 1.0 } }------ ... but fails with a type error if either shared field is not a record:------ > $ dhall--- > { foo = 1, bar = "ABC" } ∧ { foo = True }--- > <Ctrl-D>--- > Use "dhall --explain" for detailed errors--- > --- > Error: Field collision--- > --- > { foo = 1, bar = "ABC" } ∧ { foo = True }--- > --- > (stdin):1:1------ __Exercise__: Combine any record with the empty record. What do you expect--- to happen?------ You can analogously combine record types using the @//\\\\@ operator (or @(⩓)@ U+2A53):------ > $ dhall--- > { foo : Natural } ⩓ { bar : Text }--- > <Ctrl-D>--- > { foo : Natural, bar : Text }------ ... which behaves the exact same, except at the type level, meaning that the--- operator descends recursively into record types:------ > $ dhall--- > { foo : { bar : Text } } ⩓ { foo : { baz : Bool }, qux : Natural }--- > <Ctrl-D>--- > { foo : { bar : Text, baz : Bool }, qux : Natural }---- $let------ Dhall also supports @let@ expressions, which you can use to define--- intermediate values in the course of a computation, like this:------ > $ dhall--- > let x = "ha" in x ++ x--- > <Ctrl-D>--- > Text--- >--- > "haha"------ You can also annotate the types of values defined within a @let@ expression,--- like this:------ > $ dhall--- > let x : Text = "ha" in x ++ x--- > <Ctrl-D>--- > Text--- >--- > "haha"------ You need to nest @let@ expressions if you want to define more than one value--- in this way:------ > $ dhall--- > let x = "Hello, "--- > in let y = "world!"--- > in x ++ y--- > <Ctrl-D>--- > Text--- > --- > "Hello, world!"------ Dhall is whitespace-insensitive, so feel free to format things over multiple--- lines or indent in any way that you please.------ If you want to define a named function, just give a name to an anonymous--- function:------ > $ dhall--- > let twice = λ(x : Text) → x ++ x in twice "ha"--- > <Ctrl-D>--- > Text--- > --- > "haha"------ Unlike Haskell, Dhall does not support function arguments on the left-hand--- side of the equals sign, so this will not work:------ > $ dhall--- > let twice (x : Text) = x ++ x in twice "ha"--- > <Ctrl-D>--- > Error: Invalid input--- > --- > (stdin):1:11:--- > |--- > 1 | let twice (x : Text) = x ++ x in twice "ha"--- > | ^--- > unexpected '('--- > expecting ':', '=', or the rest of label------ The error message says that Dhall expected either a @(:)@ (i.e. the beginning--- of a type annotation) or a @(=)@ (the beginning of the assignment) and not a--- function argument.------ You can also use @let@ expressions to rename imports, like this:------ > $ dhall--- > let not = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/Bool/not--- > in not True--- > <Ctrl-D>--- > Bool--- > --- > False------ ... or to define synonyms for types:------ > $ dhall <<< 'let Name : Type = Text in [ "John", "Mary" ] : List Name'--- > List Text--- > --- > [ "John", "Mary" ]------ __Exercise:__ What do you think the following code will normalize to?------ > let x = 1--- > in let x = 2--- > in x------ Test your guess using the @dhall@ compiler------ __Exercise:__ Now try to guess what this code will normalize to:------ > let x = "ha"--- > in let x = x ++ "ha"--- > in x------ __Exercise:__ What about this code?------ > let x = x ++ "ha"--- > in x---- $unions------ A union is a value that can be one of many alternative types of values. For--- example, the following union type:------ > < Left : Natural | Right : Bool >------ ... represents a value that can be either a @Natural@ or a @Bool@ value. If--- you are familiar with Haskell these are exactly analogous to Haskell's--- \"sum types\".------ Each alternative is associated with a tag that distinguishes that alternative--- from other alternatives. In the above example, the @Left@ tag is used for--- the @Natural@ alternative and the @Right@ tag is used for the @Bool@--- alternative.------ You can specify the value of a union constructor like this:------ > let Union = < Left : Natural | Right : Bool>--- > --- > [ Union.Left 0, Union.Right True ] : List Union------ In other words, you can access a union constructor as a field of a union--- type and use that constructor to wrap a value of a type appropriate for--- that alternative. In the above example, the @Left@ constructor can wrap--- a @Natural@ value and the @Right@ constructor can wrap a @Bool@ value. We--- can also confirm that by inspecting their type:------ > $ echo '< Left : Natural | Right : Bool>' > ./Union------ > $ dhall --annotate <<< '(./Union).Left'--- > < Left : Natural | Right : Bool >.Left--- > : ∀(Left : Natural) → < Left : Natural | Right : Bool >------ > $ dhall --annotate <<< '(./Union).Right'--- > < Left : Natural | Right : Bool >.Right--- > : ∀(Right : Bool) → < Left : Natural | Right : Bool >------ In other words, the @Left@ constructor is a function from a @Natural@ to a--- value of our @Union@ type and the @Right@ constructor is a separate function--- from a @Bool@ to that same @Union@ type.------ You can consume a union using the built-in @merge@ function. For example,--- suppose we want to convert our union to a @Bool@ but we want to behave--- differently depending on whether or not the union is a @Natural@ wrapped in--- the @Left@ constructor or a @Bool@ wrapped in the @Right@ constructor . We--- would write:------ > $ cat > process <<EOF--- > λ(union : < Left : Natural | Right : Bool >)--- > → let handlers =--- > { Left = Natural/even -- Natural/even is a built-in function--- > , Right = λ(b : Bool) → b--- > }--- > in merge handlers union : Bool--- > EOF------ Now our @./process@ function can handle both alternatives:------ > $ dhall <<< './process ((./Union).Left 3)'--- > False------ > $ dhall <<< './process ((./Union).Right True)'--- > True------ Every @merge@ has the following form:------ > merge handlers union : type------ ... where: ------ * @union@ is the union you want to consume--- * @handlers@ is a record with one function per alternative of the union--- * @type@ is the declared result type of the @merge@------ The @merge@ function selects which function to apply from the record based on--- which alternative the union selects:------ > merge { Foo = f, ... } (< … >.Foo x) = f x------ So, for example:------ > merge { Left = Natural/even, Right = λ(b : Bool) → b } (< Left : Natural | Right : Bool >.Left 3)--- > = Natural/even 3--- > = False------ ... and similarly:------ > merge { Left = Natural/even, Right = λ(b : Bool) → b } (< Left : Natural | Right : Bool >.Right True)--- > = (λ(b : Bool) → b) True--- > = True------ Notice that each handler has to return the same type of result (@Bool@ in--- this case).------ You can also store more than one value within alternatives using Dhall's--- support for anonymous records. You can nest an anonymous record within a--- union such as in this type:------ > < Empty : {} | Person : { name : Text, age : Natural } >------ You can even go a step further and omit the type of an alternative if it--- stores no data, like this:------ > < Empty | Person : { name : Text, age : Natural } >--------- This union of records resembles following equivalent Haskell data type:------ > data Example = Empty | Person { name :: Text, age :: Text }------ Empty alternatives like @Empty@ require no argument:------ > let MyType = < Empty | Person : { name : Text, age : Natural } >--- > --- > in [ MyType.Empty -- Note the absence of any argument to `Empty`--- > , MyType.Person { name = "John", age = 23 }--- > , MyType.Person { name = "Amy" , age = 25 }--- > ]------ ... and when you @merge@ an empty alternative the correspond handler takes no--- argument:------ > λ(x : < Empty | Person : { name : Text, age : Natural } >)--- > → merge--- > { Empty = "Unknown" -- Note the absence of a `λ`--- >--- > , Person =--- > λ(person : { name : Text, age : Natural })--- > → "Name: ${person.name}, Age: ${Natural/show person.age}"--- > }--- > x------ __Exercise__: Create a list of the following type with at least one element--- per alternative:------ > List < Left : Natural | Right : Double >---- $polymorphic------ The Dhall language supports defining polymorphic functions (a.k.a.--- \"generic\" functions) that work on more than one type of value. However,--- Dhall differs from Haskell by not inferring the types of these polymorphic--- functions. Instead, you must be explicit about what type of value the--- function is specialized to.------ Take, for example, Haskell's identity function named @id@:------ > id :: a -> a--- > id = \x -> x------ The identity function is polymorphic, meaning that `id` works on values of--- different types:------ >>> id 4--- 4--- >>> id True--- True------ The equivalent function in Dhall is:------ > λ(a : Type) → λ(x : a) → x------ Notice how this function takes two arguments instead of one. The first--- argument is the type of the second argument.------ Let's illustrate how this works by actually using the above function:------ > $ echo "λ(a : Type) → λ(x : a) → x" > id------ If we supply the function alone to the compiler we get the inferred type as--- the first line:--- --- > $ dhall--- > ./id--- > <Ctrl-D>--- > ∀(a : Type) → ∀(x : a) → a--- > --- > λ(a : Type) → λ(x : a) → x------ You can read the type @(∀(a : Type) → ∀(x : a) → a)@ as saying: \"This is the--- type of a function whose first argument is named @a@ and is a @Type@. The--- second argument is named @x@ and has type @a@ (i.e. the value of the first--- argument). The result also has type @a@.\"------ This means that the type of the second argument changes depending on what--- type we provide for the first argument. When we apply @./id@ to @Natural@,--- we create a function that expects an @Natural@ argument:------ > $ dhall--- > ./id Natural--- > <Ctrl-D>--- > ∀(x : Natural) → Natural--- > --- > λ(x : Natural) → x------ Similarly, when we apply @./id@ to @Bool@, we create a function that expects a--- @Bool@ argument:------ > $ dhall--- > ./id Bool--- > <Ctrl-D>--- > ∀(x : Bool) → Bool--- > --- > λ(x : Bool) → x------ We can then supply the final argument to each of those functions to show--- that they both work on their respective types:------ > $ dhall--- > ./id Natural 4--- > <Ctrl-D>--- > Natural--- > --- > 4------ > $ dhall--- > ./id Bool True--- > <Ctrl-D>--- > Bool--- > --- > True------ Built-in functions can also be polymorphic, too. For example, we can ask--- the compiler for the type of @List/reverse@, the function that reverses a--- list:------ > $ dhall--- > List/reverse--- > <Ctrl-D>--- > ∀(a : Type) → List a → List a--- > --- > List/reverse------ The first argument to @List/reverse@ is the type of the list to reverse:------ > $ dhall--- > List/reverse Bool--- > <Ctrl-D>--- > List Bool → List Bool--- > --- > List/reverse Bool------ ... and the second argument is the list to reverse:------ > $ dhall--- > List/reverse Bool [True, False]--- > <Ctrl-D>--- > List Bool--- > --- > [False, True] : List Bool------ Note that the second argument has no name. This type:------ > ∀(a : Type) → List a → List a------ ... is equivalent to this type:------ > ∀(a : Type) → ∀(_ : List a) → List a------ In other words, if you don't see the @∀@ symbol surrounding a function--- argument type then that means that the name of the argument is @"_"@. This--- is true even for user-defined functions:------ > $ dhall--- > λ(_ : Text) → 1--- > <Ctrl-D>--- > Text → Natural--- > --- > λ(_ : Text) → 1------ The type @(Text → Natural)@ is the same as @(∀(_ : Text) → Natural)@------ __Exercise__ : Translate Haskell's `flip` function to Dhall---- $total------ Dhall is a total programming language, which means that Dhall is not--- Turing-complete and evaluation of every Dhall program is guaranteed to--- eventually halt. There is no upper bound on how long the program might take--- to evaluate, but the program is guaranteed to terminate in a finite amount of--- time and not hang forever.------ This guarantees that all Dhall programs can be safely reduced to a normal--- form where as many functions have been evaluated as possible. In fact, Dhall--- expressions can be evaluated even if all function arguments haven't been fully--- applied. For example, the following program is an anonymous function:------ > $ dhall--- > \(n : Bool) -> 10 * 10--- > <Ctrl-D>--- > ∀(n : Bool) → Natural--- > --- > λ(n : Bool) → 100------ ... and even though the function is still missing the first argument named--- @n@ the compiler is smart enough to evaluate the body of the anonymous--- function ahead of time before the function has even been invoked.------ We can use the @map@ function from the Prelude to illustrate an even more--- complex example:------ > $ dhall--- > let Prelude/List/map = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/map--- > in λ(f : Natural → Natural) → Prelude/List/map Natural Natural f [1, 2, 3]--- > <Ctrl-D>--- > ∀(f : Natural → Natural) → List Natural--- > --- > λ(f : Natural → Natural) → [f 1, f 2, f 3] : List Natural------ Dhall can apply our function to each element of the list even before we specify--- which function to apply.------ The language will also never crash or throw any exceptions. Every--- computation will succeed and produce something, even if the result might be--- an @Optional@ value:------ > $ dhall--- > List/head Natural ([] : List Natural)--- > <Ctrl-D>--- > Optional Natural--- > --- > None Natural------ __Exercise__: The Dhall Prelude provides a @replicate@ function which you can--- find here:------ <https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/replicate>------ Test what the following Dhall expression normalizes to:------ > let replicate = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/replicate--- > in replicate 10------ __Exercise__: If you have a lot of spare time, try to \"break the compiler\" by--- finding an input expression that crashes or loops forever (and file a bug--- report if you succeed)---- $assertions------ You can add compile-time assertions which you can use to add tests to your--- code. For example, we can add some tests to our @not@ function like this:------ > let not : Bool -> Bool = \(b : Bool) -> b == False--- >--- > let example0 = assert : not False === True--- >--- > let example1 = assert : not True === False--- >--- > in not------ The expression @assert : not False == True@ is a type-checking assertion--- that two expressions have the same normal form. If the two expressions differ--- then type-checking rejects the code.------ For example, suppose that we change the example to add an incorrect assertion:------ > -- ./test.dhall--- >--- > let not : Bool -> Bool = \(b : Bool) -> b == False--- >--- > let example0 = assert : not False === True--- >--- > let example1 = assert : not True === True -- Oops!--- >--- > in not------ The type-checker then rejects the assertion with the following error message:------ > $ dhall <<< './test.dhall' --- > --- > ↳ ./test.dhall--- > --- > Error: Assertion failed--- > --- > - False--- > + True--- > --- > 7│ assert : not True === True -- Oops!--- > 8│ --- > --- > ./test.dhall:7:16--- > --- > 1│ ./test.dhall--- > --- > (stdin):1:1------ You can compare expressions that contain variables, too, which is equivalent--- to symbolic reasoning:------ > $ dhall <<< '\(n : Natural) -> assert : n === (n + 0)'--- > λ(n : Natural) → assert : n ≡ n------ Dhall accepts this because the language has built-in support for normalizing--- @n + 0@ to @n@, so both sides of the comparison normalize to the same value:--- @n@.------ Note that this sort of symbolic reasoning is limited and can only detect--- equality of normal forms. Some equivalent expressions will be rejected--- if they don't share the same normal form, such as these:------ > $ dhall <<< '\(n : Natural) -> assert : Natural/even (n + n) === True'--- > --- > Use "dhall --explain" for detailed errors--- > --- > n : Natural--- > --- > Error: Assertion failed--- > --- > - … …--- > + True--- > --- > 1│ assert : Natural/even (n + n) === True--- > --- > (stdin):1:19------ Here the normalizer is not smart enough to simplify @Natural/even (n + n)@ to--- @True@ so the assertion fails.------ If you prefer to use Unicode, then the Unicode equivalent of @===@ is @≡@--- (U+2261).---- $headers------ Sometimes you would like to provide additional request headers when importing--- Dhall expressions from URLs. For example, you might want to provide an--- authorization header or specify the expected content type.------ Dhall URL imports let you add or modify request headers with the @using@--- keyword:------ > 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:------ > List { header : Text, value : Text }------ For example, if you needed to specify the content type correctly in order to--- download the file, then your @./headers@ file might look like this:------ > $ cat headers--- > [ { header = "Accept", value = "application/dhall" } ]------ ... or if you needed to provide an authorization token to access a private--- GitHub repository, then your headers could look like this:------ > [ { header = "Authorization", value = "token ${env:GITHUB_TOKEN as Text}" } ]------ This would read your GitHub API token from the @GITHUB_TOKEN@ environment--- variable and supply that token in the authorization header.------ 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/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/example.dhall using ./headers--- --- ... 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------ Sometimes you want to use share code while still ensuring that the imported--- value never changes and can't be corrupted by a malicious attacker. Dhall--- provides built-in support for hashing imported values to verify that their--- value never changes------ For example, suppose you save the following two files:------ > $ cat ./foo--- > ./bar sha256:d60d8415e36e86dae7f42933d3b0c4fe3ca238f057fba206c7e9fbf5d784fe15------ > $ cat ./bar--- > ./baz------ > $ cat ./baz--- > 1------ The first file named @./foo@ contains an example of an integrity check. You--- can add @sha256:XXX@ after any import (such as after @./bar@), where @XXX@ is--- an expected 64-character @sha256@ hash of the Dhall value. To be precise,--- the hash represents a @sha256@ hash of the UTF-8 encoding of a canonical text--- representation of the fully resolved and normalized abstract syntax tree of--- the imported expression.------ Dhall will verify that the expected hash matches the actual hash of the--- imported Dhall value and reject the import if there is a hash mismatch:------ > $ dhall <<< './foo'--- > Natural--- > --- > 1------ Any import protected by a semantic integrity check is automatically cached--- locally underneath either @~\/.cache\/dhall\/1220${HASH}@ or--- @${XDG_CACHE_HOME}\/dhall/1220${HASH}@ if you define the @XDG_CACHE_HOME@--- environment variable.------ For example, after you import @./foo@ the contents of `./bar` are locally--- cached in a fully-evaluated and binary-encoded form which you can inspect by--- running:------ > $ dhall decode < ~/.cache/dhall/1220d60d8415e36e86dae7f42933d3b0c4fe3ca238f057fba206c7e9fbf5d784fe15--- > 1------ Subsequent attempts to resolve the same import will automatically retrieve--- the import from the cache. This matters less for local imports, but comes in--- handy for remote imports to avoid redownloading them.------ The local cache takes precedence when resolving imports, so changing the--- original import afterwards will have no affect until you update the hash.--- From Dhall's point of view, the hash is the true address and the file path--- is just a suggestion for how to obtain the import if it's not already cached.------ You can disable the cache by setting `XDG_CACHE_HOME` to `/var/empty` (an--- empty and unwritable directory), like this:------ > $ XDG_CACHE_HOME=/var/empty dhall <<< './foo'--- > Natural--- > --- > 1------ We'll use this trick to test changes to the protected import in the following--- examples.------ Now, supose you add a comment to the @./bar@ file:------ > $ cat ./bar--- > -- This comment does not change the hash--- > ./baz------ ... then @./foo@ will still successfully import @./bar@ because the hash--- only depends on the normalized value and does not depend on meaningless--- changes to whitespace or comments:------ > $ XDG_CACHE_HOME=/var/empty dhall <<< './foo' # This still succeeds--- > Natural--- > --- > 1------ You can compute the Hash for any import by using the hash subcommand --- of this package. For example:------ > $ dhall hash <<< './bar'--- > sha256:d60d8415e36e86dae7f42933d3b0c4fe3ca238f057fba206c7e9fbf5d784fe15------ Now suppose that you actually change the value of the @./baz@ file:------ > $ cat ./baz--- > 2------ ... then the @./foo@ file will fail to import @./bar@, even though the--- text of the @./bar@ file technically never changed:------ > XDG_CACHE_HOME=/var/empty dhall <<< './foo'--- > --- > Error: Import integrity check failed--- > --- > Expected hash:--- > --- > ↳ d60d8415e36e86dae7f42933d3b0c4fe3ca238f057fba206c7e9fbf5d784fe15--- > --- > Actual hash:--- > --- > ↳ 4caf97e8c445d4d4b5c5b992973e098ed4ae88a355915f5a59db640a589bc9cb------ This is because the @./bar@ file now represents a new value (@2@ instead of--- @1@), even though the text of the @./bar@ file is still the same. Since the--- value changed the hash must change as well. However, we could change @./baz@--- to:------ > $ cat baz--- > if True then 1 else 2------ ... and the import would now succeed again because the final result is @1@.------ The integrity hash ensures that your import's final meaning can never change,--- so an attacker can never compromise an imported value protected by a hash--- unless they can break SHA-256 encryption. The hash not only protects the--- file that you immediately import, but also protects every transitive import--- as well.------ You can also safely refactor your imported dependencies knowing that the--- refactor will not change your hash so long as your refactor is--- behavior-preserving. This provides an easy way to detect refactoring errors--- that you might accidentally introduce. The hash not only protects you--- from attackers, but also protects against human error, too!------ If you have a file which either doesn't already use hashed imports,--- or you changed some of the imports and want to update the hashes you can use the--- freeze command to either add or update hashes:------ > $ cat foo.dhall--- > let replicate =--- > https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/List/replicate--- > in replicate 5--- > $--- > $ dhall freeze --inplace ./foo.dhall--- > $ cat ./foo.dhall--- > let replicate =--- > https://raw.githubusercontent.com/dhall-lang/Prelude/c79c2bc3c46f129cc5b6d594ce298a381bcae92c/List/replicate sha256:b0e3ec1797b32c80c0bcb7e8254b08c7e9e35e75e6b410c7ac21477ab90167ad--- > in replicate 5---- $rawText------ Sometimes you want to import the contents of a raw text file as a Dhall--- value of type `Text`. For example, one of the fields of a record might be--- the contents of a software license:------ > { package = "dhall"--- > , author = "Gabriel Gonzalez"--- > , license = ./LICENSE--- > }------ Normally if you wanted to import a text file you would need to wrap the--- contents of the file in double single-quotes, like this:------ > $ cat LICENSE--- > ''--- > Copyright (c) 2017 Gabriel Gonzalez--- > All rights reserved.--- > --- > ...--- > (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS--- > SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.--- > ''------ ... which does not work well if you need to reuse the same text file for--- other programs------ However, Dhall supports importing a raw text file by adding @as Text@ to the--- end of the import, like this:------ > { package = "dhall"--- > , author = "Gabriel Gonzalez"--- > , license = ./LICENSE as Text--- > }------ ... and then you can use the original text file unmodified:------ > $ cat LICENSE--- > Copyright (c) 2017 Gabriel Gonzalez--- > All rights reserved.--- > --- > ...--- > (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS--- > SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.---- $format------ A format subcommand is also available that you can use to --- automatically format Dhall expressions. For example, we can take the--- following unformatted Dhall expression:------ > $ cat ./unformatted--- > λ(a : Type) → λ(kvss : List (List { index : Natural, value : a })) → --- > List/build { index : Natural, value : a } (λ(list : Type) → λ(cons : { --- > index : Natural, value : a } → list → list) → λ(nil : list) → --- > (List/fold (List { index : Natural, value : a }) kvss { count : Natural, diff : --- > Natural → list } (λ(kvs : List { index : Natural, value : a }) → λ(y : { --- > count : Natural, diff : Natural → list }) → { count = y.count + List/length --- > { index : Natural, value : a } kvs, diff = λ(n : Natural) → List/fold { --- > index : Natural, value : a } kvs list (λ(kvOld : { index : Natural, value : a --- > }) → λ(z : list) → cons { index = kvOld.index + n, value = kvOld.value } --- > z) (y.diff (n + List/length { index : Natural, value : a } kvs)) }) { count = --- > 0, diff = λ(_ : Natural) → nil }).diff 0)------ ... and run the expression through the formatter:------ > $ dhall format < ./unformatted--- > λ(a : Type)--- > → λ(kvss : List (List { index : Natural, value : a }))--- > → List/build--- > { index : Natural, value : a }--- > ( λ(list : Type)--- > → λ(cons : { index : Natural, value : a } → list → list)--- > → λ(nil : list)--- > → ( List/fold--- > (List { index : Natural, value : a })--- > kvss--- > { count : Natural, diff : Natural → list }--- > ( λ(kvs : List { index : Natural, value : a })--- > → λ(y : { count : Natural, diff : Natural → list })--- > → { count = y.count + List/length { index : Natural, value : a } kvs--- > , diff =--- > λ(n : Natural)--- > → List/fold--- > { index : Natural, value : a }--- > kvs--- > list--- > ( λ(kvOld : { index : Natural, value : a })--- > → λ(z : list)--- > → cons { index = kvOld.index + n, value = kvOld.value } z--- > )--- > (y.diff (n + List/length { index : Natural, value : a } kvs))--- > }--- > )--- > { count = 0, diff = λ(_ : Natural) → nil }--- > ).diff--- > 0--- > )------ The executable formats expressions without resolving, type-checking, or--- normalizing them:------ > $ dhall format--- > let replicate = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/replicate --- > in replicate 5 (List (List Natural)) (replicate 5 (List Natural) (replicate 5 Natural 1))--- > <Ctrl-D>--- > let replicate =--- > https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/replicate --- > --- > in replicate--- > 5--- > (List (List Natural))--- > (replicate 5 (List Natural) (replicate 5 Natural 1))------ You can also use the formatter to modify files in place using the--- @--inplace@ flag (i.e. for formatting source code):------ > $ dhall format --inplace ./unformatted--- > $ cat ./unformatted--- > λ(a : Type)--- > → λ(kvss : List (List { index : Natural, value : a }))--- > → List/build--- > { index : Natural, value : a }--- > ( λ(list : Type)--- > → λ(cons : { index : Natural, value : a } → list → list)--- > → λ(nil : list)--- > → ( List/fold--- > (List { index : Natural, value : a })--- > kvss--- > { count : Natural, diff : Natural → list }--- > ( λ(kvs : List { index : Natural, value : a })--- > → λ(y : { count : Natural, diff : Natural → list })--- > → { count = y.count + List/length { index : Natural, value : a } kvs--- > , diff =--- > λ(n : Natural)--- > → List/fold--- > { index : Natural, value : a }--- > kvs--- > list--- > ( λ(kvOld : { index : Natural, value : a })--- > → λ(z : list)--- > → cons { index = kvOld.index + n, value = kvOld.value } z--- > )--- > (y.diff (n + List/length { index : Natural, value : a } kvs))--- > }--- > )--- > { count = 0, diff = λ(_ : Natural) → nil }--- > ).diff--- > 0--- > )------ Carefully note that the code formatter does not preserve all comments.--- Currently, the formatter only preserves leading comments and whitespace--- up until the last newline preceding the code. In other words:------ > $ dhall format--- > {- This comment will be preserved by the formatter -}--- > -- ... and this comment will be preserved, too--- > {- This comment will *NOT* be preserved -} 1--- > -- ... and this comment will also *NOT* be preserved--- > <Ctrl-D>--- > {- This comment will be preserved by the formatter -}--- > -- ... and this comment will be preserved, too--- > 1------ Note that you do not need to format the output of the--- @dhall@ interpreter. The interpreter already automatically formats--- multi-line expressions, too:------ > $ dhall--- > let replicate = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/replicate --- > in replicate 5 (List (List Natural)) (replicate 5 (List Natural) (replicate 5 Natural 1))--- > <Ctrl-D>--- > List (List (List Natural))--- > --- > [ [ [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > ]--- > : List (List Natural)--- > , [ [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > ]--- > : List (List Natural)--- > , [ [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > ]--- > : List (List Natural)--- > , [ [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > ]--- > : List (List Natural)--- > , [ [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > , [ 1, 1, 1, 1, 1 ] : List Natural--- > ]--- > : List (List Natural)--- > ]--- > : List (List (List Natural))---- $builtins------ Dhall is a restricted programming language that only supports simple built-in--- functions and operators. If you want to do anything fancier you will need to--- load your data into Haskell for further processing------ This section covers types, functions, and operators that are built into the--- language, meaning that you do not need to import any code to use them.--- Additionally, Dhall also comes with a Prelude (covered in the next section)--- hosted online that contains functions derived from these base utilities. The--- Prelude also re-exports all built-in functions for people who prefer--- consistency.------ The following documentation on built-ins is provided primarily as a reference.--- You don't need to read about every single built-in and you may want to skip to--- the following Prelude section.---- The language provides built-in support for the following primitive types:------ * @Bool@ values--- * @Natural@ values--- * @Integer@ values--- * @Double@ values--- * @Text@ values------ ... as well as support for the following derived types:------ * @List@s of values--- * @Optional@ values--- * Anonymous records--- * Anonymous unions---- $caveats------ Dhall differs in a few important ways from other programming languages, so--- you should keep the following caveats in mind:------ First, Dhall only supports addition and multiplication on @Natural@ numbers--- (i.e. non-negative integers), which are not the same type of number as--- @Integer@s (which can be negative). An @Integer@ number is a number prefixed--- with either a @+@ or @-@ symbol whereas a @Natural@ number has no leading--- sign. If you try to add or multiply two @Integer@s you will get a type--- error:------ > $ dhall--- > +2 + +2--- > <Ctrl-D>--- > Use "dhall --explain" for detailed errors--- > --- > Error: ❰+❱ only works on ❰Natural❱s--- > --- > +2 + +2--- > --- > (stdin):1:1------ In fact, there are no built-in functions for @Integer@s (or @Double@s). As--- far as the language is concerned they are opaque values that can only be--- shuffled around but not used in any meaningful way until they have been--- loaded into Haskell.------ Second, the equality @(==)@ and inequality @(!=)@ operators only work on--- @Bool@s. You cannot test any other types of values for equality.------ However, you can extend the language with your own built-ins using the--- Haskell API, as described in the next section.---- $extending------ You can use the Haskell API to extend the Dhall configuration language with--- new built-in functions. This section contains a simple Haskell recipe to add--- a new @Natural/equal@ built-in function of type:------ > Natural/equal : Natural → Natural → Bool------ To do so, we:--- --- * extend the type-checking context to include the type of @Natural/equal@--- * extend the normalizer to evaluate all occurrences of @Natural/equal@------ ... like this:------ > -- example.hs--- > --- > {-# LANGUAGE OverloadedStrings #-}--- > --- > module Main where--- > --- > import Dhall.Core (Expr(..), ReifiedNormalizer(..))--- > --- > import qualified Data.Text.IO--- > import qualified Dhall--- > import qualified Dhall.Context--- > import qualified Lens.Family as Lens--- > --- > main :: IO ()--- > main = do--- > text <- Data.Text.IO.getContents--- > --- > let startingContext = transform Dhall.Context.empty--- > where--- > transform = Dhall.Context.insert "Natural/equal" naturalEqualType--- > --- > naturalEqualType =--- > Pi "_" Natural (Pi "_" Natural Bool)--- > --- > let normalizer (App (App (Var "Natural/equal") (NaturalLit x)) (NaturalLit y)) =--- > Just (BoolLit (x == y))--- > normalizer _ =--- > Nothing--- > --- > let inputSettings = transform Dhall.defaultInputSettings--- > where--- > transform =--- > Lens.set Dhall.normalizer (ReifiedNormalizer (pure . normalizer))--- > . Lens.set Dhall.startingContext startingContext--- > --- > x <- Dhall.inputWithSettings inputSettings Dhall.auto text--- > --- > Data.Text.IO.putStrLn x------ Here is an example use of the above program:------ > $ ./example <<< 'if Natural/equal 2 (1 + 1) then "Equal" else "Not equal"'--- > Equal------ Note that existing Dhall tools that type-check expressions will reject--- expressions containing unexpected free variable such as @Natural/equal@:------ > $ dhall <<< 'Natural/equal 2 (1 + 1)'--- > --- > Use "dhall --explain" for detailed errors--- > --- > Error: Unbound variable--- > --- > Natural/equal --- > --- > (stdin):1:1------ You will need to either:--- --- * create your own parallel versions of these tools, or:--- * < https://github.com/dhall-lang/dhall-lang/blob/master/.github/CONTRIBUTING.md#how-do-i-change-the-language try to upstream your built-ins into the language>--- --- The general guidelines for adding new built-ins to the language are:--- --- * Keep built-ins easy to implement across language bindings--- * Prefer general purpose built-ins or built-ins appropriate for the task of program configuration--- * Design built-ins to catch errors as early as possible (i.e. when type-checking the configuration)---- $builtinOverview------ Each of the following sections provides an overview of builtin functions and--- operators for each type. For each function you get:------ * An example use of that function------ * A \"type judgement\" explaining when that function or operator is well--- typed------ For example, for the following judgement:------ > Γ ⊢ x : Bool Γ ⊢ y : Bool--- > ───────────────────────────--- > Γ ⊢ x && y : Bool------ ... you can read that as saying: "if @x@ has type @Bool@ and @y@ has type--- @Bool@, then @x && y@ has type @Bool@"------ Similarly, for the following judgement:------ > ─────────────────────────────────--- > Γ ⊢ Natural/even : Natural → Bool------ ... you can read that as saying: "@Natural/even@ always has type--- @Natural → Bool@"------ * Rules for how that function or operator behaves------ These rules are just equalities that come in handy when reasoning about code.--- For example, the section on @(&&)@ has the following rules:------ > (x && y) && z = x && (y && z)--- >--- > x && True = x--- >--- > True && x = x------ These rules are also a contract for how the compiler should behave. If you--- ever observe code that does not obey these rules you should file a bug--- report.---- $bool------ There are two values that have type @Bool@ named @True@ and @False@:------ > ───────────────--- > Γ ⊢ True : Bool------ > ────────────────--- > Γ ⊢ False : Bool------ The built-in operations for values of type @Bool@ are:------- $or------ Example:------ > $ dhall--- > True || False--- > <Ctrl-D>--- > Bool--- > --- > True------ Type:------ > Γ ⊢ x : Bool Γ ⊢ y : Bool--- > ───────────────────────────--- > Γ ⊢ x || y : Bool------ Rules:------ > (x || y) || z = x || (y || z)--- > --- > x || False = x--- > --- > False || x = x--- >--- > x || (y && z) = (x || y) && (x || z)--- > --- > x || True = True--- > --- > True || x = True---- $and------ Example:------ > $ dhall--- > True && False--- > <Ctrl-D>--- > Bool--- > --- > False------ Type:------ > Γ ⊢ x : Bool Γ ⊢ y : Bool--- > ───────────────────────────--- > Γ ⊢ x && y : Bool------ Rules:------ > (x && y) && z = x && (y && z)--- > --- > x && True = x--- > --- > True && x = x--- >--- > x && (y || z) = (x && y) || (x && z)--- > --- > x && False = False--- > --- > False && x = False---- $equal------ Example:------ > $ dhall--- > True == False--- > <Ctrl-D>--- > Bool--- > --- > False------ Type:------ > Γ ⊢ x : Bool Γ ⊢ y : Bool--- > ───────────────────────────--- > Γ ⊢ x == y : Bool------ Rules:------ > (x == y) == z = x == (y == z)--- > --- > x == True = x--- > --- > True == x = x--- >--- > x == x = True---- $unequal------ Example:------ > $ dhall--- > True != False--- > <Ctrl-D>--- > Bool--- > --- > True------ Type:------ > Γ ⊢ x : Bool Γ ⊢ y : Bool--- > ───────────────────────────--- > Γ ⊢ x != y : Bool------ Rules:------ > (x != y) != z = x != (y != z)--- > --- > x != False = x--- > --- > False != x = x--- >--- > x != x = False---- $ifthenelse------ Example:------ > $ dhall--- > if True then 3 else 5--- > <Ctrl-D>--- > Natural--- > --- > 3------ Type:------ > Γ ⊢ t : Type--- > ─────────────────────--- > Γ ⊢ b : Bool Γ ⊢ l : t Γ ⊢ r : t--- > ────────────────────────────────────--- > Γ ⊢ if b then l else r : t------ Rules:------ > if b then True else False = b--- > --- > if True then l else r = l--- > --- > if False then l else r = r---- $natural------ @Natural@ literals are non-negative numbers without any leading sign, like--- this:------ > 4 : Natural------ If you add a @+@ or @-@ sign then you get an @Integer@ literal, which is a--- different type of value---- $plus------ Example:------ > $ dhall--- > 2 + 3--- > <Ctrl-D>--- > Natural--- > --- > 5------ Type:------ > Γ ⊢ x : Natural Γ ⊢ y : Natural--- > ─────────────────────────────────--- > Γ ⊢ x + y : Natural------ Rules:------ > (x + y) + z = x + (y + z)--- >--- > x + 0 = x--- >--- > 0 + x = x---- $times------ Example:------ > $ dhall--- > 2 * 3--- > <Ctrl-D>--- > Natural--- > --- > 6------ Type:------ > Γ ⊢ x : Natural Γ ⊢ y : Natural--- > ─────────────────────────────────--- > Γ ⊢ x * y : Natural------ Rules:------ > (x * y) * z = x * (y * z)--- >--- > x * 1 = x--- >--- > 1 * x = x--- >--- > (x + y) * z = (x * z) + (y * z)--- >--- > x * (y + z) = (x * y) + (x * z)--- >--- > x * 0 = 0--- >--- > 0 * x = 0---- $even------ Example:------ > $ dhall--- > Natural/even 6--- > <Ctrl-D>--- > Bool--- > --- > True------ Type:------ > ─────────────────────────────────--- > Γ ⊢ Natural/even : Natural → Bool------ Rules:------ > Natural/even (x + y) = Natural/even x == Natural/even y--- >--- > Natural/even 0 = True--- >--- > Natural/even (x * y) = Natural/even x || Natural/even y--- >--- > Natural/even 1 = False---- $odd------ Example:------ > $ dhall--- > Natural/odd 6--- > <Ctrl-D>--- > Bool--- > --- > False------ Type:------ > ────────────────────────────────--- > Γ ⊢ Natural/odd : Natural → Bool------ Rules:------ > Natural/odd (x + y) = Natural/odd x != Natural/odd y--- >--- > Natural/odd 0 = False--- >--- > Natural/odd (x * y) = Natural/odd x && Natural/odd y--- >--- > Natural/odd 1 = True---- $isZero------ Example:------ > $ dhall--- > Natural/isZero 6--- > <Ctrl-D>--- > Bool--- > --- > False------ Type:------ > ───────────────────────────────────--- > Γ ⊢ Natural/isZero : Natural → Bool------ Rules:------ > Natural/isZero (x + y) = Natural/isZero x && Natural/isZero y--- >--- > Natural/isZero 0 = True--- >--- > Natural/isZero (x * y) = Natural/isZero x || Natural/isZero y--- >--- > Natural/isZero 1 = False---- $naturalFold------ Example:------ > $ dhall--- > Natural/fold 40 Text (λ(t : Text) → t ++ "!") "You're welcome"--- > <Ctrl-D>--- > Text--- > --- > "You're welcome!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"------ Type:------ > ──────────────────────────────────────────────────────────────────────────────────────────────────────────--- > Γ ⊢ Natural/fold : Natural → ∀(natural : Type) → ∀(succ : natural → natural) → ∀(zero : natural) → natural------ Rules:--- --- > Natural/fold (x + y) n s z = Natural/fold x n s (Natural/fold y n s z)--- > --- > Natural/fold 0 n s z = z--- > --- > Natural/fold (x * y) n s = Natural/fold x n (Natural/fold y n s)--- > --- > Natural/fold 1 n s = s---- $naturalBuild------ Example:------ > $ dhall--- > Natural/build (λ(natural : Type) → λ(succ : natural → natural) → λ(zero : natural) → succ (succ zero))--- > <Ctrl-D>--- > Natural--- > --- > 2------ Type:------ > ─────────────────────────────────────────────────────────────────────────────────────────────────────────────--- > Γ ⊢ Natural/build : (∀(natural : Type) → ∀(succ : natural → natural) → ∀(zero : natural) → natural) → Natural------ Rules:------ > Natural/fold (Natural/build x) = x--- >--- > Natural/build (Natural/fold x) = x---- $integer------ @Integer@ literals are prefixed with a @+@ (if they are non-negative) or a--- @-@ sign (if they are negative), like this:------ > +3 : Integer--- > -2 : Integer------ If you omit the sign then they are @Natural@ literals and not @Integer@s------ There are no built-in operations on @Integer@s. For all practical purposes--- they are opaque values within the Dhall language---- $double------ A @Double@ literal is a floating point value with at least one decimal--- place, such as:------ > -2.0 : Double--- > 3.14159 : Double------ There are no built-in operations on @Double@s. For all practical purposes--- they are opaque values within the Dhall language---- $text------ A @Text@ literal is just a sequence of characters enclosed in double quotes,--- like:------ > "ABC" : Text------ The only thing you can do with @Text@ values is concatenate them---- $textAppend------ Example:------ > $ dhall--- > "Hello, " ++ "world!"--- > <Ctrl-D>--- > Text--- > --- > "Hello, world!"------ Type:------ > Γ ⊢ x : Text Γ ⊢ y : Text--- > ───────────────────────────--- > Γ ⊢ x && y : Text------ Rules:------ > (x ++ y) ++ z = x ++ (y ++ z)--- > --- > x ++ "" = x--- > --- > "" ++ x = x---- $list------ Dhall @List@ literals are a sequence of values inside of brackets separated by--- commas:------ > Γ ⊢ t : Type Γ ⊢ x : t Γ ⊢ y : t ...--- > ──────────────────────────────────────────--- > Γ ⊢ [x, y, ... ] : List t------ Also, every empty @List@ must end with a mandatory type annotation, for example:------ > [] : List Natural------ The built-in operations on @List@s are:---- $listAppend------ Example:------ > $ dhall--- > [1, 2, 3] # [4, 5, 6]--- > <Ctrl-D>--- > List Natural--- >--- > [1, 2, 3, 4, 5, 6]------ Type:------ > Γ ⊢ x : List a Γ ⊢ y : List a--- > ─────────────────────────────────--- > Γ ⊢ x # y : List a------ Rules:------ > ([] : List a) # xs = xs--- >--- > xs # ([] : List a) = xs--- >--- > (xs # ys) # zs = xs # (ys # zs)---- $listFold------ Example:------ > $ dhall--- > List/fold Bool [True, False, True] Bool (λ(x : Bool) → λ(y : Bool) → x && y) True--- > <Ctrl-D>--- > Bool--- > --- > False------ Type:------ > ────────────────────────────────────────────────────────────────────────────────────────────────────────--- > Γ ⊢ List/fold : ∀(a : Type) → List a → ∀(list : Type) → ∀(cons : a → list → list) → ∀(nil : list) → list------ Rules:------ > let Prelude/List/concat = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/concat--- >--- > List/fold a (Prelude/List/concat a xss) b c--- > = List/fold (List a) xss b (λ(x : List a) → List/fold a x b c)--- >--- > List/fold a ([] : List a) b c n = n--- >--- > List/fold a ([x] : List a) b c = c x---- $listBuild------ Example:------ > $ dhall--- > List/build Natural (λ(list : Type) → λ(cons : Natural → list → list) → λ(nil : list) → cons 1 (cons 2 (cons 3 nil)))--- > <Ctrl-D>--- > List Natural--- > --- > [1, 2, 3] : List Natural------ Type:------ > ───────────────────────────────────────────────────────────────────────────────────────────────────────────--- > Γ ⊢ List/build : ∀(a : Type) → (∀(list : Type) → ∀(cons : a → list → list) → ∀(nil : list) → list) → List a------ Rules:------ > List/build t (List/fold t x) = x--- >--- > List/fold t (List/build t x) = x---- $listLength------ Example:------ > $ dhall--- > List/length Natural [1, 2, 3]--- > <Ctrl-D>--- > Natural--- > --- > 3------ Type:------ > ────────────────────────────────────────────────--- > Γ ⊢ List/length : ∀(a : Type) → List a → Natural------ Rules:------ > List/length t xs = List/fold t xs Natural (λ(_ : t) → λ(n : Natural) → n + 1) 0---- $listHead------ Example:------ > $ dhall--- > List/head Natural [1, 2, 3]--- > <Ctrl-D>--- > Optional Natural--- > --- > Some 1------ Type:------ > ───────────────────────────────────────────────--- > Γ ⊢ List/head ∀(a : Type) → List a → Optional a------ Rules:------ > let Prelude/Optional/head = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/Optional/head--- > let Prelude/List/concat = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/concat--- > let Prelude/List/concatMap = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/concatMap--- > let Prelude/List/map = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/map--- > --- > List/head a (Prelude/List/concat a xss) =--- > Prelude/Optional/head a (Prelude/List/map (List a) (Optional a) (List/head a) xss)--- > --- > List/head a ([x] : List a) = Some x--- > --- > List/head b (Prelude/List/concatMap a b f m)--- > = Prelude/Optional/concatMap a b (λ(x : a) → List/head b (f x)) (List/head a m)---- $listLast------ Example:------ > $ dhall--- > List/last Natural [1, 2, 3]--- > <Ctrl-D>--- > Optional Natural--- > --- > Some 1------ Type:------ > ─────────────────────────────────────────────────--- > Γ ⊢ List/last : ∀(a : Type) → List a → Optional a------ Rules:------ > let Prelude/Optional/last = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/Optional/last--- > let Prelude/List/concat = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/concat--- > let Prelude/List/concatMap = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/concatMap--- > let Prelude/List/map = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/map--- > --- > List/last a (Prelude/List/concat a xss) =--- > Prelude/Optional/last a (Prelude/List/map (List a) (Optional a) (List/last a) xss)--- > --- > List/last a ([x] : List a) = Some x--- > --- > List/last b (Prelude/List/concatMap a b f m)--- > = Prelude/Optional/concatMap a b (λ(x : a) → List/last b (f x)) (List/last a m)---- $listIndexed------ Example------ > $ dhall--- > List/indexed Text ["ABC", "DEF", "GHI"]--- > <Ctrl-D>--- > List { index : Natural, value : Text }--- > --- > [{ index = 0, value = "ABC" }, { index = 1, value = "DEF" }, { index = 2, value = "GHI" }] : List { index : Natural, value : Text }------ Type:------ > ─────────────────────────────────────────────────────────────────────────────--- > Γ ⊢ List/indexed : ∀(a : Type) → List a → List { index : Natural, value : a }------ Rules:------ > let Prelude/List/shifted = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/shifted--- > let Prelude/List/concat = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/concat--- > let Prelude/List/map = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/map--- > --- > List/indexed a (Prelude/List/concat a xss) =--- > Prelude/List/shifted a (Prelude/List/map (List a) (List { index : Natural, value : a }) (List/indexed a) xss)---- $listReverse------ Example:------ > $ dhall--- > List/reverse Natural [1, 2, 3]--- > <Ctrl-D>--- > List Natural--- > --- > [3, 2, 1] : List Natural------ Type:------ > ─────────────────────────────────────────────────--- > Γ ⊢ List/reverse : ∀(a : Type) → List a → List a------ Rules:------ > let Prelude/List/map = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/map--- > let Prelude/List/concat = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/concat--- > let Prelude/List/concatMap = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/List/concatMap--- > --- > List/reverse a (Prelude/List/concat a xss)--- > = Prelude/List/concat a (List/reverse (List a) (Prelude/List/map (List a) (List a) (List/reverse a) xss))--- >--- > List/reverse a ([x] : List a) = [x] : List a--- >--- > List/reverse b (Prelude/List/concatMap a b f xs)--- > = Prelude/List/concatMap a b (λ(x : a) → List/reverse b (f x)) (List/reverse a xs)--- >--- > List/reverse a ([x, y] : List a) = [y, x] : List a---- $optional1------ Dhall @Optional@ literals are either @Some@ followed by a value:------ > Γ ⊢ x : t Γ ⊢ t : Type--- > ────────────────────────--- > Γ ⊢ Some x : Optional t------ ... or @None@ followed by a type:------ > ───────────────────────────────────--- > Γ ⊢ None : ∀(t : Type) → Optional t------ Note that `None` is a valid standalone function------ The built-in operations on @Optional@ values are:---- $optionalFold------ Example:------ > $ dhall--- > Optional/fold Text (Some "ABC") Text (λ(t : Text) → t) ""--- > <Ctrl-D>--- > Text--- > --- > "ABC"------ Type:------ > ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────--- > Γ ⊢ Optional/fold : ∀(a : Type) → Optional a → ∀(optional : Type) → ∀(some : a → optional) → ∀(none : optional) → optional------ Rules:------ > Optional/fold a (None a) o j n = n--- >--- > Optional/fold a (Some x) o j n = j x---- $prelude------ There is also a Prelude available at:------ <http://prelude.dhall-lang.org>------ ... which currenty redirects to:------ <https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75>------ There is nothing \"official\" or \"standard\" about this Prelude other than--- the fact that it is mentioned in this tutorial. The \"Prelude\" is just a--- set of convenient utilities which didn't quite make the cut to be built into--- the language. Feel free to host your own custom Prelude if you want!------ If you visit the above link you can browse the Prelude, which has a few--- subdirectories. For example, the @Bool@ subdirectory has a @not@ file--- located here:------ <https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/Bool/not>------ The @not@ function is just a UTF8-encoded text file hosted online with the--- following contents------ > $ curl https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/Bool/not--- > {---- > Flip the value of a `Bool`--- > --- > Examples:--- > --- > ```--- > ./not True = False--- > --- > ./not False = True--- > ```--- > -}--- > let not : Bool → Bool--- > = λ(b : Bool) → b == False--- > --- > in not------ The file could have been much shorter, like this:------ > λ(b : Bool) → b == False------ ... but all the functions exported from the Prelude try to be as--- self-documenting as possible by including:------ * the name of the function--- * the type of the function--- * documentation (including a few examples)------ The performance penalty for adding these helpful features is negligible.------ You can use this @not@ function either directly:------ > $ dhall--- > https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/Bool/not True--- > <Ctrl-D>--- > Bool--- > --- > False------ ... or assign the URL to a shorter name:------ > $ dhall--- > let Bool/not = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/Bool/not--- > in Bool/not True--- > <Ctrl-D>--- > Bool--- > --- > False------ Some functions in the Prelude just re-export built-in functions for--- consistency and documentation, such as @Prelude\/Natural\/even@, which--- re-exports the built-in @Natural/even@ function:------ > $ curl https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/Natural/even--- > {---- > Returns `True` if a number if even and returns `False` otherwise--- > --- > Examples:--- > --- > ```--- > ./even 3 = False--- > --- > ./even 0 = True--- > ```--- > -}--- > let even : Natural → Bool--- > = Natural/even--- > --- > in even------ You can also clone the Prelude locally to your filesystem if you prefer--- using `git clone`, like this:------ > $ git clone https://github.com/dhall-lang/Prelude.git--- > $ tree Prelude--- > Prelude--- > ├── Bool--- > │ ├── and--- > │ ├── build--- > │ ├── even--- > │ ├── fold--- > │ ├── not--- > │ ├── odd--- > │ ├── or--- > │ ├── package.dhall--- > │ └── show--- > ├── Double--- > │ ├── package.dhall--- > │ └── show--- > ├── Integer--- > │ ├── package.dhall--- > │ └── show--- > ├── List--- > │ ├── all--- > │ ├── any--- > │ ├── build--- > │ ├── concat--- > │ ├── concatMap--- > │ ├── filter--- > │ ├── fold--- > │ ├── generate--- > │ ├── head--- > │ ├── indexed--- > │ ├── iterate--- > │ ├── last--- > │ ├── length--- > │ ├── map--- > │ ├── null--- > │ ├── package.dhall--- > │ ├── replicate--- > │ ├── reverse--- > │ ├── shifted--- > │ └── unzip--- > ├── Monoid--- > ├── Natural--- > │ ├── build--- > │ ├── enumerate--- > │ ├── even--- > │ ├── fold--- > │ ├── isZero--- > │ ├── odd--- > │ ├── package.dhall--- > │ ├── product--- > │ ├── show--- > │ ├── sum--- > │ └── toInteger--- > ├── Optional--- > │ ├── None--- > │ ├── Some--- > │ ├── all--- > │ ├── any--- > │ ├── build--- > │ ├── concat--- > │ ├── filter--- > │ ├── fold--- > │ ├── head--- > │ ├── last--- > │ ├── length--- > │ ├── map--- > │ ├── null--- > │ ├── package.dhall--- > │ ├── toList--- > │ └── unzip--- > ├── Record--- > │ ├── keyText--- > │ ├── keyValue--- > │ └── package.dhall--- > ├── Text--- > │ ├── concat--- > │ ├── concatMap--- > │ ├── concatMapSep--- > │ ├── concatSep--- > │ └── package.dhall--- > └── package.dhall--- > --- > 8 directories, 70 files------ Browse the Prelude online to learn more by seeing what functions are--- available and reading their inline documentation:------ <https://github.com/dhall-lang/Prelude>------ __Exercise__: Try to use a new Prelude function that has not been covered--- previously in this tutorial------ You can also import the entire Prelude as a single large record for--- convenience:------ > $ dhall--- > let Prelude = https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/package.dhall--- >--- > in λ(x : Text)--- > → Prelude.`List`.length Text (Prelude.`List`.replicate 10 Text x)--- > <Ctrl-D>--- > ∀(x : Text) → Natural--- >--- > λ(x : Text) → 10------ The organization of the package mirrors the layout of the Prelude, meaning--- that every directory is stored as a record whose children are the fields of--- that record.------ __Exercise__: Browse the Prelude by running:------ > $ dhall <<< 'https://raw.githubusercontent.com/dhall-lang/Prelude/35deff0d41f2bf86c42089c6ca16665537f54d75/package.dhall'---- $conclusion------ By this point you should be able to use the Dhall configuration language to--- author, import, and program configuration files. If you run into any issues--- you can report them at:------ <https://github.com/dhall-lang/dhall-haskell/issues>------ You can also request features, support, or documentation improvements on the--- above issue tracker.------ If you would like to contribute to the Dhall project you can try to port Dhall--- to other languages besides Haskell so that Dhall configuration files can be--- read into those languages, too.---- $faq------ * Why do empty lists require a type annotation?------ Unlike Haskell, Dhall cannot infer a polymorphic type for the empty list--- because Dhall represents polymorphic values as functions of types, like this:------ > λ(a : Type) → [] : List a------ If the compiler treated an empty list literal as syntactic short-hand for--- the above polymorphic function then you'd get the unexpected behavior where--- a list literal is a function if the list has 0 elements but not a function--- otherwise.--- --- * Does Dhall support user-defined recursive types?------ No, but you can translate recursive code to non-recursive code by following--- this guide:------ <https://github.com/dhall-lang/dhall-lang/wiki/How-to-translate-recursive-code-to-Dhall How to translate recursive code to Dhall>+ module contains a tutorial explaining how to author configuration files+ using this language+-}+module Dhall.Tutorial (+ -- * Introduction+ -- $introduction++ -- * Types+ -- $types++ -- * Imports+ -- $imports++ -- * Lists+ -- $lists++ -- * Optional values+ -- $optional0++ -- * Records+ -- $records++ -- * Functions+ -- $functions++ -- * Compiler+ -- $compiler++ -- * Strings+ -- $strings++ -- * Combine+ -- $combine++ -- * Let expressions+ -- $let++ -- * Defaults+ -- $defaults++ -- * Unions+ -- $unions++ -- * Polymorphic functions+ -- $polymorphic++ -- * Total+ -- $total++ -- * Assertions+ -- $assertions++ -- * Headers+ -- $headers++ -- * Import integrity+ -- $integrity++ -- * Raw text+ -- $rawText++ -- * Formatting code+ -- $format++ -- * Built-in functions+ -- $builtins++ -- ** Caveats+ -- $caveats++ -- ** Extending the language+ -- $extending++ -- * Prelude+ -- $prelude++ -- * Conclusion+ -- $conclusion++ -- * Frequently Asked Questions (FAQ)+ -- $faq+ ) where++import Data.Vector (Vector)+import Dhall++-- $setup+--+-- >>> :set -XOverloadedStrings++-- $introduction+--+-- The simplest way to use Dhall is to ignore the programming language features+-- and use it as a strongly typed configuration format. For example, suppose+-- that you create the following configuration file:+-- +-- > -- ./config.dhall+-- > { foo = 1+-- > , bar = [3.0, 4.0, 5.0]+-- > }+-- +-- You can read the above configuration file into Haskell using the following+-- code:+-- +-- > -- example.hs+-- > +-- > {-# LANGUAGE DeriveGeneric #-}+-- > {-# LANGUAGE OverloadedStrings #-}+-- > +-- > import Dhall+-- > +-- > data Example = Example { foo :: Natural, bar :: Vector Double }+-- > deriving (Generic, Show)+-- > +-- > instance FromDhall Example+-- > +-- > main :: IO ()+-- > main = do+-- > x <- input auto "./config.dhall"+-- > print (x :: Example)+-- +-- If you compile and run the above example, the program prints the corresponding+-- Haskell record:+-- +-- > $ ./example+-- > Example {foo = 1, bar = [3.0,4.0,5.0]}+--+-- You can also load some types directly into Haskell without having to define a+-- record, like this:+--+-- >>> import Dhall+-- >>> :set -XOverloadedStrings+-- >>> input auto "True" :: IO Bool+-- True+--+-- The `input` function can decode any value if we specify the value's expected+-- `Type`:+--+-- > input+-- > :: Type a -- Expected type+-- > -> Text -- Dhall program+-- > -> IO a -- Decoded expression+--+-- ... and we can either specify an explicit type like `bool`:+--+-- > bool :: Type Bool+-- > +-- > input bool :: Text -> IO Bool+-- >+-- > input bool "True" :: IO Bool+--+-- >>> input bool "True"+-- True+--+-- ... or we can use `auto` to let the compiler infer what type to decode from+-- the expected return type:+--+-- > auto :: FromDhall a => Type a+-- >+-- > input auto :: FromDhall a => Text -> IO a+--+-- >>> input auto "True" :: IO Bool+-- True+--+-- You can see what types `auto` supports \"out-of-the-box\" by browsing the+-- instances for the `FromDhall` class. For example, the following instance+-- says that we can directly decode any Dhall expression that evaluates to a+-- @Bool@ into a Haskell `Bool`:+--+-- > instance FromDhall Bool+--+-- ... which is why we could directly decode the string @\"True\"@ into the+-- value `True`.+--+-- There is also another instance that says that if we can decode a value of+-- type @a@, then we can also decode a @List@ of values as a `Vector` of @a@s:+--+-- > instance FromDhall a => FromDhall (Vector a)+--+-- Therefore, since we can decode a @Bool@, we must also be able to decode a+-- @List@ of @Bool@s, like this:+--+-- >>> input auto "[True, False]" :: IO (Vector Bool)+-- [True,False]+--+-- We could also specify what type to decode by providing an explicit `Type`+-- instead of using `auto` with a type annotation:+--+-- >>> input (vector bool) "[True, False]"+-- [True,False]+--+-- __Exercise:__ Create a @./config.dhall@ file that the following program can+-- decode:+--+-- > {-# LANGUAGE DeriveAnyClass #-}+-- > {-# LANGUAGE DeriveGeneric #-}+-- > {-# LANGUAGE OverloadedStrings #-}+-- > +-- > import Dhall+-- > +-- > data Person = Person { age :: Natural, name :: Text }+-- > deriving (Generic, FromDhall, Show)+-- > +-- > main :: IO ()+-- > main = do+-- > x <- input auto "./config.dhall"+-- > print (x :: Person)+--+-- __Exercise:__ Create a @./config.dhall@ file that the following program can+-- decode:+--+-- > {-# LANGUAGE OverloadedStrings #-}+-- > +-- > import Data.Functor.Identity+-- > import Dhall+-- > +-- > instance FromDhall a => FromDhall (Identity a)+-- > +-- > main :: IO ()+-- > main = do+-- > x <- input auto "./config.dhall"+-- > print (x :: Identity Double)++-- $types+--+-- Suppose that we try to decode a value of the wrong type, like this:+--+-- >>> input auto "1" :: IO Bool+-- *** Exception:+-- ...Error...: Expression doesn't match annotation+-- ...+-- - Bool+-- + Natural+-- ...+-- 1│ 1 : Bool+-- ...+-- (input):1:1+-- ...+--+-- The interpreter complains because the string @\"1\"@ cannot be decoded into a+-- Haskell value of type `Bool`. This part of the type error:+--+-- > - Bool+-- > + Natural+--+-- ... means that the expected type was @Bool@ but the inferred type of the+-- expression @1@ was @Natural@.+--+-- The code excerpt from the above error message has two components:+--+-- * the expression being type checked (i.e. @1@)+-- * the expression's expected type (i.e. @Bool@)+--+-- > Expression+-- > ⇩+-- > 1 : Bool+-- > ⇧+-- > Expected type+--+-- The @(:)@ symbol is how Dhall annotates values with their expected types.+-- This notation is equivalent to type annotations in Haskell using the @(::)@+-- symbol. Whenever you see:+--+-- > x : t+--+-- ... you should read that as \"we expect the expression @x@ to have type+-- @t@\". However, we might be wrong and if our expected type does not match the+-- expression's actual type then the type checker will complain.+--+-- In this case, the expression @1@ does not have type @Bool@ so type checking+-- fails with an exception.+--+-- __Exercise:__ Load the Dhall library into @ghci@ and run these commands to get+-- get a more detailed error message:+--+-- > >>> import Dhall+-- > >>> :set -XOverloadedStrings+-- > >>> detailed (input auto "1") :: IO Bool+-- > ...+--+-- ... then read the entire error message+--+-- __Exercise:__ Fix the type error, either by changing the value to decode or+-- changing the expected type++-- $imports+--+-- You might wonder why in some cases we can decode a configuration file:+--+-- >>> writeFile "bool" "True"+-- >>> input auto "./bool" :: IO Bool+-- True+--+-- ... and in other cases we can decode a value directly:+--+-- >>> input auto "True" :: IO Bool+-- True+--+-- This is because importing a configuration from a file is a special case of a+-- more general language feature: Dhall expressions can reference other+-- expressions by their file path.+--+-- To illustrate this, let's create three files:+-- +-- > $ echo "True" > bool1+-- > $ echo "False" > bool2+-- > $ echo "./bool1 && ./bool2" > both+--+-- ... and read in all three files in a single expression:+-- +-- >>> input auto "[ ./bool1 , ./bool2 , ./both ]" :: IO (Vector Bool)+-- [True,False,False]+--+-- Each file path is replaced with the Dhall expression contained within that+-- file. If that file contains references to other files then those references+-- are transitively resolved.+--+-- In other words: configuration files can reference other configuration files,+-- either by their relative or absolute paths. This means that we can split a+-- configuration file into multiple files, like this:+--+-- > -- ./config.dhall+-- > { foo = 1+-- > , bar = ./bar.dhall+-- > }+--+-- > -- ./bar.dhall+-- > [3.0, 4.0, 5.0]+--+-- ... which is equivalent to our original configuration:+--+-- > -- ./config.dhall+-- > { foo = 1+-- > , bar = [3.0, 4.0, 5.0]+-- > }+--+-- However, the Dhall language will forbid cycles in these file references. For+-- example, if we create the following cycle:+--+-- > $ echo './file1' > file2+-- > $ echo './file2' > file1+--+-- ... then the interpreter will reject the import:+--+-- >>> input auto "./file1" :: IO Natural+-- *** Exception: +-- ↳ ./file1+-- ↳ ./file2+-- ...+-- Cyclic import: ./file1+-- ...+--+-- You can also import expressions by URL. For example, you can find a Dhall+-- expression hosted at this GitHub URL:+--+-- <https://raw.githubusercontent.com/dhall-lang/dhall-haskell/18e4e9a18dc53271146df3ccf5b4177c3552236b/examples/True>+--+-- > $ curl https://raw.githubusercontent.com/dhall-lang/dhall-haskell/18e4e9a18dc53271146df3ccf5b4177c3552236b/examples/True+-- > True+--+-- ... and you can reference that expression either directly:+--+-- >>> input auto "https://raw.githubusercontent.com/dhall-lang/dhall-haskell/18e4e9a18dc53271146df3ccf5b4177c3552236b/examples/True" :: IO Bool+-- True+-- +-- ... or inside of a larger expression:+--+-- >>> input auto "False == https://raw.githubusercontent.com/dhall-lang/dhall-haskell/18e4e9a18dc53271146df3ccf5b4177c3552236b/examples/True" :: IO Bool+-- False+--+-- You're not limited to hosting Dhall expressions on GitHub. You can host a+-- Dhall expression anywhere that you can host UTF8-encoded text on the web,+-- such as a pastebin, or your own web server.+--+-- You can also import Dhall expressions from environment variables, too:+--+-- >>> System.Environment.setEnv "FOO" "1"+-- >>> input auto "env:FOO" :: IO Natural+-- 1+--+-- You can import types, too. For example, we can change our @./bar@ file to:+--+-- > -- ./bar.dhall+-- > [3.0, 4.0, 5.0] : List ./type.dhall+--+-- ... then specify the type in a separate file:+--+-- > -- ./type.dhall+-- > Double+--+-- ... and everything still type checks:+--+-- > $ ./example+-- > Example {foo = 1, bar = [3.0,4.0,5.0]}+--+-- Note that imports should be terminated by whitespace or parentheses otherwise+-- you will get either an import error or a parse error, like this:+--+-- >>> writeFile "baz" "2.0"+-- >>> input auto "./baz: Double" :: IO Double+-- *** Exception: +-- ↳ ./baz: +-- ...+-- ...Error...: Missing file ...baz:+-- ...+--+-- This is because the parser thinks that @./baz:@ is a single token due to+-- the missing whitespace before the colon and tries to import a file named+-- @./baz:@, which does not exist. To fix the problem we have to add a space+-- after @./baz@:+--+-- >>> input auto "./baz : Double" :: IO Double+-- 2.0+--+-- __Exercise:__ There is a @not@ function hosted online here:+--+-- <https://prelude.dhall-lang.org/Bool/not>+--+-- Visit that link and read the documentation. Then try to guess what this+-- code returns:+--+-- > >>> input auto "https://prelude.dhall-lang.org/Bool/not https://raw.githubusercontent.com/dhall-lang/dhall-haskell/18e4e9a18dc53271146df3ccf5b4177c3552236b/examples/True" :: IO Bool+-- > ???+--+-- Run the code to test your guess++-- $lists+--+-- You can store 0 or more values of the same type in a list, like this:+--+-- > [1, 2, 3]+--+-- Every list can be followed by the type of the list. The type annotation is+-- required for empty lists but optional for non-empty lists. You will get a+-- type error if you provide an empty list without a type annotation:+--+-- >>> input auto "[]" :: IO (Vector Natural)+-- *** Exception:+-- ...Error...: An empty list requires a type annotation+-- ...+-- 1│ []+-- ...+-- (input):1:1+--+-- Also, list elements must all have the same type. You will get an error if+-- you try to store elements of different types in a list:+--+-- >>> input auto "[1, True, 3]" :: IO (Vector Natural)+-- *** Exception:+-- ...Error...: List elements should all have the same type+-- ...+-- - Natural+-- + Bool+-- ...+-- 1│ True+-- ...+-- (input):1:5+-- ...+--+-- __Exercise:__ Replace the @\"???\"@ with an expression that successfully+-- decodes to the specified type:+--+-- > >>> input auto "???" :: IO (Vector (Vector Natural))++-- $optional0+--+-- @Optional@ values are either of the form @Some value@ or @None type@.+--+-- For example, these are valid @Optional@ values:+--+-- > Some 1+-- >+-- > None Natural+--+-- ... which both have type @Optional Natural@+--+-- An @Optional@ corresponds to Haskell's `Maybe` type for decoding purposes:+--+-- >>> input auto "Some 1" :: IO (Maybe Natural)+-- Just 1+-- >>> input auto "None Natural" :: IO (Maybe Natural)+-- Nothing+--+-- __Exercise:__ Replace the @\"???\"@ with an expression that successfully+-- decodes to the specified type:+--+-- > >>> input auto "???" :: IO (Maybe (Maybe (Maybe Natural)))++-- $records+--+-- Record literals are delimited by curly braces and their fields are separated+-- by commas. For example, this is a valid record literal:+--+-- > { foo = True+-- > , bar = 2+-- > , baz = 4.2+-- > }+--+-- A record type is like a record literal except instead of specifying each+-- field's value we specify each field's type. For example, the preceding+-- record literal has the following record type:+--+-- > { foo : Bool+-- > , bar : Natural+-- > , baz : Double+-- > }+--+-- If you want to specify an empty record literal, you must use @{=}@, which is+-- special syntax reserved for empty records. If you want to specify the empty+-- record type, then you use @{}@. If you forget which is which you can always+-- ask the @dhall@ compiler to remind you of the type for each one:+--+-- > $ dhall type <<< '{=}'+-- > {}+--+-- > $ dhall type <<< '{}'+-- > Type+--+-- This is our first use of the @dhall@ command-line tool (provided by this+-- package), which provides a @type@ subcommand for inferring an expression's+-- type. By default the tool reads the expression on standard input and outputs+-- the type to standard output.+--+-- Note that @<<<@ is a feature specific to the Bash shell to feed a string to+-- a command's standard input. If you are using another shell, then you can+-- instead do this:+--+-- > $ echo '{=}' | dhall type+-- > {}+--+-- __Exercise__: Use the @dhall type@ command to infer the type of this record:+--+-- > -- ./nested.dhall+-- > { foo = 1+-- > , bar =+-- > { baz = 2.0+-- > , qux = True+-- > }+-- > }+--+-- You can access a field of a record using the following syntax:+--+-- > record.fieldName+--+-- ... which means to access the value of the field named @fieldName@ from the+-- @record@. For example:+--+-- >>> input auto "{ foo = True, bar = 2, baz = 4.2 }.baz" :: IO Double+-- 4.2+--+-- ... and you can project out multiple fields into a new record using this+-- syntax:+--+-- > someRecord.{ field₀, field₁, … }+--+-- For example:+--+-- > $ dhall <<< '{ x = 1, y = True, z = "ABC" }.{ x, y }'+-- > { x = 1, y = True }+--+-- This is our first example of using the @dhall@ command-line tool with no+-- subcommand (like @type@), which evaluates the provided expression. By+-- default, this reads the expression on standard input and outputs the+-- evaluated result on standard output.+--+-- __Exercise__: Save the above @./nested.dhall@ file and then try to access the+-- value of the @baz@ field. Test that this works by interpreting your code+-- using the @dhall@ command.+--+-- You can convert a record to a list of key-value pairs (a.k.a. a \"Map\") by+-- using the `toMap` keyword. For example:+--+-- > $ dhall <<< 'toMap { foo = 1, bar = 2 }'+-- > [ { mapKey = "bar", mapValue = 2 }, { mapKey = "foo", mapValue = 1 } ]+--+-- This conversion only works if all field of the record have the same type.+-- This comes in handy when you need to convert a Dhall record to the Dhall+-- equivalent of a homogeneous map (i.e. Haskell's @"Data.Map"@).++-- $functions+--+-- The Dhall programming language also supports user-defined anonymous+-- functions. For example, we can save the following anonymous function to a+-- file:+--+-- > -- ./makeBools.dhall+-- > \(n : Bool) ->+-- > [ n && True, n && False, n || True, n || False ]+--+-- ... or we can use Dhall's support for Unicode characters to use @λ@ (U+03BB)+-- instead of @\\@ and @→@ (U+2192) instead of @->@ (for people who are into that+-- sort of thing):+--+-- > $ -- ./makeBools.dhall+-- > λ(n : Bool) →+-- > [ n && True, n && False, n || True, n || False ]+-- > <Ctrl-D>+--+-- You can read this as a function of one argument named @n@ that has type+-- @Bool@. This function returns a @List@ of @Bool@s. Each element of the+-- @List@ depends on the input argument named @n@.+--+-- The (ASCII) syntax for anonymous functions resembles the syntax for anonymous+-- functions in Haskell. The only difference is that Dhall requires you to+-- annotate the type of the function's input.+--+-- You can import this function into Haskell, too:+--+-- >>> makeBools <- input auto "./makeBools.dhall" :: IO (Bool -> Vector Bool)+-- >>> makeBools True+-- [True,False,True,True]+--+-- The reason this works is that there is an `FromDhall` instance for simple+-- functions:+--+-- > instance (ToDhall a, FromDhall b) => FromDhall (a -> b)+--+-- Thanks to currying, this instance works for functions of multiple simple+-- arguments:+--+-- >>> dhallAnd <- input auto "λ(x : Bool) → λ(y : Bool) → x && y" :: IO (Bool -> Bool -> Bool)+-- >>> dhallAnd True False+-- False+--+-- However, you can't convert anything more complex than that (like a polymorphic+-- or higher-order function). You will need to apply those functions to their+-- arguments within Dhall before converting their result to a Haskell value.+--+-- Just like `FromDhall`, you can derive `ToDhall` for user-defined data types:+--+-- > {-# LANGUAGE DeriveAnyClass #-}+-- > {-# LANGUAGE DeriveGeneric #-}+-- > {-# LANGUAGE OverloadedStrings #-}+-- > +-- > module Main where+-- > +-- > import Dhall+-- > +-- > data Example0 = Example0 { foo :: Bool, bar :: Bool }+-- > deriving (Generic, ToDhall)+-- > +-- > main = do+-- > f <- input auto "λ(r : { foo : Bool, bar : Bool }) → r.foo && r.bar"+-- > print (f (Example0 { foo = True, bar = False }) :: Bool)+-- > print (f (Example0 { foo = True, bar = True }) :: Bool)+--+-- The above program prints:+--+-- > False+-- > True++-- $compiler+--+-- We can also test our @makeBools@ function directly from the command line.+-- This library comes with a command-line executable program named @dhall@ that+-- you can use to both type-check files and convert them to a normal form. Our+-- compiler takes a program on standard input and then prints the program's type+-- to standard error followed by the program's normal form to standard output:+--+-- > $ dhall --annotate <<< './makeBools.dhall'+-- > (λ(n : Bool) → [ n, False, True, n ]) : ∀(n : Bool) → List Bool+--+-- The @--annotate@ flag adds a type signature to the output to let us know+-- what type the interpreter inferred for our expression. The type signature+-- is @∀(n : Bool) → List Bool@ which says that @makeBools@ is a function of one+-- argument named @n@ that has type @Bool@ and the function returns a @List@ of+-- @Bool@s. The @∀@ (U+2200) symbol is shorthand for the ASCII @forall@+-- keyword:+--+-- > ∀(x : a) → b -- This type ...+-- > +-- > forall (x : a) → b -- ... is the same as this type+--+-- ... and Dhall's @forall@ keyword behaves the same way as Haskell's @forall@+-- keyword for input values that are @Type@s:+--+-- > forall (x : Type) → b -- This Dhall type ...+-- +-- > forall x . b -- ... is the same as this Haskell type+--+-- The part where Dhall differs from Haskell is that you can also use+-- @∀@/@forall@ to give names to non-@Type@ arguments (such as the first+-- argument to @makeBools@).+--+-- This expression is our program's normal form:+--+-- > λ(n : Bool) → [ n, False, True, n ]+--+-- ... and the interpreter was able to simplify our expression by noting that:+--+-- * @n && True = n@+-- * @n && False = False@+-- * @n || True = True@+-- * @n || False = n@+--+-- To apply a function to an argument you separate the function and argument by+-- whitespace (just like Haskell):+--+-- @f x@+--+-- You can read the above as \"apply the function @f@ to the argument @x@\". This+-- means that we can \"apply\" our @./makeBools@ function to a @Bool@ argument+-- like this:+--+-- > $ dhall <<< './makeBools.dhall True'+-- > [True, False, True, True]+--+-- Remember that file paths are synonymous with their contents, so the above+-- code is exactly equivalent to:+-- +-- > $ dhall <<< '(λ(n : Bool) → [n && True, n && False, n || True, n || False]) True'+-- > [True, False, True, True]+--+-- When you apply an anonymous function to an argument, you substitute the+-- \"bound variable" with the function's argument:+--+-- > Bound variable+-- > ⇩+-- > (λ(n : Bool) → ...) True+-- > ⇧+-- > Function argument+--+-- So in our above example, we would replace all occurrences of @n@ with @True@,+-- like this:+--+-- > -- If we replace all of these `n`s with `True` ...+-- > [n && True, n && False, n || True, n || False]+-- >+-- > -- ... then we get this:+-- > [True && True, True && False, True || True, True || False]+-- >+-- > -- ... which reduces to the following normal form:+-- > [True, False, True, True]+--+-- Now that we've verified that our function type checks and works, we can use+-- the same function within Haskell:+--+-- >>> input auto "./makeBools.dhall True" :: IO (Vector Bool)+-- [True,False,True,True]+--+-- __Exercise__: Create a file named @getFoo@ that is a function of the following+-- type:+--+-- > ∀(r : { foo : Bool, bar : Text }) → Bool+--+-- This function should take a single input argument named @r@ that is a record+-- with two fields. The function should return the value of the @foo@ field.+--+-- __Exercise__: Use the @dhall@ command to infer the type of the function you+-- just created and verify that your function has the correct type+--+-- __Exercise__: Use the @dhall@ command to apply your function to a sample+-- record++-- $strings+-- Dhall supports ordinary string literals with Haskell-style escaping rules:+--+-- > $ dhall+-- > "Hello, \"world\"!"+-- > <Ctrl-D>+-- > "Hello, \"world\"!"+--+-- ... and Dhall also supports Nix-style multi-line string literals:+--+-- > $ dhall+-- > ''+-- > #!/bin/bash+-- > +-- > echo "Hi!"+-- > ''+-- > <Ctrl-D>+-- > "\n#!/bin/bash\n\necho \"Hi!\"\n"+--+-- These \"double single quote strings\" ignore all special characters, with one+-- exception: if you want to include a @''@ in the string, you will need to+-- escape it with a preceding @'@ (i.e. use @'''@ to insert @''@ into the final+-- string).+--+-- These strings also strip leading whitespace using the same rules as Nix.+-- Specifically: \"it strips from each line a number of spaces equal to the+-- minimal indentation of the string as a whole (disregarding the indentation+-- of empty lines).\"+--+-- You can also interpolate expressions into strings using @${...}@ syntax. For+-- example:+--+-- > $ dhall+-- > let name = "John Doe"+-- > let age = 21+-- > in "My name is ${name} and my age is ${Natural/show age}"+-- > <Ctrl-D>+-- > "My name is John Doe and my age is 21"+--+-- Note that you can only interpolate expressions of type @Text@+--+-- If you need to insert a @"${"@ into a string without interpolation then use+-- @"''${"@ (same as Nix)+--+-- > ''+-- > for file in *; do+-- > echo "Found ''${file}"+-- > done+-- > ''++-- $combine+--+-- You can combine two records, using either the @(//)@ operator or the+-- @(/\\)@ operator.+--+-- The @(//)@ operator (or @(⫽)@ U+2AFD) combines the fields of both records,+-- preferring fields from the right record if they share fields in common:+--+-- > $ dhall+-- > { foo = 1, bar = "ABC" } // { baz = True }+-- > <Ctrl-D>+-- > { bar = "ABC", baz = True, foo = 1 }+-- > $ dhall+-- > { foo = 1, bar = "ABC" } ⫽ { bar = True } -- Fancy unicode+-- > <Ctrl-D>+-- > { bar = True, foo = 1 }+--+-- Note that the order of record fields does not matter. The compiler+-- automatically sorts the fields.+--+-- The @(/\\)@ operator (or @(∧)@ U+2227) also lets you combine records, but+-- behaves differently if the records share fields in common. The operator+-- combines shared fields recursively if they are both records:+--+-- > $ dhall+-- > { foo = { bar = True }, baz = "ABC" } /\ { foo = { qux = 1.0 } }+-- > <Ctrl-D>+-- > { baz = "ABC", foo = { bar = True, qux = 1.0 } }+--+-- ... but fails with a type error if either shared field is not a record:+--+-- > $ dhall+-- > { foo = 1, bar = "ABC" } ∧ { foo = True }+-- > <Ctrl-D>+-- > Use "dhall --explain" for detailed errors+-- > +-- > Error: Field collision+-- > +-- > { foo = 1, bar = "ABC" } ∧ { foo = True }+-- > +-- > (stdin):1:1+--+-- __Exercise__: Combine any record with the empty record. What do you expect+-- to happen?+--+-- You can analogously combine record types using the @//\\\\@ operator (or @(⩓)@ U+2A53):+--+-- > $ dhall+-- > { foo : Natural } ⩓ { bar : Text }+-- > <Ctrl-D>+-- > { foo : Natural, bar : Text }+--+-- ... which behaves the exact same, except at the type level, meaning that the+-- operator descends recursively into record types:+--+-- > $ dhall+-- > { foo : { bar : Text } } ⩓ { foo : { baz : Bool }, qux : Natural }+-- > <Ctrl-D>+-- > { foo : { bar : Text, baz : Bool }, qux : Natural }++-- $let+--+-- Dhall also supports @let@ expressions, which you can use to define+-- intermediate values in the course of a computation, like this:+--+-- > $ dhall+-- > let x = "ha" in x ++ x+-- > <Ctrl-D>+-- > "haha"+--+-- You can also annotate the types of values defined within a @let@ expression,+-- like this:+--+-- > $ dhall+-- > let x : Text = "ha" in x ++ x+-- > <Ctrl-D>+-- > "haha"+--+-- You need to nest @let@ expressions if you want to define more than one value+-- in this way:+--+-- > $ dhall+-- > let x = "Hello, "+-- > let y = "world!"+-- > in x ++ y+-- > <Ctrl-D>+-- > "Hello, world!"+--+-- Dhall is whitespace-insensitive, so feel free to format things over multiple+-- lines or indent in any way that you please.+--+-- If you want to define a named function, just give a name to an anonymous+-- function:+--+-- > $ dhall+-- > let twice = λ(x : Text) → x ++ x in twice "ha"+-- > <Ctrl-D>+-- > "haha"+--+-- Unlike Haskell, Dhall does not support function arguments on the left-hand+-- side of the equals sign, so this will not work:+--+-- > $ dhall+-- > let twice (x : Text) = x ++ x in twice "ha"+-- > <Ctrl-D>+-- > Error: Invalid input+-- > +-- > (stdin):1:11:+-- > |+-- > 1 | let twice (x : Text) = x ++ x in twice "ha"+-- > | ^+-- > unexpected '('+-- > expecting ':', '=', or the rest of label+--+-- The error message says that Dhall expected either a @(:)@ (i.e. the beginning+-- of a type annotation) or a @(=)@ (the beginning of the assignment) and not a+-- function argument.+--+-- You can also use @let@ expressions to rename imports, like this:+--+-- > $ dhall+-- > let not = https://prelude.dhall-lang.org/Bool/not+-- > in not True+-- > <Ctrl-D>+-- > False+--+-- ... or to define synonyms for types:+--+-- > $ dhall <<< 'let Name : Type = Text in [ "John", "Mary" ] : List Name'+-- > List Text+-- > +-- > [ "John", "Mary" ]+--+-- __Exercise:__ What do you think the following code will normalize to?+--+-- > let x = 1+-- > let x = 2+-- > in x+--+-- Test your guess using the @dhall@ compiler+--+-- __Exercise:__ Now try to guess what this code will normalize to:+--+-- > let x = "ha"+-- > let x = x ++ "ha"+-- > in x+--+-- __Exercise:__ What about this code?+--+-- > let x = x ++ "ha"+-- > in x++-- $defaults+--+-- For records with a large number of defaultable fields you can use the+-- @::@ operator to auto-complete a record. For example:+--+-- > let greet =+-- > \(args : { greeting : Text, name : Text })+-- > -> "${args.greeting}, ${args.name}!"+-- > +-- > let Greeting =+-- > { Type = { greeting : Text, name : Text }+-- > , default = { greeting = "Hello", name = "John" }+-- > }+-- > +-- > in ''+-- > ${greet Greeting::{=}}+-- > ${greet Greeting::{ greeting = "Hola" }}+-- > ${greet Greeting::{ name = "Jane" }}+-- > ${greet Greeting::{ greeting = "Hola", name = "Jane" }}+-- > ''+--+-- This operator is syntactic sugar. Specifically an expression of the form:+--+-- > T::r+--+-- ... is equivalent to:+--+-- > (T.default // r) : T.Type+--+-- So, for example:+--+-- > Greeting::{ greeting = "Hola" }+--+-- ... is the same thing as:+--+-- > (Greeting.default // { greeting = "Hola" }) : Greeting.Type+--+-- ... which is the same thing as:+--+-- > ({ greeting = "Hello", name = "John" } // { greeting = "Hola" }) : { greeting : Text, name : Text }+--+-- ... which is the same thing as:+--+-- > { greeting = "Hola", name = "John" }++-- $unions+--+-- A union is a value that can be one of many alternative types of values. For+-- example, the following union type:+--+-- > < Left : Natural | Right : Bool >+--+-- ... represents a value that can be either a @Natural@ or a @Bool@ value. If+-- you are familiar with Haskell these are exactly analogous to Haskell's+-- \"sum types\" and the above type is equivalent to @Either Natural Bool@.+--+-- Each alternative is associated with a tag that distinguishes that alternative+-- from other alternatives. In the above example, the @Left@ tag is used for+-- the @Natural@ alternative and the @Right@ tag is used for the @Bool@+-- alternative.+--+-- You can specify the value of a union constructor like this:+--+-- > let Union = < Left : Natural | Right : Bool>+-- > +-- > in [ Union.Left 0, Union.Right True ]+--+-- In other words, you can access a union constructor as a field of a union+-- type and use that constructor to wrap a value of a type appropriate for+-- that alternative. In the above example, the @Left@ constructor can wrap+-- a @Natural@ value and the @Right@ constructor can wrap a @Bool@ value. We+-- can also confirm that by inspecting their type:+--+-- > $ echo '< Left : Natural | Right : Bool>' > ./Union+--+-- > $ dhall --annotate <<< '(./Union).Left'+-- > < Left : Natural | Right : Bool >.Left+-- > : ∀(Left : Natural) → < Left : Natural | Right : Bool >+--+-- > $ dhall --annotate <<< '(./Union).Right'+-- > < Left : Natural | Right : Bool >.Right+-- > : ∀(Right : Bool) → < Left : Natural | Right : Bool >+--+-- In other words, the @Left@ constructor is a function from a @Natural@ to a+-- value of our @Union@ type and the @Right@ constructor is a separate function+-- from a @Bool@ to that same @Union@ type.+--+-- You can consume a union using the built-in @merge@ function. For example,+-- suppose we want to convert our union to a @Bool@ but we want to behave+-- differently depending on whether or not the union is a @Natural@ wrapped in+-- the @Left@ constructor or a @Bool@ wrapped in the @Right@ constructor . We+-- would write:+--+-- > $ cat > process <<EOF+-- > λ(union : < Left : Natural | Right : Bool >)+-- > → let handlers =+-- > { Left = Natural/even -- Natural/even is a built-in function+-- > , Right = λ(b : Bool) → b+-- > }+-- > in merge handlers union+-- > EOF+--+-- Now our @./process@ function can handle both alternatives:+--+-- > $ dhall <<< './process ((./Union).Left 3)'+-- > False+--+-- > $ dhall <<< './process ((./Union).Right True)'+-- > True+--+-- Every @merge@ has the following form:+--+-- > merge handlers union [ : type ]+--+-- ... where: +--+-- * @union@ is the union you want to consume+-- * @handlers@ is a record with one function per alternative of the union+-- * @type@ is an optional declared result type of the @merge@+--+-- The @merge@ function selects which function to apply from the record based on+-- which alternative the union selects:+--+-- > merge { Foo = f, ... } (< … >.Foo x) = f x+--+-- So, for example:+--+-- > merge { Left = Natural/even, Right = λ(b : Bool) → b } (< Left : Natural | Right : Bool >.Left 3)+-- > = Natural/even 3+-- > = False+--+-- ... and similarly:+--+-- > merge { Left = Natural/even, Right = λ(b : Bool) → b } (< Left : Natural | Right : Bool >.Right True)+-- > = (λ(b : Bool) → b) True+-- > = True+--+-- Notice that each handler has to return the same type of result (@Bool@ in+-- this case).+--+-- You can also store more than one value within alternatives using Dhall's+-- support for anonymous records. You can nest an anonymous record within a+-- union such as in this type:+--+-- > < Empty : {} | Person : { name : Text, age : Natural } >+--+-- You can even go a step further and omit the type of an alternative if it+-- stores no data, like this:+--+-- > < Empty | Person : { name : Text, age : Natural } >+--+-- The above Dhall type resembles the following equivalent Haskell data type:+--+-- > data Example = Empty | Person { name :: Text, age :: Text }+--+-- Empty alternatives like @Empty@ require no argument:+--+-- > let Example = < Empty | Person : { name : Text, age : Natural } >+-- > +-- > in [ Example.Empty -- Note the absence of any argument to `Empty`+-- > , Example.Person { name = "John", age = 23 }+-- > , Example.Person { name = "Amy" , age = 25 }+-- > ]+--+-- ... and when you @merge@ an empty alternative the correspond handler takes no+-- argument:+--+-- > λ(x : < Empty | Person : { name : Text, age : Natural } >)+-- > → merge+-- > { Empty = "Unknown" -- Note the absence of a `λ`+-- >+-- > , Person =+-- > λ(person : { name : Text, age : Natural })+-- > → "Name: ${person.name}, Age: ${Natural/show person.age}"+-- > }+-- > x+--+-- __Exercise__: Create a list of the following type with at least one element+-- per alternative:+--+-- > List < Left : Natural | Right : Double >++-- $polymorphic+--+-- The Dhall language supports defining polymorphic functions (a.k.a.+-- \"generic\" functions) that work on more than one type of value. However,+-- Dhall differs from Haskell by not inferring the types of these polymorphic+-- functions. Instead, you must be explicit about what type of value the+-- function is specialized to.+--+-- Take, for example, Haskell's identity function named @id@:+--+-- > id :: a -> a+-- > id = \x -> x+--+-- The identity function is polymorphic, meaning that `id` works on values of+-- different types:+--+-- >>> id 4+-- 4+-- >>> id True+-- True+--+-- The equivalent function in Dhall is:+--+-- > λ(a : Type) → λ(x : a) → x+--+-- Notice how this function takes two arguments instead of one. The first+-- argument is the type of the second argument.+--+-- Let's illustrate how this works by actually using the above function:+--+-- > $ echo "λ(a : Type) → λ(x : a) → x" > id+--+-- Let's ask the interpreter for the type of this function:+-- the first line:+-- +-- > $ dhall type <<< './id'+-- > ∀(a : Type) → ∀(x : a) → a+--+-- You can read the type @(∀(a : Type) → ∀(x : a) → a)@ as saying: \"This is the+-- type of a function whose first argument is named @a@ and is a @Type@. The+-- second argument is named @x@ and has type @a@ (i.e. the value of the first+-- argument). The result also has type @a@.\"+--+-- This means that the type of the second argument changes depending on what+-- type we provide for the first argument. When we apply @./id@ to @Natural@,+-- we create a function that expects an @Natural@ argument:+--+-- > $ dhall type <<< './id Natural'+-- > ∀(x : Natural) → Natural+--+-- Similarly, when we apply @./id@ to @Bool@, we create a function that expects a+-- @Bool@ argument:+--+-- > $ dhall type <<< './id Bool'+-- > ∀(x : Bool) → Bool+--+-- We can then supply the final argument to each of those functions to show+-- that they both work on their respective types:+--+-- > $ dhall <<< './id Natural 4'+-- > 4+--+-- > $ dhall <<< './id Bool True'+-- > True+--+-- Built-in functions can also be polymorphic, too. For example, we can ask+-- the compiler for the type of @List/reverse@, the function that reverses a+-- list:+--+-- > $ dhall --annotate+-- > List/reverse+-- > <Ctrl-D>+-- > List/reverse : ∀(a : Type) → List a → List a+--+-- The first argument to @List/reverse@ is the type of the list to reverse:+--+-- > $ dhall+-- > List/reverse Bool+-- > <Ctrl-D>+-- > List/reverse Bool : List Bool → List Bool+--+-- ... and the second argument is the list to reverse:+--+-- > $ dhall+-- > List/reverse Bool [True, False]+-- > <Ctrl-D>+-- > [False, True]+--+-- Note that the second argument has no name. This type:+--+-- > ∀(a : Type) → List a → List a+--+-- ... is equivalent to this type:+--+-- > ∀(a : Type) → ∀(_ : List a) → List a+--+-- In other words, if you don't see the @∀@ symbol surrounding a function+-- argument type then that means that the name of the argument is @"_"@. This+-- is true even for user-defined functions:+--+-- > $ dhall type <<< 'λ(_ : Text) → 1'+-- > Text → Natural+--+-- The type @Text → Natural@ is the same as @∀(_ : Text) → Natural@+--+-- __Exercise__ : Translate Haskell's `flip` function to Dhall++-- $total+--+-- Dhall is a total programming language, which means that Dhall is not+-- Turing-complete and evaluation of every Dhall program is guaranteed to+-- eventually halt. There is no upper bound on how long the program might take+-- to evaluate, but the program is guaranteed to terminate in a finite amount of+-- time and not hang forever.+--+-- This guarantees that all Dhall programs can be safely reduced to a normal+-- form where as many functions have been evaluated as possible. In fact, Dhall+-- expressions can be evaluated even if all function arguments haven't been fully+-- applied. For example, the following program is an anonymous function:+--+-- > $ dhall+-- > \(n : Bool) -> 10 * 10+-- > <Ctrl-D>+-- > λ(n : Bool) → 100+--+-- ... and even though the function is still missing the first argument named+-- @n@ the compiler is smart enough to evaluate the body of the anonymous+-- function ahead of time before the function has even been invoked.+--+-- We can use the @map@ function from the Prelude to illustrate an even more+-- complex example:+--+-- > $ dhall+-- > let List/map = https://prelude.dhall-lang.org/List/map+-- > in λ(f : Natural → Natural) → List/map Natural Natural f [1, 2, 3]+-- > <Ctrl-D>+-- > λ(f : Natural → Natural) → [f 1, f 2, f 3]+--+-- Dhall can apply our function to each element of the list even before we specify+-- which function to apply.+--+-- The language will also never crash or throw any exceptions. Every+-- computation will succeed and produce something, even if the result might be+-- an @Optional@ value:+--+-- > $ dhall <<< 'List/head Natural ([] : List Natural)'+-- > None Natural+--+-- __Exercise__: The Dhall Prelude provides a @replicate@ function which you can+-- find here:+--+-- <https://prelude.dhall-lang.org/List/replicate>+--+-- Test what the following Dhall expression normalizes to:+--+-- > let replicate = https://prelude.dhall-lang.org/List/replicate+-- > in replicate 10+--+-- __Exercise__: If you have a lot of spare time, try to \"break the compiler\"+-- by finding an input expression that crashes or loops forever (and file a bug+-- report if you succeed).++-- $assertions+--+-- You can add compile-time assertions which you can use to add tests to your+-- code. For example, we can add some tests to our @not@ function like this:+--+-- > let not+-- > : Bool → Bool+-- > = λ(b : Bool) → b == False+-- > +-- > let example0 = assert : not False === True+-- > +-- > let example1 = assert : not True === False+-- > +-- > in not+--+-- The expression @assert : not False == True@ is a type-checking assertion+-- that two expressions have the same normal form. If the two expressions differ+-- then type-checking rejects the code.+--+-- For example, suppose that we change the example to add an incorrect assertion:+--+-- > -- ./test.dhall+-- >+-- > let not+-- > : Bool → Bool+-- > = λ(b : Bool) → b == False+-- >+-- > let example0 = assert : not False === True+-- >+-- > let example1 = assert : not True === True -- Oops!+-- >+-- > in not+--+-- The type-checker then rejects the assertion with the following error message:+--+-- > $ dhall <<< './test.dhall' +-- > +-- > ↳ ./test.dhall+-- > +-- > Error: Assertion failed+-- > +-- > - False+-- > + True+-- > +-- > 7│ assert : not True === True -- Oops!+-- > 8│ +-- > +-- > ./test.dhall:7:16+-- > +-- > 1│ ./test.dhall+-- > +-- > (stdin):1:1+--+-- You can compare expressions that contain variables, too, which is equivalent+-- to symbolic reasoning:+--+-- > $ dhall <<< '\(n : Natural) -> assert : n === (n + 0)'+-- > λ(n : Natural) → assert : n ≡ n+--+-- Dhall accepts this because the language has built-in support for normalizing+-- @n + 0@ to @n@, so both sides of the comparison normalize to the same value:+-- @n@.+--+-- Note that this sort of symbolic reasoning is limited and can only detect+-- equality of normal forms. Some equivalent expressions will be rejected+-- if they don't share the same normal form, such as these:+--+-- > $ dhall <<< '\(n : Natural) -> assert : Natural/even (n + n) === True'+-- > +-- > Use "dhall --explain" for detailed errors+-- > +-- > n : Natural+-- > +-- > Error: Assertion failed+-- > +-- > - … …+-- > + True+-- > +-- > 1│ assert : Natural/even (n + n) === True+-- > +-- > (stdin):1:19+--+-- Here the interpreter is not smart enough to simplify @Natural/even (n + n)@+-- to @True@ so the assertion fails.+--+-- If you prefer to use Unicode, then the Unicode equivalent of @===@ is @≡@+-- (U+2261).++-- $headers+--+-- Sometimes you would like to provide additional request headers when importing+-- Dhall expressions from URLs. For example, you might want to provide an+-- authorization header or specify the expected content type.+--+-- Dhall URL imports let you add or modify request headers with the @using@+-- keyword:+--+-- > 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:+--+-- > List { header : Text, value : Text }+--+-- For example, if you needed to specify the content type correctly in order to+-- download the file, then your @./headers@ file might look like this:+--+-- > $ cat headers+-- > [ { header = "Accept", value = "application/dhall" } ]+--+-- ... or if you needed to provide an authorization token to access a private+-- GitHub repository, then your headers could look like this:+--+-- > [ { header = "Authorization", value = "token ${env:GITHUB_TOKEN as Text}" } ]+--+-- This would read your GitHub API token from the @GITHUB_TOKEN@ environment+-- variable and supply that token in the authorization header.+--+-- 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/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/example.dhall using ./headers+-- +-- ... 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+--+-- Sometimes you want to use share code while still ensuring that the imported+-- value never changes and can't be corrupted by a malicious attacker. Dhall+-- provides built-in support for hashing imported values to verify that their+-- value never changes+--+-- For example, suppose you save the following two files:+--+-- > $ cat ./foo+-- > ./bar sha256:d60d8415e36e86dae7f42933d3b0c4fe3ca238f057fba206c7e9fbf5d784fe15+--+-- > $ cat ./bar+-- > ./baz+--+-- > $ cat ./baz+-- > 1+--+-- The first file named @./foo@ contains an example of an integrity check. You+-- can add @sha256:XXX@ after any import (such as after @./bar@), where @XXX@ is+-- an expected 64-character @sha256@ hash of the Dhall value. To be precise,+-- the hash represents a @sha256@ hash of the UTF-8 encoding of a canonical text+-- representation of the fully resolved and normalized abstract syntax tree of+-- the imported expression.+--+-- Dhall will verify that the expected hash matches the actual hash of the+-- imported Dhall value and reject the import if there is a hash mismatch:+--+-- > $ dhall <<< './foo'+-- > Natural+-- > +-- > 1+--+-- Any import protected by a semantic integrity check is automatically cached+-- locally underneath either @~\/.cache\/dhall\/1220${HASH}@ or+-- @${XDG_CACHE_HOME}\/dhall/1220${HASH}@ if you define the @XDG_CACHE_HOME@+-- environment variable.+--+-- For example, after you import @./foo@ the contents of `./bar` are locally+-- cached in a fully-evaluated and binary-encoded form which you can inspect by+-- running:+--+-- > $ dhall decode < ~/.cache/dhall/1220d60d8415e36e86dae7f42933d3b0c4fe3ca238f057fba206c7e9fbf5d784fe15+-- > 1+--+-- Subsequent attempts to resolve the same import will automatically retrieve+-- the import from the cache. This matters less for local imports, but comes in+-- handy for remote imports to avoid redownloading them.+--+-- The local cache takes precedence when resolving imports, so changing the+-- original import afterwards will have no affect until you update the hash.+-- From Dhall's point of view, the hash is the true address and the file path+-- is just a suggestion for how to obtain the import if it's not already cached.+--+-- You can disable the cache by setting `XDG_CACHE_HOME` to `/var/empty` (an+-- empty and unwritable directory), like this:+--+-- > $ XDG_CACHE_HOME=/var/empty dhall <<< './foo'+-- > Natural+-- > +-- > 1+--+-- We'll use this trick to test changes to the protected import in the following+-- examples.+--+-- Now, supose you add a comment to the @./bar@ file:+--+-- > $ cat ./bar+-- > -- This comment does not change the hash+-- > ./baz+--+-- ... then @./foo@ will still successfully import @./bar@ because the hash+-- only depends on the normalized value and does not depend on meaningless+-- changes to whitespace or comments:+--+-- > $ XDG_CACHE_HOME=/var/empty dhall <<< './foo' # This still succeeds+-- > Natural+-- > +-- > 1+--+-- You can compute the Hash for any import by using the hash subcommand +-- of this package. For example:+--+-- > $ dhall hash <<< './bar'+-- > sha256:d60d8415e36e86dae7f42933d3b0c4fe3ca238f057fba206c7e9fbf5d784fe15+--+-- Now suppose that you actually change the value of the @./baz@ file:+--+-- > $ cat ./baz+-- > 2+--+-- ... then the @./foo@ file will fail to import @./bar@, even though the+-- text of the @./bar@ file technically never changed:+--+-- > XDG_CACHE_HOME=/var/empty dhall <<< './foo'+-- > +-- > Error: Import integrity check failed+-- > +-- > Expected hash:+-- > +-- > ↳ d60d8415e36e86dae7f42933d3b0c4fe3ca238f057fba206c7e9fbf5d784fe15+-- > +-- > Actual hash:+-- > +-- > ↳ 4caf97e8c445d4d4b5c5b992973e098ed4ae88a355915f5a59db640a589bc9cb+--+-- This is because the @./bar@ file now represents a new value (@2@ instead of+-- @1@), even though the text of the @./bar@ file is still the same. Since the+-- value changed the hash must change as well. However, we could change @./baz@+-- to:+--+-- > $ cat baz+-- > if True then 1 else 2+--+-- ... and the import would now succeed again because the final result is @1@.+--+-- The integrity hash ensures that your import's final meaning can never change,+-- so an attacker can never compromise an imported value protected by a hash+-- unless they can break SHA-256 encryption. The hash not only protects the+-- file that you immediately import, but also protects every transitive import+-- as well.+--+-- You can also safely refactor your imported dependencies knowing that the+-- refactor will not change your hash so long as your refactor is+-- behavior-preserving. This provides an easy way to detect refactoring errors+-- that you might accidentally introduce. The hash not only protects you+-- from attackers, but also protects against human error, too!+--+-- If you have a file which either doesn't already use hashed imports,+-- or you changed some of the imports and want to update the hashes you can use the+-- freeze command to either add or update hashes:+--+-- > $ cat foo.dhall+-- > let replicate = https://prelude.dhall-lang.org/List/replicate+-- > in replicate 5+-- > $+-- > $ dhall freeze --inplace ./foo.dhall+-- > $ cat ./foo.dhall+-- > let replicate =+-- > https://prelude.dhall-lang.org/List/replicate sha256:d4250b45278f2d692302489ac3e78280acb238d27541c837ce46911ff3baa347+-- > +-- > in replicate 5++-- $rawText+--+-- Sometimes you want to import the contents of a raw text file as a Dhall+-- value of type `Text`. For example, one of the fields of a record might be+-- the contents of a software license:+--+-- > { package = "dhall"+-- > , author = "Gabriel Gonzalez"+-- > , license = ./LICENSE+-- > }+--+-- Normally if you wanted to import a text file you would need to wrap the+-- contents of the file in double single-quotes, like this:+--+-- > $ cat LICENSE+-- > ''+-- > Copyright (c) 2017 Gabriel Gonzalez+-- > All rights reserved.+-- > +-- > ...+-- > (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+-- > SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+-- > ''+--+-- ... which does not work well if you need to reuse the same text file for+-- other programs+--+-- However, Dhall supports importing a raw text file by adding @as Text@ to the+-- end of the import, like this:+--+-- > { package = "dhall"+-- > , author = "Gabriel Gonzalez"+-- > , license = ./LICENSE as Text+-- > }+--+-- ... and then you can use the original text file unmodified:+--+-- > $ cat LICENSE+-- > Copyright (c) 2017 Gabriel Gonzalez+-- > All rights reserved.+-- > +-- > ...+-- > (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+-- > SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.++-- $format+--+-- A format subcommand is also available that you can use to +-- automatically format Dhall expressions. For example, we can take the+-- following unformatted Dhall expression:+--+-- > $ cat ./unformatted+-- > λ(a : Type) → λ(kvss : List (List { index : Natural, value : a })) → +-- > List/build { index : Natural, value : a } (λ(list : Type) → λ(cons : { +-- > index : Natural, value : a } → list → list) → λ(nil : list) → +-- > (List/fold (List { index : Natural, value : a }) kvss { count : Natural, diff : +-- > Natural → list } (λ(kvs : List { index : Natural, value : a }) → λ(y : { +-- > count : Natural, diff : Natural → list }) → { count = y.count + List/length +-- > { index : Natural, value : a } kvs, diff = λ(n : Natural) → List/fold { +-- > index : Natural, value : a } kvs list (λ(kvOld : { index : Natural, value : a +-- > }) → λ(z : list) → cons { index = kvOld.index + n, value = kvOld.value } +-- > z) (y.diff (n + List/length { index : Natural, value : a } kvs)) }) { count = +-- > 0, diff = λ(_ : Natural) → nil }).diff 0)+--+-- ... and run the expression through the formatter:+--+-- > $ dhall format < ./unformatted+-- > λ(a : Type)+-- > → λ(kvss : List (List { index : Natural, value : a }))+-- > → List/build+-- > { index : Natural, value : a }+-- > ( λ(list : Type)+-- > → λ(cons : { index : Natural, value : a } → list → list)+-- > → λ(nil : list)+-- > → ( List/fold+-- > (List { index : Natural, value : a })+-- > kvss+-- > { count : Natural, diff : Natural → list }+-- > ( λ(kvs : List { index : Natural, value : a })+-- > → λ(y : { count : Natural, diff : Natural → list })+-- > → { count =+-- > y.count + List/length { index : Natural, value : a } kvs+-- > , diff =+-- > λ(n : Natural)+-- > → List/fold+-- > { index : Natural, value : a }+-- > kvs+-- > list+-- > ( λ(kvOld : { index : Natural, value : a })+-- > → λ(z : list)+-- > → cons+-- > { index = kvOld.index + n, value = kvOld.value }+-- > z+-- > )+-- > ( y.diff+-- > (n + List/length { index : Natural, value : a } kvs)+-- > )+-- > }+-- > )+-- > { count = 0, diff = λ(_ : Natural) → nil }+-- > ).diff+-- > 0+-- > )+--+-- The executable formats expressions without resolving, type-checking, or+-- normalizing them:+--+-- > $ dhall format+-- > let replicate = https://prelude.dhall-lang.org/List/replicate +-- > in replicate 5 (List (List Natural)) (replicate 5 (List Natural) (replicate 5 Natural 1))+-- > <Ctrl-D>+-- > let replicate = https://prelude.dhall-lang.org/List/replicate+-- > +-- > in replicate+-- > 5+-- > (List (List Natural))+-- > (replicate 5 (List Natural) (replicate 5 Natural 1))+--+-- You can also use the formatter to modify files in place using the+-- @--inplace@ flag (i.e. for formatting source code):+--+-- > $ dhall format --inplace ./unformatted+--+-- Carefully note that the code formatter does not preserve all comments.+-- Currently, the formatter only preserves two types of comments:+--+-- * Leading comments at the beginning of the file+-- * Comments within a @let@ binding+--+-- For example:+--+-- > $ dhall format+-- > {- This comment will be preserved by the formatter -}+-- > -- ... and this comment will be preserved, too+-- > {- This comment will *NOT* be preserved -} 1+-- > -- ... and this comment will also *NOT* be preserved+-- > <Ctrl-D>+-- > {- This comment will be preserved by the formatter -}+-- > -- ... and this comment will be preserved, too+-- > 1+--+-- Also:+--+-- > let {- This comment will be preserved -}+-- > x {- This comment will also be preserved-} =+-- > {- ... and this one will be preserved, too -}+-- > 1+-- > +-- > in x+--+-- Note that you do not need to format the output of the+-- @dhall@ interpreter. The interpreter already automatically formats+-- multi-line expressions, too:+--+-- > $ dhall+-- > let replicate = https://prelude.dhall-lang.org/List/replicate +-- > in replicate 5 (List (List Natural)) (replicate 5 (List Natural) (replicate 5 Natural 1))+-- > <Ctrl-D>+-- > [ [ [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > ]+-- > , [ [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > ]+-- > , [ [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > ]+-- > , [ [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > ]+-- > , [ [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > , [ 1, 1, 1, 1, 1 ]+-- > ]+-- > ]++-- $builtins+--+-- Dhall is a restricted programming language that only supports simple built-in+-- functions and operators. If you want to do anything fancier you will need to+-- load your data into Haskell for further processing+--+-- This section covers types, functions, and operators that are built into the+-- language, meaning that you do not need to import any code to use them.+-- Additionally, Dhall also comes with a Prelude (covered in the next section)+-- hosted online that contains functions derived from these base utilities. The+-- Prelude also re-exports all built-in functions for people who prefer+-- consistency.+--+-- The language provides built-in support for the following primitive types:+--+-- * @Bool@ values+-- * @Natural@ values+-- * @Integer@ values+-- * @Double@ values+-- * @Text@ values+--+-- ... as well as support for the following derived types:+--+-- * @List@s of values+-- * @Optional@ values+-- * Anonymous records+-- * Anonymous unions+--+-- You can find an up-to-date list of available built-in functions and operators+-- here:+--+-- <https://github.com/dhall-lang/dhall-lang/wiki/Built-in-types%2C-functions%2C-and-operators Built-in types, functions, and operators>++-- $caveats+--+-- Dhall differs in a few important ways from other programming languages, so+-- you should keep the following caveats in mind:+--+-- First, Dhall only supports addition and multiplication on @Natural@ numbers+-- (i.e. non-negative integers), which are not the same type of number as+-- @Integer@s (which can be negative). An @Integer@ number is a number prefixed+-- with either a @+@ or @-@ symbol whereas a @Natural@ number has no leading+-- sign. If you try to add or multiply two @Integer@s you will get a type+-- error:+--+-- > $ dhall+-- > +2 + +2+-- > <Ctrl-D>+-- > Use "dhall --explain" for detailed errors+-- > +-- > Error: ❰+❱ only works on ❰Natural❱s+-- > +-- > +2 + +2+-- > +-- > (stdin):1:1+--+-- In fact, there are no built-in functions for @Integer@s (or @Double@s) other+-- than @Integer/show@ and @Double/show@. As far as the language is concerned+-- they are opaque values that can only be shuffled around but not used in any+-- meaningful way until they have been loaded into Haskell.+--+-- Second, the equality @(==)@ and inequality @(!=)@ operators only work on+-- @Bool@s. You cannot test any other types of values for equality.+--+-- However, you can extend the language with your own built-ins using the+-- Haskell API, as described in the next section.++-- $extending+--+-- You can use the Haskell API to extend the Dhall configuration language with+-- new built-in functions. This section contains a simple Haskell recipe to add+-- a new @Natural/equal@ built-in function of type:+--+-- > Natural/equal : Natural → Natural → Bool+--+-- To do so, we:+-- +-- * extend the type-checking context to include the type of @Natural/equal@+-- * extend the normalizer to evaluate all occurrences of @Natural/equal@+--+-- ... like this:+--+-- > -- example.hs+-- > +-- > {-# LANGUAGE OverloadedStrings #-}+-- > +-- > module Main where+-- > +-- > import Dhall.Core (Expr(..), ReifiedNormalizer(..))+-- > +-- > import qualified Data.Text.IO+-- > import qualified Dhall+-- > import qualified Dhall.Context+-- > import qualified Lens.Family as Lens+-- > +-- > main :: IO ()+-- > main = do+-- > text <- Data.Text.IO.getContents+-- > +-- > let startingContext = transform Dhall.Context.empty+-- > where+-- > transform = Dhall.Context.insert "Natural/equal" naturalEqualType+-- > +-- > naturalEqualType =+-- > Pi "_" Natural (Pi "_" Natural Bool)+-- > +-- > let normalizer (App (App (Var "Natural/equal") (NaturalLit x)) (NaturalLit y)) =+-- > Just (BoolLit (x == y))+-- > normalizer _ =+-- > Nothing+-- > +-- > let inputSettings = transform Dhall.defaultInputSettings+-- > where+-- > transform =+-- > Lens.set Dhall.normalizer (ReifiedNormalizer (pure . normalizer))+-- > . Lens.set Dhall.startingContext startingContext+-- > +-- > x <- Dhall.inputWithSettings inputSettings Dhall.auto text+-- > +-- > Data.Text.IO.putStrLn x+--+-- Here is an example use of the above program:+--+-- > $ ./example <<< 'if Natural/equal 2 (1 + 1) then "Equal" else "Not equal"'+-- > Equal+--+-- Note that existing Dhall tools that type-check expressions will reject+-- expressions containing unexpected free variable such as @Natural/equal@:+--+-- > $ dhall <<< 'Natural/equal 2 (1 + 1)'+-- > +-- > Use "dhall --explain" for detailed errors+-- > +-- > Error: Unbound variable+-- > +-- > Natural/equal +-- > +-- > (stdin):1:1+--+-- You will need to either:+-- +-- * create your own parallel versions of these tools, or:+-- * < https://github.com/dhall-lang/dhall-lang/blob/master/.github/CONTRIBUTING.md#how-do-i-change-the-language try to upstream your built-ins into the language>+-- +-- The general guidelines for adding new built-ins to the language are:+-- +-- * Keep built-ins easy to implement across language bindings+-- * Prefer general purpose built-ins or built-ins appropriate for the task of program configuration+-- * Design built-ins to catch errors as early as possible (i.e. when type-checking the configuration)++-- $prelude+--+-- There is also a Prelude available at:+--+-- <https://prelude.dhall-lang.org>+--+-- If you visit the above link you can browse the Prelude, which has a few+-- subdirectories. For example, the @Bool@ subdirectory has a @not@ file, which+-- you can reference using this URL:+--+-- <https://prelude.dhall-lang.org/Bool/not>+--+-- The @not@ function is just a UTF8-encoded text file hosted online with the+-- following contents+--+-- > $ $ dhall <<< 'https://prelude.dhall-lang.org/Bool/not as Text'+-- > ''+-- > {-+-- > Flip the value of a `Bool`+-- > -}+-- > let not : Bool → Bool = λ(b : Bool) → b == False+-- > +-- > let example0 = assert : not True ≡ False+-- > +-- > let example1 = assert : not False ≡ True+-- > +-- > in not+-- > ''+--+-- The file could have been much shorter, like this:+--+-- > λ(b : Bool) → b == False+--+-- ... but all the functions exported from the Prelude try to be as+-- self-documenting as possible by including:+--+-- * the name of the function+-- * the type of the function+-- * documentation+-- * tests+--+-- The performance penalty for adding these helpful features is negligible,+-- especially if you protect the import with a semantic integrity check because+-- then the import would be cached compactly as @λ(_ : Bool) → _ == False@.+--+-- You can use this @not@ function either directly:+--+-- > $ dhall+-- > https://prelude.dhall-lang.org/Bool/not True+-- > <Ctrl-D>+-- > False+--+-- ... or assign the URL to a shorter name:+--+-- > $ dhall+-- > let Bool/not = https://prelude.dhall-lang.org/Bool/not+-- > in Bool/not True+-- > <Ctrl-D>+-- > False+--+-- Some functions in the Prelude just re-export built-in functions for+-- consistency and documentation, such as @Prelude\/Natural\/even@, which+-- re-exports the built-in @Natural/even@ function:+--+-- > $ dhall <<< 'https://prelude.dhall-lang.org/Natural/even as Text'+-- > ''+-- > {-+-- > Returns `True` if a number if even and returns `False` otherwise+-- > -}+-- > let even : Natural → Bool = Natural/even+-- > +-- > let example0 = assert : even 3 ≡ False+-- > +-- > let example1 = assert : even 0 ≡ True+-- > +-- > in even+-- > ''+--+-- You can also clone the Prelude locally to your filesystem if you prefer+-- using `git clone`, like this:+--+-- > $ git clone https://github.com/dhall-lang/dhall-lang.git+-- > $ tree dhall-lang/Prelude+-- > dhall-lang/Prelude+-- > ├── Bool+-- > │ ├── and+-- > │ ├── build+-- > │ ├── even+-- > │ ├── fold+-- > │ ├── not+-- > │ ├── odd+-- > │ ├── or+-- > │ ├── package.dhall+-- > │ └── show+-- > ├── Double+-- > │ ├── package.dhall+-- > │ └── show+-- > ├── Function+-- > │ ├── compose+-- > │ └── package.dhall+-- > ├── Integer+-- > │ ├── package.dhall+-- > │ ├── show+-- > │ └── toDouble+-- > ├── JSON+-- > │ ├── Nesting+-- > │ ├── Tagged+-- > │ ├── Type+-- > │ ├── array+-- > │ ├── bool+-- > │ ├── keyText+-- > │ ├── keyValue+-- > │ ├── null+-- > │ ├── number+-- > │ ├── object+-- > │ ├── package.dhall+-- > │ ├── render+-- > │ └── string+-- > ├── List+-- > │ ├── all+-- > │ ├── any+-- > │ ├── build+-- > │ ├── concat+-- > │ ├── concatMap+-- > │ ├── default+-- > │ ├── empty+-- > │ ├── filter+-- > │ ├── fold+-- > │ ├── generate+-- > │ ├── head+-- > │ ├── indexed+-- > │ ├── iterate+-- > │ ├── last+-- > │ ├── length+-- > │ ├── map+-- > │ ├── null+-- > │ ├── package.dhall+-- > │ ├── replicate+-- > │ ├── reverse+-- > │ ├── shifted+-- > │ └── unzip+-- > ├── Location+-- > │ ├── Type+-- > │ └── package.dhall+-- > ├── Map+-- > │ ├── Entry+-- > │ ├── Type+-- > │ ├── empty+-- > │ ├── keyText+-- > │ ├── keyValue+-- > │ ├── keys+-- > │ ├── map+-- > │ ├── package.dhall+-- > │ └── values+-- > ├── Monoid+-- > ├── Natural+-- > │ ├── build+-- > │ ├── enumerate+-- > │ ├── equal+-- > │ ├── even+-- > │ ├── fold+-- > │ ├── greaterThan+-- > │ ├── greaterThanEqual+-- > │ ├── isZero+-- > │ ├── lessThan+-- > │ ├── lessThanEqual+-- > │ ├── odd+-- > │ ├── package.dhall+-- > │ ├── product+-- > │ ├── show+-- > │ ├── subtract+-- > │ ├── sum+-- > │ ├── toDouble+-- > │ └── toInteger+-- > ├── Optional+-- > │ ├── all+-- > │ ├── any+-- > │ ├── build+-- > │ ├── concat+-- > │ ├── default+-- > │ ├── filter+-- > │ ├── fold+-- > │ ├── head+-- > │ ├── last+-- > │ ├── length+-- > │ ├── map+-- > │ ├── null+-- > │ ├── package.dhall+-- > │ ├── toList+-- > │ └── unzip+-- > ├── Text+-- > │ ├── concat+-- > │ ├── concatMap+-- > │ ├── concatMapSep+-- > │ ├── concatSep+-- > │ ├── default+-- > │ ├── defaultMap+-- > │ ├── package.dhall+-- > │ └── show+-- > ├── XML+-- > │ ├── Type+-- > │ ├── attribute+-- > │ ├── element+-- > │ ├── emptyAttributes+-- > │ ├── leaf+-- > │ ├── package.dhall+-- > │ ├── render+-- > │ └── text+-- > └── package.dhall+--+-- Browse the Prelude online to learn more by seeing what functions are+-- available and reading their inline documentation:+--+-- <https://prelude.dhall-lang.org>+--+-- __Exercise__: Try to use a new Prelude function that has not been covered+-- previously in this tutorial+--+-- You can also import the entire Prelude as a single large record for+-- convenience:+--+-- > $ dhall+-- > let Prelude = https://prelude.dhall-lang.org/package.dhall+-- >+-- > in λ(x : Text)+-- > → Prelude.List.length Text (Prelude.List.replicate 10 Text x)+-- > <Ctrl-D>+-- > ∀(x : Text) → Natural+-- >+-- > λ(x : Text) → 10+--+-- The organization of the package mirrors the layout of the Prelude, meaning+-- that every directory is stored as a record whose children are the fields of+-- that record.+--+-- __Exercise__: Browse the Prelude by running:+--+-- > $ dhall <<< 'https://prelude.dhall-lang.org/package.dhall'++-- $conclusion+--+-- By this point you should be able to use the Dhall configuration language to+-- author, import, and program configuration files. If you run into any issues+-- you can report them at:+--+-- <https://github.com/dhall-lang/dhall-haskell/issues>+--+-- You can also request features, support, or documentation improvements on the+-- above issue tracker.+--+-- If you would like to contribute to the Dhall project you can try to port+-- Dhall to languages that do not yet have Dhall integrations so that Dhall configuration files+-- can be read into those languages, too.++-- $faq+--+-- <https://github.com/dhall-lang/dhall-lang/wiki/Frequently-Asked-Questions-(FAQ) Frequently Asked Questions (FAQ)>
src/Dhall/TypeCheck.hs view
@@ -53,6 +53,7 @@ import qualified Data.Text as Text import qualified Data.Text.Prettyprint.Doc as Pretty import qualified Data.Text.Prettyprint.Doc.Render.String as Pretty+import qualified Data.Traversable import qualified Dhall.Context import qualified Dhall.Core import qualified Dhall.Diff@@ -61,8 +62,16 @@ import qualified Dhall.Set import qualified Dhall.Pretty.Internal import qualified Dhall.Util+import qualified Lens.Family +{-| A type synonym for `Void`++ This is provided for backwards compatibility, since Dhall used to use its+ own `X` type instead of @"Data.Void".`Void`@. You should use `Void` instead+ of `X` now+-} type X = Void+{-# DEPRECATED X "Use Data.Void.Void instead" #-} traverseWithIndex_ :: Applicative f => (Int -> a -> f b) -> Seq a -> f () traverseWithIndex_ k xs =@@ -305,7 +314,7 @@ if Eval.conv values _T₀' _T₁' then do- return _T₀'+ return _T₁' else do let _T₀'' = quote names _T₀'@@ -593,35 +602,20 @@ die MissingListType ListLit (Just _T₀) ts -> do- _ <- loop ctx _T₀-- let _T₀' = eval values _T₀-- let _T₀'' = quote names _T₀'-- case _T₀' of- VList _T₁' -> do- tT₁' <- loop ctx (quote names _T₁')-- case tT₁' of- VConst Type -> return ()- _ -> die (InvalidListType _T₀'')-- _ -> do- die (InvalidListType _T₀'')+ if Data.Sequence.null ts+ then do+ _ <- loop ctx _T₀ - let process i t = do- _T₁' <- loop ctx t+ let _T₀' = eval values _T₀ - if Eval.conv values _T₀' _T₁'- then return ()- else do- let _T₁'' = quote names _T₁'- die (InvalidListElement i _T₀'' t _T₁'')+ let _T₀'' = quote names _T₀' - traverseWithIndex_ process ts+ case _T₀' of+ VList _ -> return _T₀'+ _ -> die (InvalidListType _T₀'') - return _T₀'+ -- See https://github.com/dhall-lang/dhall-haskell/issues/1359.+ else die ListLitInvariant ListAppend x y -> do tx' <- loop ctx x@@ -757,18 +751,16 @@ return (VConst c) RecordLit xts -> do- let process x t = do+ let process t = do _T' <- loop ctx t let _T'' = quote names _T' - tT' <- loop ctx _T''+ _ <- loop ctx _T'' - case tT' of- VConst _ -> return _T'- _ -> die (InvalidFieldType x _T'')+ return _T' - xTs <- Dhall.Map.unorderedTraverseWithKey process (Dhall.Map.sort xts)+ xTs <- traverse process (Dhall.Map.sort xts) return (VRecord xTs) @@ -904,6 +896,9 @@ return (VRecord (Dhall.Map.union xRs' xLs')) + RecordCompletion l r -> do+ loop ctx (Annot (Prefer (Field l "default") r) (Field l "Type"))+ Merge t u mT₁ -> do _T' <- loop ctx t @@ -937,86 +932,100 @@ then return () else die (UnusedHandler diffT) - (my₀, _T₁') <- do- case mT₁ of- Just _T₁ -> do- return (Nothing, eval values _T₁)+ if Data.Set.null diffU+ then return ()+ else die (MissingHandler diffU) - Nothing -> do- case Dhall.Map.uncons yTs' of- Nothing -> do- die MissingMergeType+ let match _y _T₀' Nothing =+ return _T₀' - Just (y₀, _T', _) -> do- _T₁' <- do- case Dhall.Map.lookup y₀ yUs' of- Nothing -> do- die (UnusedHandler diffU)+ match y handler' (Just _A₁') =+ case Eval.toVHPi handler' of+ Just (x, _A₀', _T₀') -> do+ if Eval.conv values _A₀' _A₁'+ then do+ let _T₁' = _T₀' (fresh ctx x) - Just Nothing -> do- return _T'+ let _T₁'' = quote names _T₁' - Just (Just _) -> do- case Eval.toVHPi _T' of- Just (x, _A₀', _T₀') -> do- return (_T₀' (fresh ctx x))+ -- x appearing in _T₁'' would indicate a disallowed+ -- handler type (see+ -- https://github.com/dhall-lang/dhall-lang/issues/749).+ --+ -- If x appears in _T₁'', quote will have given it index+ -- -1. Any well-typed variable has a non-negative index,+ -- so we can simply look for negative indices to detect x.+ let containsBadVar (Var (V _ n)) =+ n < 0 - Nothing -> do- let _T'' = quote names _T'+ containsBadVar e =+ Lens.Family.anyOf+ Dhall.Core.subExpressions+ containsBadVar+ e - die (HandlerNotAFunction y₀ _T'')+ if containsBadVar _T₁''+ then do+ let handler'' = quote names handler' - return (Just y₀, _T₁')+ let outputType = Dhall.Core.shift 1 (V x (-1)) _T₁'' - let _T₁'' = quote names _T₁'+ die (DisallowedHandlerType y handler'' outputType x) - _ <- loop ctx _T₁''+ else return _T₁' - let process y mU = do- case Dhall.Map.lookup y yTs' of- Nothing -> do- die (MissingHandler diffU)+ else do+ let _A₀'' = quote names _A₀'+ let _A₁'' = quote names _A₁' - Just _T' -> do- _T₃' <- do- case mU of- Nothing -> do- return _T'+ die (HandlerInputTypeMismatch y _A₁'' _A₀'') - Just _A₁' -> do- case Eval.toVHPi _T' of- Just (x, _A₀', _T₂') -> do- if Eval.conv values _A₀' _A₁'- then do- return ()+ Nothing -> do+ let handler'' = quote names handler' - else do- let _A₀'' = quote names _A₀'- let _A₁'' = quote names _A₁'+ die (HandlerNotAFunction y handler'') - die (HandlerInputTypeMismatch y _A₁'' _A₀'')+ matched <-+ sequence+ (Data.Map.intersectionWithKey match (Dhall.Map.toMap yTs') (Dhall.Map.toMap yUs')) - return (_T₂' (fresh ctx x))+ let checkMatched :: Data.Map.Map Text (Val a) -> Either (TypeError s a) (Maybe (Val a))+ checkMatched = fmap (fmap snd) . Data.Foldable.foldlM go Nothing . Data.Map.toList+ where+ go Nothing (y₁, _T₁') =+ return (Just (y₁, _T₁')) - Nothing -> do- let _T'' = quote names _T'+ go yT₀'@(Just (y₀, _T₀')) (y₁, _T₁') =+ if Eval.conv values _T₀' _T₁'+ then return yT₀' - die (HandlerNotAFunction y _T'')+ else do+ let _T₀'' = quote names _T₀'+ let _T₁'' = quote names _T₁'+ die (HandlerOutputTypeMismatch y₀ _T₀'' y₁ _T₁'') - if Eval.conv values _T₁' _T₃'- then do- return ()+ mT₀' <- checkMatched matched - else do- let _T₃'' = quote names _T₃'+ mT₁' <- Data.Traversable.for mT₁ $ \_T₁ -> do+ _ <- loop ctx _T₁ - case my₀ of- Nothing -> die (InvalidHandlerOutputType y _T₁'' _T₃'')- Just y₀ -> die (HandlerOutputTypeMismatch y₀ _T₁'' y _T₃'')+ return (eval values _T₁) - Dhall.Map.unorderedTraverseWithKey_ process yUs'+ case (mT₀', mT₁') of+ (Nothing, Nothing) ->+ die MissingMergeType+ (Nothing, Just _T₁') ->+ return _T₁'+ (Just _T₀', Nothing) ->+ return _T₀'+ (Just _T₀', Just _T₁') -> do+ if Eval.conv values _T₀' _T₁'+ then return _T₀' - return _T₁'+ else do+ let _T₀'' = quote names _T₀'+ let _T₁'' = quote names _T₁'+ die (AnnotMismatch (Merge t u Nothing) _T₁'' _T₀'') ToMap e mT₁ -> do _E' <- loop ctx e@@ -1080,7 +1089,7 @@ die (InvalidToMapType _T₁'') (Just (Right _T'), Just _T₁') | Eval.conv values (mapType _T') _T₁' -> do- pure _T₁'+ pure (mapType _T') | otherwise -> do let _T₁'' = quote names _T₁' @@ -1093,8 +1102,6 @@ case _E' of VRecord xTs' -> do- _ <- loop ctx _E''- case Dhall.Map.lookup x xTs' of Just _T' -> return _T' Nothing -> die (MissingField x _E'')@@ -1121,8 +1128,6 @@ case _E' of VRecord xTs' -> do- _ <- loop ctx _E''- let process x = case Dhall.Map.lookup x xTs' of Just _T' -> return (x, _T')@@ -1269,6 +1274,7 @@ | MismatchedListElements Int (Expr s a) (Expr s a) (Expr s a) | InvalidListElement Int (Expr s a) (Expr s a) (Expr s a) | InvalidListType (Expr s a)+ | ListLitInvariant | InvalidSome (Expr s a) (Expr s a) (Expr s a) | InvalidPredicate (Expr s a) (Expr s a) | IfBranchMismatch (Expr s a) (Expr s a) (Expr s a) (Expr s a)@@ -1292,6 +1298,7 @@ | UnusedHandler (Set Text) | MissingHandler (Set Text) | HandlerInputTypeMismatch Text (Expr s a) (Expr s a)+ | DisallowedHandlerType Text (Expr s a) (Expr s a) Text | HandlerOutputTypeMismatch Text (Expr s a) Text (Expr s a) | InvalidHandlerOutputType Text (Expr s a) (Expr s a) | MissingMergeType@@ -2240,6 +2247,21 @@ txt2 = insert expr2 txt3 = insert expr3 +prettyTypeMessage (ListLitInvariant) = ErrorMessages {..}+ where+ short = "Internal error: A non-empty list literal violated an internal invariant"++ long =+ "Explanation: Internal error: A non-empty list literal violated an internal \n\+ \invariant. \n\+ \ \n\+ \A non-empty list literal must always be represented as \n\+ \ \n\+ \ ListLit Nothing [x, y, ...] \n\+ \ \n\+ \Please file a bug report at https://github.com/dhall-lang/dhall-haskell/issues, \n\+ \ideally including the offending source code. \n"+ prettyTypeMessage (InvalidListType expr0) = ErrorMessages {..} where short = "Invalid type for ❰List❱"@@ -3140,6 +3162,52 @@ txt1 = insert expr1 txt2 = insert expr2 +prettyTypeMessage (DisallowedHandlerType label handlerType handlerOutputType variable) =+ ErrorMessages {..}+ where+ short = "Disallowed handler type"++ long =+ "Explanation: You can ❰merge❱ the alternatives of a union using a record with one\n\+ \handler per alternative, like this: \n\+ \ \n\+ \ \n\+ \ ┌─────────────────────────────────────────────────────────────────┐ \n\+ \ │ let union = < Left : Natural | Right : Bool >.Left 2 │ \n\+ \ │ let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │ \n\+ \ │ in merge handlers union : Bool │ \n\+ \ └─────────────────────────────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \... but the output type of a handler may not depend on the input value. \n\+ \ \n\+ \For example, the following expression is " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ Invalid: The output type is ❰Optional A❱, which references the input \n\+ \ value ❰A❱. \n\+ \ ⇩ \n\+ \ ┌──────────────────────────────────────────┐ \n\+ \ │ merge { x = None } (< x : Type >.x Bool) │ \n\+ \ └──────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \Your handler for the following alternative: \n\+ \ \n\+ \" <> insert label <> "\n\+ \ \n\+ \... has type: \n\+ \ \n\+ \" <> insert handlerType <> "\n\+ \ \n\+ \... where the output type: \n\+ \ \n\+ \" <> insert handlerOutputType <> "\n\+ \ \n\+ \... references the handler's input value: \n\+ \ \n\+ \" <> insert variable <> "\n"+ prettyTypeMessage (InvalidHandlerOutputType expr0 expr1 expr2) = ErrorMessages {..} where@@ -3316,7 +3384,19 @@ \ └─────────────────────────────────────────────────────────────────────┘ \n\ \ \n\ \ \n\- \... but the argument to ❰toMap❱ must be a record and not some other type. \n"+ \... but the argument to ❰toMap❱ must be a record and not some other type. \n\+ \ \n\+ \Some common reasons why you might get this error: \n\+ \ \n\+ \● You accidentally provide an empty record type instead of an empty record when \n\+ \ using ❰toMap❱: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────────────────────────────┐ \n\+ \ │ toMap {} : List { mapKey : Text, mapValue : Natural } │ \n\+ \ └───────────────────────────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ This should be ❰{=}❱ instead \n" prettyTypeMessage (InvalidToMapRecordKind type_ kind) = ErrorMessages {..} where@@ -3430,9 +3510,9 @@ "Explanation: You can only access fields on records or unions, like this: \n\ \ \n\ \ \n\- \ ┌───────────────────────────────────┐ \n\+ \ ┌─────────────────────────────────┐ \n\ \ │ { foo = True, bar = \"ABC\" }.foo │ This is valid ... \n\- \ └───────────────────────────────────┘ \n\+ \ └─────────────────────────────────┘ \n\ \ \n\ \ \n\ \ ┌───────────────────────────────────────────┐ \n\@@ -4249,6 +4329,8 @@ InvalidListElement <$> pure a <*> f b <*> f c <*> f d InvalidListType a -> InvalidListType <$> f a+ ListLitInvariant ->+ pure ListLitInvariant InvalidSome a b c -> InvalidSome <$> f a <*> f b <*> f c InvalidPredicate a b ->@@ -4295,6 +4377,8 @@ MissingHandler <$> pure a HandlerInputTypeMismatch a b c -> HandlerInputTypeMismatch <$> pure a <*> f b <*> f c+ DisallowedHandlerType a b c d ->+ DisallowedHandlerType <$> pure a <*> f b <*> f c <*> pure d HandlerOutputTypeMismatch a b c d -> HandlerOutputTypeMismatch <$> pure a <*> f b <*> pure c <*> f d InvalidHandlerOutputType a b c ->
src/Dhall/Util.hs view
@@ -9,6 +9,7 @@ , _ERROR , Censor(..) , Input(..)+ , Output(..) , getExpression , getExpressionAndHeader ) where@@ -97,13 +98,13 @@ get parser censor input = do inText <- do case input of- File file -> Data.Text.IO.readFile file- StandardInput -> Data.Text.IO.getContents+ InputFile file -> Data.Text.IO.readFile file+ StandardInput -> Data.Text.IO.getContents let name = case input of- File file -> file- StandardInput -> "(stdin)"+ InputFile file -> file+ StandardInput -> "(stdin)" let result = parser name inText @@ -118,7 +119,10 @@ data Censor = NoCensor | Censor -- | Path to input-data Input = StandardInput | File FilePath+data Input = StandardInput | InputFile FilePath++-- | Path to output+data Output = StandardOutput | OutputFile FilePath -- | Convenient utility for retrieving an expression getExpression :: Censor -> Input -> IO (Expr Src Import)
src/Dhall/Version.hs view
@@ -1,3 +1,6 @@+{-| Utilities for getting the current version of the Haskell implementation of+ Dhall+-} module Dhall.Version ( dhallVersion , dhallVersionString ) where@@ -5,8 +8,10 @@ import qualified Data.Version as V import qualified Paths_dhall as P +-- | The current `Version` of the Haskell implementation dhallVersion :: V.Version dhallVersion = P.version +-- | The current version `String` for the Haskell implementation dhallVersionString :: String dhallVersionString = V.showVersion dhallVersion
tests/Dhall/Test/Dhall.hs view
@@ -22,7 +22,7 @@ import Data.Scientific (Scientific) import Data.Text (Text) import Data.Vector (Vector)-import Dhall (Inject, Interpret)+import Dhall (ToDhall, FromDhall) import Dhall.Core (Expr(..)) import GHC.Generics (Generic) import Numeric.Natural (Natural)@@ -40,14 +40,14 @@ = LitF Natural | AddF expr expr | MulF expr expr- deriving (Eq, Functor, Generic, Interpret, Show)+ deriving (Eq, Functor, Generic, FromDhall, Show) tests :: TestTree tests = testGroup "Input" [ shouldShowDetailedTypeError , shouldHandleUnionLiteral- , shouldHaveWorkingRecursiveInterpret+ , shouldHaveWorkingRecursiveFromDhall , shouldHaveWorkingGenericAuto , shouldHandleUnionsCorrectly , shouldTreatAConstructorStoringUnitAsEmptyAlternative@@ -116,8 +116,8 @@ Dhall.embed exampleInputType () @=? Field (Union (Dhall.Map.singleton "A" Nothing)) "A" -shouldHaveWorkingRecursiveInterpret :: TestTree-shouldHaveWorkingRecursiveInterpret = testGroup "recursive Interpret instance"+shouldHaveWorkingRecursiveFromDhall :: TestTree+shouldHaveWorkingRecursiveFromDhall = testGroup "recursive FromDhall instance" [ testCase "works for a recursive expression" $ do actual <- Dhall.input Dhall.auto "./tests/recursive/expr0.dhall" @@ -156,10 +156,10 @@ ] data NonEmptyUnion = N0 Bool | N1 Natural | N2 Text- deriving (Eq, Generic, Inject, Interpret, Show)+ deriving (Eq, Generic, ToDhall, FromDhall, Show) data Enum = E0 | E1 | E2- deriving (Eq, Generic, Inject, Interpret, Show)+ deriving (Eq, Generic, ToDhall, FromDhall, Show) data Records = R0 {}@@ -167,12 +167,10 @@ | R2 { x :: Double } | R3 { a :: (), b :: () } | R4 { x :: Double, y :: Double }- deriving (Eq, Generic, Inject, Interpret, Show)+ deriving (Eq, Generic, ToDhall, FromDhall, Show) data Products = P0 | P1 () | P2 Double | P3 () () | P4 Double Double- deriving (Eq, Generic, Inject, Interpret, Show)--deriving instance Interpret ()+ deriving (Eq, Generic, ToDhall, FromDhall, Show) shouldHandleUnionsCorrectly :: TestTree shouldHandleUnionsCorrectly =@@ -257,52 +255,52 @@ `shouldMarshalIntoSmart` P4 1.0 2.0 , N0 True- `shouldInjectInto`+ `shouldEmbedAs` "(< N0 : { _1 : Bool } | N1 : { _1 : Natural } | N2 : { _1 : Text } >).N0 { _1 = True }" , N1 5- `shouldInjectInto`+ `shouldEmbedAs` "(< N0 : { _1 : Bool } | N1 : { _1 : Natural } | N2 : { _1 : Text } >).N1 { _1 = 5 }" , N2 "ABC"- `shouldInjectInto`+ `shouldEmbedAs` "(< N0 : { _1 : Bool } | N1 : { _1 : Natural } | N2 : { _1 : Text } >).N2 { _1 = \"ABC\" }" , N0 True- `shouldInjectIntoSmart`+ `shouldEmbedAsSmart` "(< N0 : Bool | N1 : Natural | N2 : Text >).N0 True" , N1 5- `shouldInjectIntoSmart`+ `shouldEmbedAsSmart` "(< N0 : Bool | N1 : Natural | N2 : Text >).N1 5" , N2 "ABC"- `shouldInjectIntoSmart`+ `shouldEmbedAsSmart` "(< N0 : Bool | N1 : Natural | N2 : Text >).N2 \"ABC\"" - , E0 `shouldInjectInto` "< E0 | E1 | E2 >.E0"- , E1 `shouldInjectInto` "< E0 | E1 | E2 >.E1"- , E2 `shouldInjectInto` "< E0 | E1 | E2 >.E2"+ , E0 `shouldEmbedAs` "< E0 | E1 | E2 >.E0"+ , E1 `shouldEmbedAs` "< E0 | E1 | E2 >.E1"+ , E2 `shouldEmbedAs` "< E0 | E1 | E2 >.E2" - , R0 `shouldInjectInto` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R0"- , R1 { a = () } `shouldInjectInto` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R1 { a = {=} }"- , R2 { x = 1.0 } `shouldInjectInto` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R2 { x = 1.0}"- , R3 { a = (), b = () } `shouldInjectInto` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R3 { a = {=}, b = {=} }"- , R4 { x = 1.0, y = 2.0 } `shouldInjectInto` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R4 { x = 1.0, y = 2.0 }"+ , R0 `shouldEmbedAs` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R0"+ , R1 { a = () } `shouldEmbedAs` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R1 { a = {=} }"+ , R2 { x = 1.0 } `shouldEmbedAs` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R2 { x = 1.0}"+ , R3 { a = (), b = () } `shouldEmbedAs` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R3 { a = {=}, b = {=} }"+ , R4 { x = 1.0, y = 2.0 } `shouldEmbedAs` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R4 { x = 1.0, y = 2.0 }" - , R0 `shouldInjectIntoSmart` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R0"- , R1 { a = () } `shouldInjectIntoSmart` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R1 { a = {=} }"- , R2 { x = 1.0 } `shouldInjectIntoSmart` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R2 { x = 1.0}"- , R3 { a = (), b = () } `shouldInjectIntoSmart` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R3 { a = {=}, b = {=} }"- , R4 { x = 1.0, y = 2.0 } `shouldInjectIntoSmart` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R4 { x = 1.0, y = 2.0 }"+ , R0 `shouldEmbedAsSmart` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R0"+ , R1 { a = () } `shouldEmbedAsSmart` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R1 { a = {=} }"+ , R2 { x = 1.0 } `shouldEmbedAsSmart` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R2 { x = 1.0}"+ , R3 { a = (), b = () } `shouldEmbedAsSmart` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R3 { a = {=}, b = {=} }"+ , R4 { x = 1.0, y = 2.0 } `shouldEmbedAsSmart` "< R0 | R1 : { a : {} } | R2 : { x : Double } | R3 : { a : {}, b : {} } | R4 : { x : Double, y : Double } >.R4 { x = 1.0, y = 2.0 }" - , P0 `shouldInjectInto` "< P0 | P1 : { _1 : {} } | P2 : { _1 : Double } | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P0"- , P1 () `shouldInjectInto` "< P0 | P1 : { _1 : {} } | P2 : { _1 : Double } | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P1 { _1 = {=} }"- , P2 1.0 `shouldInjectInto` "< P0 | P1 : { _1 : {} } | P2 : { _1 : Double } | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P2 { _1 = 1.0 }"- , P3 () () `shouldInjectInto` "< P0 | P1 : { _1 : {} } | P2 : { _1 : Double } | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P3 { _1 = {=}, _2 = {=} }"- , P4 1.0 2.0 `shouldInjectInto` "< P0 | P1 : { _1 : {} } | P2 : { _1 : Double } | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P4 { _1 = 1.0, _2 = 2.0 }"+ , P0 `shouldEmbedAs` "< P0 | P1 : { _1 : {} } | P2 : { _1 : Double } | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P0"+ , P1 () `shouldEmbedAs` "< P0 | P1 : { _1 : {} } | P2 : { _1 : Double } | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P1 { _1 = {=} }"+ , P2 1.0 `shouldEmbedAs` "< P0 | P1 : { _1 : {} } | P2 : { _1 : Double } | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P2 { _1 = 1.0 }"+ , P3 () () `shouldEmbedAs` "< P0 | P1 : { _1 : {} } | P2 : { _1 : Double } | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P3 { _1 = {=}, _2 = {=} }"+ , P4 1.0 2.0 `shouldEmbedAs` "< P0 | P1 : { _1 : {} } | P2 : { _1 : Double } | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P4 { _1 = 1.0, _2 = 2.0 }" - , P0 `shouldInjectIntoSmart` "< P0 | P1 | P2 : Double | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P0"- , P1 () `shouldInjectIntoSmart` "< P0 | P1 | P2 : Double | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P1"- , P2 1.0 `shouldInjectIntoSmart` "< P0 | P1 | P2 : Double | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P2 1.0"- , P3 () () `shouldInjectIntoSmart` "< P0 | P1 | P2 : Double | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P3 { _1 = {=}, _2 = {=} }"- , P4 1.0 2.0 `shouldInjectIntoSmart` "< P0 | P1 | P2 : Double | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P4 { _1 = 1.0, _2 = 2.0 }"+ , P0 `shouldEmbedAsSmart` "< P0 | P1 | P2 : Double | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P0"+ , P1 () `shouldEmbedAsSmart` "< P0 | P1 | P2 : Double | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P1"+ , P2 1.0 `shouldEmbedAsSmart` "< P0 | P1 | P2 : Double | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P2 1.0"+ , P3 () () `shouldEmbedAsSmart` "< P0 | P1 | P2 : Double | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P3 { _1 = {=}, _2 = {=} }"+ , P4 1.0 2.0 `shouldEmbedAsSmart` "< P0 | P1 | P2 : Double | P3 : { _1 : {}, _2 : {} } | P4 : { _1 : Double, _2 : Double } >.P4 { _1 = 1.0, _2 = 2.0 }" ] where smartOptions =@@ -329,14 +327,14 @@ expectedValue @=? actualValue - value `shouldInjectInto` expectedCode = testCase "Inject" $ do+ value `shouldEmbedAs` expectedCode = testCase "ToDhall" $ do parsedExpression <- Dhall.Core.throws (Dhall.Parser.exprFromText "(test)" expectedCode) resolvedExpression <- Dhall.Import.assertNoImports parsedExpression Dhall.Core.denote resolvedExpression @=? Dhall.embed Dhall.inject value - value `shouldInjectIntoSmart` expectedCode = testCase "Inject" $ do+ value `shouldEmbedAsSmart` expectedCode = testCase "ToDhall" $ do parsedExpression <- Dhall.Core.throws (Dhall.Parser.exprFromText "(test)" expectedCode) resolvedExpression <- Dhall.Import.assertNoImports parsedExpression@@ -366,7 +364,7 @@ , "{ _1 = True, _2 = {=} }" `correspondsTo` (True, ()) ] where- correspondsTo :: (Eq a, Interpret a, Show a) => Text -> a -> TestTree+ correspondsTo :: (Eq a, FromDhall a, Show a) => Text -> a -> TestTree dhallCode `correspondsTo` expectedHaskellValue = testCase "Marshall Dhall code to Haskell" $ do actualHaskellValue <- Dhall.input Dhall.auto dhallCode@@ -396,7 +394,7 @@ , "{ _1 = True, _2 = {=} }" `correspondsTo` (True, ()) ] where- correspondsTo :: Inject a => Text -> a -> TestTree+ correspondsTo :: ToDhall a => Text -> a -> TestTree expectedDhallCode `correspondsTo` haskellValue = testCase "Marshall Haskell to Dhall code" $ do let actualDhallCode =
tests/Dhall/Test/Format.hs view
@@ -55,12 +55,17 @@ let doc = Doc.pretty header <> Pretty.prettyCharacterSet characterSet expr+ <> "\n" let docStream = Doc.layoutSmart Pretty.layoutOpts doc let actualText = Doc.Render.Text.renderStrict docStream expectedText <- Text.IO.readFile outputFile let message =- "The formatted expression did not match the expected output"+ "The formatted expression did not match the expected output\n"+ <> "Expected:\n\n" <> Text.unpack expectedText <> "\n\n"+ <> "Actual:\n\n" <> Text.unpack actualText <> "\n\n"+ <> "Expected (show): " <> show expectedText <> "\n"+ <> "Actual (show): " <> show actualText <> "\n" - Tasty.HUnit.assertEqual message expectedText actualText+ Tasty.HUnit.assertBool message (actualText == expectedText)
tests/Dhall/Test/Lint.hs view
@@ -4,8 +4,8 @@ import Data.Monoid (mempty, (<>)) import Data.Text (Text)+import Data.Void (Void) import Dhall.Core (Expr, Import)-import Dhall.TypeCheck (X) import Prelude hiding (FilePath) import Test.Tasty (TestTree) import Turtle (FilePath)@@ -41,14 +41,14 @@ parsedInput <- Core.throws (Parser.exprFromText mempty inputText) - let actualExpression :: Expr X Import+ let actualExpression :: Expr Void Import actualExpression = Core.denote (Lint.lint parsedInput) outputText <- Text.IO.readFile outputFile parsedOutput <- Core.throws (Parser.exprFromText mempty outputText) - let expectedExpression :: Expr X Import+ let expectedExpression :: Expr Void Import expectedExpression = Core.denote parsedOutput let message = "The linted expression did not match the expected output"
tests/Dhall/Test/Main.hs view
@@ -5,6 +5,7 @@ import qualified Dhall.Test.Dhall import qualified Dhall.Test.Diff+import qualified Dhall.Test.Tags import qualified Dhall.Test.Format import qualified Dhall.Test.SemanticHash import qualified Dhall.Test.Import@@ -35,6 +36,8 @@ lintTests <- Dhall.Test.Lint.getTests + tagsTests <- Dhall.Test.Tags.getTests+ diffTests <- Dhall.Test.Diff.getTests semanticHashTests <- Dhall.Test.SemanticHash.getTests@@ -49,6 +52,7 @@ , lintTests , diffTests , semanticHashTests+ , tagsTests , Dhall.Test.Regression.tests , Dhall.Test.Tutorial.tests , Dhall.Test.QuickCheck.tests
tests/Dhall/Test/Normalization.hs view
@@ -4,8 +4,8 @@ import Data.Monoid ((<>)) import Data.Text (Text)+import Data.Void (Void) import Dhall.Core (Expr(..), Var(..), throws)-import Dhall.TypeCheck (X) import Prelude hiding (FilePath) import Test.Tasty (TestTree) import Turtle (FilePath, (</>))@@ -86,7 +86,7 @@ nestedReduction = Tasty.HUnit.testCase "doubleReduction" $ do minType <- Context.insert "min" <$> Test.Util.code "Natural → Natural → Natural" fiveorlessType <- Context.insert "fiveorless" <$> Test.Util.code "Natural → Natural"- wurbleType <- Context.insert "wurble" <$> Test.Util.code "Natural → Integer"+ wurbleType <- Context.insert "wurble" <$> Test.Util.code "Natural → Natural" let tyCtx = minType . fiveorlessType . wurbleType $ Context.empty @@ -126,7 +126,7 @@ expectedResolved <- Import.assertNoImports expectedExpr - let expectedNormalized = Core.denote expectedResolved :: Expr X X+ let expectedNormalized = Core.denote expectedResolved :: Expr Void Void let message = "The normalized expression did not match the expected output"@@ -138,11 +138,11 @@ -} unitTest :: Text -> TestTree unitTest prefix = do- let skip = []+ let expectedFailures = [] let prefixString = Text.unpack prefix - Test.Util.testCase prefix skip $ do+ Test.Util.testCase prefix expectedFailures $ do let actualPath = prefixString <> "A.dhall" let expectedPath = prefixString <> "B.dhall" @@ -155,7 +155,7 @@ let actualNormalized = Core.alphaNormalize- (Core.normalize actualResolved :: Expr X X)+ (Core.normalize actualResolved :: Expr Void Void) expectedExpr <- throws (Parser.exprFromText mempty expectedCode) @@ -183,7 +183,7 @@ let actualNormalized = Core.alphaNormalize- (Core.normalize actualResolved :: Expr X X)+ (Core.normalize actualResolved :: Expr Void Void) expectedExpr <- throws (Parser.exprFromText mempty expectedCode)
tests/Dhall/Test/Parser.hs view
@@ -4,8 +4,8 @@ import Data.Monoid ((<>)) import Data.Text (Text)+import Data.Void (Void) import Dhall.Core (Binding(..), Expr(..), Import, Var(..))-import Dhall.TypeCheck (X) import Prelude hiding (FilePath) import Test.Tasty (TestTree) import Turtle (FilePath, (</>))@@ -121,14 +121,14 @@ shouldParse :: Text -> TestTree shouldParse path = do- let skip =+ let expectedFailures = -- This is a bug created by a parsing performance -- improvement [ parseDirectory </> "success/unit/MergeParenAnnotation" ] let pathString = Text.unpack path - Test.Util.testCase path skip $ do+ Test.Util.testCase path expectedFailures $ do text <- Text.IO.readFile (pathString <> "A.dhall") encoded <- ByteString.Lazy.readFile (pathString <> "B.dhallb")@@ -151,7 +151,7 @@ shouldNotParse :: Text -> TestTree shouldNotParse path = do- let skip =+ let expectedFailures = [ -- These two unexpected successes are due to not correctly -- requiring non-empty whitespace after the `:` in a type -- annotation@@ -180,6 +180,7 @@ -- treats a missing type annotation on an empty list as -- as a type-checking failure instead of a parse failure, -- but this might be fixable.+ , parseDirectory </> "failure/unit/ListLitEmptyMissingAnnotation.dhall" , parseDirectory </> "failure/unit/ListLitEmptyAnnotation.dhall" -- The same performance improvements also broke the@@ -189,7 +190,7 @@ let pathString = Text.unpack path - Test.Util.testCase path skip (do+ Test.Util.testCase path expectedFailures (do bytes <- ByteString.readFile pathString case Text.Encoding.decodeUtf8' bytes of@@ -201,11 +202,11 @@ shouldDecode :: Text -> TestTree shouldDecode pathText = do- let skip = []+ let expectedFailures = [] let pathString = Text.unpack pathText - Test.Util.testCase pathText skip (do+ Test.Util.testCase pathText expectedFailures (do bytes <- ByteString.Lazy.readFile (pathString <> "A.dhallb") term <- Core.throws (Serialise.deserialiseOrFail bytes)@@ -216,7 +217,7 @@ parsedExpression <- Core.throws (Parser.exprFromText mempty text) - let strippedExpression :: Expr X Import+ let strippedExpression :: Expr Void Import strippedExpression = Core.denote parsedExpression let message =@@ -226,15 +227,15 @@ shouldNotDecode :: Text -> TestTree shouldNotDecode pathText = do- let skip = []+ let expectedFailures = [] let pathString = Text.unpack pathText - Test.Util.testCase pathText skip (do+ Test.Util.testCase pathText expectedFailures (do bytes <- ByteString.Lazy.readFile (pathString <> ".dhallb") term <- Core.throws (Serialise.deserialiseOrFail bytes) - case Binary.decodeExpression term :: Either Binary.DecodingFailure (Expr X Import) of+ case Binary.decodeExpression term :: Either Binary.DecodingFailure (Expr Void Import) of Left _ -> return () Right _ -> Tasty.HUnit.assertFailure "Unexpected successful decode" )
tests/Dhall/Test/QuickCheck.hs view
@@ -1,8 +1,10 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fno-warn-orphans #-} @@ -10,12 +12,15 @@ import Codec.Serialise (DeserialiseFailure(..)) import Data.Either (isRight)+import Data.Either.Validation (Validation(..))+import Dhall (ToDhall(..), FromDhall(..), auto, extract, inject, embed, Vector) import Dhall.Map (Map) import Dhall.Core ( Binding(..) , Chunks(..) , Const(..) , Directory(..)+ , DhallDouble(..) , Expr(..) , File(..) , FilePrefix(..)@@ -29,9 +34,11 @@ ) import Data.Functor.Identity (Identity(..))+import Data.Typeable (Typeable, typeRep)+import Data.Proxy (Proxy(..)) import Dhall.Set (Set) import Dhall.Src (Src(..))-import Dhall.TypeCheck (Typer)+import Dhall.TypeCheck (Typer, TypeError) import Generic.Random (Weights, W, (%), (:+)(..)) import Test.QuickCheck (Arbitrary(..), Gen, Positive(..), Property, NonNegative(..), genericShrink, (===), (==>))@@ -45,7 +52,12 @@ import qualified Data.Coerce import qualified Data.List import qualified Data.Sequence+import qualified Data.SpecialValues+import qualified Data.HashSet+import qualified Data.Set import qualified Data.Text as Text+import qualified Data.Map+import qualified Data.HashMap.Strict as HashMap import qualified Dhall.Binary import qualified Dhall.Context import qualified Dhall.Core@@ -54,6 +66,7 @@ import qualified Dhall.Set import qualified Dhall.TypeCheck import qualified Generic.Random+import qualified Numeric.Natural as Nat import qualified Test.QuickCheck import qualified Test.Tasty import qualified Test.Tasty.QuickCheck@@ -161,6 +174,13 @@ shrink = genericShrink +instance Arbitrary DhallDouble where+ arbitrary = fmap DhallDouble (Test.QuickCheck.oneof [ arbitrary, special ])+ where+ special = Test.QuickCheck.elements Data.SpecialValues.specialValues++ shrink = genericShrink+ instance Arbitrary Directory where arbitrary = lift1 Directory @@ -169,7 +189,10 @@ instance (Arbitrary s, Arbitrary a) => Arbitrary (Expr s a) where arbitrary = Test.QuickCheck.suchThat- (Generic.Random.genericArbitraryRecG customGens weights)+ (Generic.Random.withBaseCase+ (Generic.Random.genericArbitraryRecG customGens weights)+ (Var <$> arbitrary)+ ) standardizedExpression where customGens@@ -248,6 +271,7 @@ % (7 :: W "Combine") % (1 :: W "CombineTypes") % (7 :: W "Prefer")+ % (7 :: W "RecordCompletion") % (1 :: W "Merge") % (1 :: W "ToMap") % (7 :: W "Field")@@ -396,6 +420,48 @@ Right importlessExpression -> isRight (Dhall.TypeCheck.typeOf importlessExpression) Left _ -> False +inferredTypesAreNormalized :: Expr () Import -> Property+inferredTypesAreNormalized expression =+ Test.Tasty.QuickCheck.counterexample report (all Dhall.Core.isNormalized result)+ where+ report = "Got: " ++ show result+ ++ "\nExpected: " ++ show (fmap Dhall.Core.normalize result+ :: Either (TypeError () Import) (Expr () Import))++ result = Dhall.TypeCheck.typeWithA filterOutEmbeds Dhall.Context.empty expression++ filterOutEmbeds :: Typer a+ filterOutEmbeds _ = Const Sort -- This could be any ill-typed expression.++normalizingAnExpressionDoesntChangeItsInferredType :: Expr () Import -> Property+normalizingAnExpressionDoesntChangeItsInferredType expression =+ case (eT0, eT1) of+ (Right t0, Right t1) -> t0 === t1+ _ -> Test.QuickCheck.discard+ where+ eT0 = typeCheck expression+ eT1 = typeCheck (Dhall.Core.normalize expression)++ typeCheck = Dhall.TypeCheck.typeWithA filterOutEmbeds Dhall.Context.empty++ filterOutEmbeds :: Typer a+ filterOutEmbeds _ = Const Sort -- This could be any ill-typed expression.++embedThenExtractIsIdentity+ :: forall a. (ToDhall a, FromDhall a, Eq a, Typeable a, Arbitrary a, Show a)+ => Proxy a+ -> (String, Property, QuickCheckTests)+embedThenExtractIsIdentity p =+ ( "Embedding then extracting is identity for " ++ show (typeRep p)+ , Test.QuickCheck.property (prop :: a -> Bool)+ , QuickCheckTests 1000+ )+ where+ prop a = case extract auto (embed inject a) of+ Success a' -> a == a'+ Failure _ -> False++ tests :: TestTree tests = testProperties'@@ -420,7 +486,27 @@ , Test.QuickCheck.property isSameAsSelf , QuickCheckTests 10000 )+ , ( "Inferred types should be normalized"+ , Test.QuickCheck.property inferredTypesAreNormalized+ , QuickCheckTests 10000+ )+ , ( "Normalizing an expression doesn't change its inferred type"+ , Test.QuickCheck.property normalizingAnExpressionDoesntChangeItsInferredType+ , QuickCheckTests 10000+ )+ , embedThenExtractIsIdentity (Proxy :: Proxy (Text.Text))+ , embedThenExtractIsIdentity (Proxy :: Proxy [Nat.Natural])+ , embedThenExtractIsIdentity (Proxy :: Proxy (Bool, Double))+ , embedThenExtractIsIdentity (Proxy :: Proxy (Data.Sequence.Seq ()))+ , embedThenExtractIsIdentity (Proxy :: Proxy (Maybe Integer))+ , embedThenExtractIsIdentity (Proxy :: Proxy (Data.Set.Set Nat.Natural))+ , embedThenExtractIsIdentity (Proxy :: Proxy (Data.HashSet.HashSet Text.Text))+ , embedThenExtractIsIdentity (Proxy :: Proxy (Vector Double))+ , embedThenExtractIsIdentity (Proxy :: Proxy (Data.Map.Map Double Bool))+ , embedThenExtractIsIdentity (Proxy :: Proxy (HashMap.HashMap Double Bool)) ]++ testProperties' :: String -> [(String, Property, QuickCheckTests)] -> TestTree testProperties' name = Test.Tasty.testGroup name . map f
tests/Dhall/Test/Regression.hs view
@@ -22,9 +22,10 @@ import qualified Test.Tasty import qualified Test.Tasty.HUnit +import Data.Void (Void) import Dhall.Import (Imported, MissingImports(..)) import Dhall.Parser (Src, SourcedException(..))-import Dhall.TypeCheck (TypeError, X)+import Dhall.TypeCheck (TypeError) import Test.Tasty (TestTree) import Test.Tasty.HUnit ((@?=)) @@ -40,6 +41,7 @@ , issue253 , issue1131a , issue1131b+ , issue1341 , parsing0 , typeChecking0 , typeChecking1@@ -49,7 +51,7 @@ ] data Foo = Foo Integer Bool | Bar Bool Bool Bool | Baz Integer Integer- deriving (Show, Dhall.Generic, Dhall.Interpret, Dhall.Inject)+ deriving (Show, Dhall.Generic, Dhall.FromDhall, Dhall.ToDhall) unnamedFields :: TestTree unnamedFields = Test.Tasty.HUnit.testCase "Unnamed Fields" (do@@ -68,7 +70,7 @@ ) let inj = Dhall.inject :: Dhall.InputType Foo- Test.Tasty.HUnit.assertEqual "Good Inject" (Dhall.declared inj) (Dhall.expected ty)+ Test.Tasty.HUnit.assertEqual "Good ToDhall" (Dhall.declared inj) (Dhall.expected ty) let tu_ty = Dhall.auto :: Dhall.Type (Integer, Bool) Test.Tasty.HUnit.assertEqual "Auto Tuple" (Dhall.expected tu_ty) (Dhall.Core.Record (@@ -95,7 +97,7 @@ let shouldNotTypeCheck text = do let handler :: SourcedException MissingImports -> IO Bool handler (SourcedException _ (MissingImports [e])) =- case Control.Exception.fromException e :: Maybe (Imported (TypeError Src X)) of+ case Control.Exception.fromException e :: Maybe (Imported (TypeError Src Void)) of Just _ -> return True Nothing -> return False handler _ = do@@ -170,6 +172,13 @@ issue1131b :: TestTree issue1131b = Test.Tasty.HUnit.testCase "Issue #1131 b" (Util.assertDoesntTypeCheck "toMap {=} : List {mapKey : Text, mapValue : 0}")++issue1341 :: TestTree+issue1341 = Test.Tasty.HUnit.testCase "Issue #1341" (do+ let nan = Dhall.Core.DoubleLit (Dhall.Core.DhallDouble (0/0)) :: Dhall.Core.Expr () ()+ let actual = Dhall.Core.V "x" 0 `Dhall.Core.freeIn` nan+ let msg = "NaN shouldn't contain any free variables"+ Test.Tasty.HUnit.assertEqual msg False actual) parsing0 :: TestTree parsing0 = Test.Tasty.HUnit.testCase "Parsing regression #0" (do
+ tests/Dhall/Test/Tags.hs view
@@ -0,0 +1,63 @@+{-# LANGUAGE OverloadedStrings #-}++module Dhall.Test.Tags where++import Data.Monoid ((<>))+import Data.Text (Text)+import Dhall.Util (Input(..))+import Prelude hiding (FilePath)+import Test.Tasty (TestTree)+import Turtle (FilePath)++import qualified Data.Text as Text+import qualified Data.Text.IO as Text.IO+import qualified Dhall.Tags as Tags+import qualified Dhall.Test.Util as Test.Util+import qualified Test.Tasty as Tasty+import qualified Test.Tasty.HUnit as Tasty.HUnit+import qualified Turtle++tagsDirectory :: FilePath+tagsDirectory = "./tests/tags"++getTests :: IO TestTree+getTests = do+ tagsTests <- Test.Util.discover (Turtle.chars <* ".dhall") tagsTest (Turtle.lstree tagsDirectory)++ let testTree = Tasty.testGroup "tags tests" [ tagsTests, tagsDirTest ]++ return testTree++tagsTest :: Text -> TestTree+tagsTest prefix =+ Tasty.HUnit.testCase (Text.unpack prefix) $ do+ let inputFile = Text.unpack (prefix <> ".dhall")+ let outputFile = Text.unpack (prefix <> ".tags")++ actualTags <- fixPathSeparators <$> Tags.generate (InputFile inputFile) Nothing False++ expectedTags <- Text.IO.readFile outputFile++ let message = "The actual tags did not match the expected tags"++ Tasty.HUnit.assertEqual message expectedTags actualTags++tagsDirTest :: TestTree+tagsDirTest =+ Tasty.HUnit.testCase "all" $ do+ let outputFile = Text.unpack . Turtle.format Turtle.fp $ tagsDirectory Turtle.</> "all.tags"++ actualTags <- fmap fixPathSeparators+ (Tags.generate+ (InputFile (Text.unpack . Turtle.format Turtle.fp $ tagsDirectory))+ (Just [".dhall"])+ False)++ expectedTags <- Text.IO.readFile outputFile++ let message = "The actual tags did not match the expected tags for directory test"++ Tasty.HUnit.assertEqual message expectedTags actualTags++fixPathSeparators :: Text -> Text+fixPathSeparators = Text.replace "\\" "/"
tests/Dhall/Test/Tutorial.hs view
@@ -12,7 +12,7 @@ import Data.Monoid ((<>)) import Data.Text (Text)-import Dhall (Inject)+import Dhall (ToDhall) import GHC.Generics (Generic) import Numeric.Natural (Natural) import Test.Tasty (TestTree)@@ -69,7 +69,7 @@ makeBools True False @?= False ) data Example0 = Example0 { foo :: Bool, bar :: Bool }- deriving (Generic, Inject)+ deriving (Generic, ToDhall) _Functions_2 :: TestTree _Functions_2 = Test.Tasty.HUnit.testCase "Example #2" (do
tests/Dhall/Test/TypeInference.hs view
@@ -44,14 +44,9 @@ successTest :: Text -> TestTree successTest prefix = do- let skip = [ -- We correctly infer the expected type @NaN ≡ NaN@ here,- -- but the comparison between the inferred and the expected type- -- fails due to `Expr`'s 'Eq' instance, which inherits the- -- @NaN /= NaN@ inequality from 'Double'.- typeInferenceDirectory </> "success/unit/AssertNaN"- ]+ let expectedFailures = [] - Test.Util.testCase prefix skip $ do+ Test.Util.testCase prefix expectedFailures $ do let prefixFP = Text.unpack prefix actualCode <- Text.IO.readFile (prefixFP <> "A.dhall")@@ -85,17 +80,18 @@ failureTest :: Text -> TestTree failureTest prefix = do- let skip = [ typeInferenceDirectory </> "failure/unit/MergeEmptyNeedsDirectAnnotation1"+ let expectedFailures = [ typeInferenceDirectory </> "failure/unit/MergeEmptyNeedsDirectAnnotation1" -- Duplicate fields are incorrectly caught during parsing: -- https://github.com/dhall-lang/dhall-haskell/issues/772 , typeInferenceDirectory </> "failure/unit/RecordLitDuplicateFields"+ , typeInferenceDirectory </> "failure/unit/RecordProjectionDuplicateFields" , typeInferenceDirectory </> "failure/unit/RecordTypeDuplicateFields" , typeInferenceDirectory </> "failure/unit/UnionTypeDuplicateVariants1" , typeInferenceDirectory </> "failure/unit/UnionTypeDuplicateVariants2" ] - Test.Util.testCase prefix skip $ do+ Test.Util.testCase prefix expectedFailures $ do let prefixFP = Text.unpack prefix code <- Text.IO.readFile (prefixFP <> ".dhall")@@ -104,16 +100,8 @@ Left _ -> Tasty.HUnit.assertFailure (prefixFP <> " should have parsed") Right e -> return e - tryResolvedExpr <-- Exception.try- (Test.Util.loadRelativeTo- (FilePath.takeDirectory prefixFP)- Import.IgnoreSemanticCache- expression)-- case tryResolvedExpr of- Left (SomeException _) -> return ()+ resolved <- Import.assertNoImports expression - Right resolved -> case TypeCheck.typeOf resolved of- Left _ -> return ()- Right _ -> Tasty.HUnit.assertFailure (prefixFP <> " should not have type-checked")+ case TypeCheck.typeOf resolved of+ Left _ -> return ()+ Right _ -> Tasty.HUnit.assertFailure (prefixFP <> " should not have type-checked")
tests/Dhall/Test/Util.hs view
@@ -24,12 +24,12 @@ import Control.Monad.Trans.State.Strict (StateT) import Data.Bifunctor (first) import Data.Text (Text)+import Data.Void (Void) import Dhall.Context (Context) import Dhall.Core (Expr, Normalizer, ReifiedNormalizer(..), Import) import Dhall.Import (Status(..), SemanticCacheMode(..)) import Data.Monoid((<>)) import Dhall.Parser (Src)-import Dhall.TypeCheck (X) import Prelude hiding (FilePath) import Test.Tasty.HUnit import Test.Tasty (TestTree)@@ -60,17 +60,17 @@ import qualified Data.Text.IO #endif -normalize' :: Expr Src X -> Text+normalize' :: Expr Src Void -> Text normalize' = Dhall.Core.pretty . Dhall.Core.normalize -normalizeWith' :: Normalizer X -> Expr Src X -> Text+normalizeWith' :: Normalizer Void -> Expr Src Void -> Text normalizeWith' ctx t = Dhall.Core.pretty (Dhall.Core.normalizeWith (Just (ReifiedNormalizer ctx)) t) -code :: Text -> IO (Expr Src X)+code :: Text -> IO (Expr Src Void) code = codeWith Dhall.Context.empty -codeWith :: Context (Expr Src X) -> Text -> IO (Expr Src X)+codeWith :: Context (Expr Src Void) -> Text -> IO (Expr Src Void) codeWith ctx expr = do expr0 <- case Dhall.Parser.exprFromText mempty expr of Left parseError -> Control.Exception.throwIO parseError@@ -81,21 +81,21 @@ Right _ -> return () return expr1 -load :: Expr Src Import -> IO (Expr Src X)+load :: Expr Src Import -> IO (Expr Src Void) load = loadRelativeTo "." UseSemanticCache -loadRelativeTo :: FilePath.FilePath -> SemanticCacheMode -> Expr Src Import -> IO (Expr Src X)+loadRelativeTo :: FilePath.FilePath -> SemanticCacheMode -> Expr Src Import -> IO (Expr Src Void) loadRelativeTo rootDirectory semanticCacheMode expression = State.evalStateT (loadWith expression) (Dhall.Import.emptyStatus rootDirectory) { _semanticCacheMode = semanticCacheMode } #ifdef WITH_HTTP-loadWith :: Expr Src Import -> StateT Status IO (Expr Src X)+loadWith :: Expr Src Import -> StateT Status IO (Expr Src Void) loadWith = Dhall.Import.loadWith #else-loadWith :: Expr Src Import -> StateT Status IO (Expr Src X)+loadWith :: Expr Src Import -> StateT Status IO (Expr Src Void) loadWith expr = do let mockRemote' url = do liftIO . putStrLn $ "\nTesting without real HTTP support --"@@ -155,23 +155,23 @@ equivalent :: Text -> Text -> IO () equivalent text0 text1 = do- expr0 <- fmap Dhall.Core.normalize (code text0) :: IO (Expr X X)- expr1 <- fmap Dhall.Core.normalize (code text1) :: IO (Expr X X)+ expr0 <- fmap Dhall.Core.normalize (code text0) :: IO (Expr Void Void)+ expr1 <- fmap Dhall.Core.normalize (code text1) :: IO (Expr Void Void) assertEqual "Expressions are not equivalent" expr0 expr1 -assertNormalizesTo :: Expr Src X -> Text -> IO ()+assertNormalizesTo :: Expr Src Void -> Text -> IO () assertNormalizesTo e expected = do assertBool msg (not $ Dhall.Core.isNormalized e) normalize' e @?= expected where msg = "Given expression is already in normal form" -assertNormalizesToWith :: Normalizer X -> Expr Src X -> Text -> IO ()+assertNormalizesToWith :: Normalizer Void -> Expr Src Void -> Text -> IO () assertNormalizesToWith ctx e expected = do assertBool msg (not $ Dhall.Core.isNormalizedWith ctx (first (const ()) e)) normalizeWith' ctx e @?= expected where msg = "Given expression is already in normal form" -assertNormalized :: Expr Src X -> IO ()+assertNormalized :: Expr Src Void -> IO () assertNormalized e = do assertBool msg1 (Dhall.Core.isNormalized e) assertEqual msg2 (normalize' e) (Dhall.Core.pretty e)@@ -210,8 +210,8 @@ return (Tasty.testGroup "discover" tests) testCase :: Text -> [ FilePath ] -> Assertion -> TestTree-testCase prefix skip assertion =- if prefix `elem` map (Turtle.format fp) skip+testCase prefix expectedFailures assertion =+ if prefix `elem` map (Turtle.format fp) expectedFailures then Tasty.ExpectedFailure.expectFail test else test where
+ tests/format/applicationMultilineA.dhall view
@@ -0,0 +1,30 @@+{ app =+ ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff+ aaaaaaaaaaaaaaaaa+, app2 =+ f+ a+ b+ cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc+, app3 =+ f+ a+ b+ c+ dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd+, some =+ Some+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+, merge =+ merge+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb+, merge3 =+ merge+ a+ b+ ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc+, toMap =+ toMap+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+}
+ tests/format/applicationMultilineB.dhall view
@@ -0,0 +1,30 @@+{ app =+ ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff+ aaaaaaaaaaaaaaaaa+, app2 =+ f+ a+ b+ cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc+, app3 =+ f+ a+ b+ c+ dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd+, some =+ Some+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+, merge =+ merge+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb+, merge3 =+ merge+ a+ b+ ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc+, toMap =+ toMap+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+}
tests/format/asciiB.dhall view
+ tests/format/concatSepA.dhall view
@@ -0,0 +1,30 @@+{-+Concatenate a `List` of `Text` values with a separator in between each value+-}+let Status = < Empty | NonEmpty : Text >++let concatSep+ : ∀(separator : Text) → ∀(elements : List Text) → Text+ = λ(separator : Text)+ → λ(elements : List Text)+ → let status =+ List/fold Text elements Status+ ( λ(element : Text)+ → λ(status : Status)+ → merge+ { Empty = Status.NonEmpty element+ , NonEmpty =+ λ(result : Text)+ → Status.NonEmpty (element ++ separator ++ result)+ }+ status+ )+ Status.Empty+ + in merge { Empty = "", NonEmpty = λ(result : Text) → result } status++let example0 = assert : concatSep ", " [ "ABC", "DEF", "GHI" ] ≡ "ABC, DEF, GHI"++let example1 = assert : concatSep ", " ([] : List Text) ≡ ""++in concatSep
+ tests/format/concatSepB.dhall view
@@ -0,0 +1,33 @@+{-+Concatenate a `List` of `Text` values with a separator in between each value+-}+let Status = < Empty | NonEmpty : Text >++let concatSep+ : ∀(separator : Text) → ∀(elements : List Text) → Text+ = λ(separator : Text)+ → λ(elements : List Text)+ → let status =+ List/fold+ Text+ elements+ Status+ ( λ(element : Text)+ → λ(status : Status)+ → merge+ { Empty = Status.NonEmpty element+ , NonEmpty =+ λ(result : Text)+ → Status.NonEmpty (element ++ separator ++ result)+ }+ status+ )+ Status.Empty+ + in merge { Empty = "", NonEmpty = λ(result : Text) → result } status++let example0 = assert : concatSep ", " [ "ABC", "DEF", "GHI" ] ≡ "ABC, DEF, GHI"++let example1 = assert : concatSep ", " ([] : List Text) ≡ ""++in concatSep
tests/format/doubleRoundB.dhall view
tests/format/emptyRecordB.dhall view
tests/format/escapeNumericLabelB.dhall view
tests/format/escapeSingleQuotedOpenInterpolationB.dhall view
tests/format/fieldOrderB.dhall view
+ tests/format/functionLikeA.dhall view
@@ -0,0 +1,6 @@+{ example0 = merge a b + merge a b+, example1 = (merge a b : T) + (merge a b : T)+, example2 = toMap a + toMap a+, example3 = (toMap a b : T) + (toMap a b : T)+, example4 = Some a + Some a+}
+ tests/format/functionLikeB.dhall view
@@ -0,0 +1,6 @@+{ example0 = merge a b + merge a b+, example1 = (merge a b : T) + (merge a b : T)+, example2 = toMap a + toMap a+, example3 = (toMap a b : T) + (toMap a b : T)+, example4 = Some a + Some a+}
tests/format/headerB.dhall view
tests/format/ifThenElseB.dhall view
tests/format/importAccessB.dhall view
tests/format/importLines2A.dhall view
tests/format/importLines2B.dhall view
tests/format/importLinesB.dhall view
tests/format/importSuffixB.dhall view
tests/format/innerMultilineB.dhall view
tests/format/issue1301B.dhall view
+ tests/format/issue1413A.dhall view
@@ -0,0 +1,6 @@+let foo =++ {- test -}+ "hello"++in foo
+ tests/format/issue1413B.dhall view
@@ -0,0 +1,6 @@+let foo =+ + {- test -}+ "hello"++in foo
tests/format/largeExponentB.dhall view
tests/format/largeRecordB.dhall view
@@ -34,15 +34,15 @@ , dark_room = Some False , disclose = Some- ( defaults.Disclose- ⫽ { inventory = Some { prompt = True, default = True }- , attributes = Some { prompt = True, default = False }- , monsters_killed = Some { prompt = False, default = True }- , monsters_genocided = Some { prompt = False, default = False }- , conduct = Some { prompt = False, default = False }- , dungeon_overview = Some { prompt = False, default = False }- }- )+ ( defaults.Disclose+ ⫽ { inventory = Some { prompt = True, default = True }+ , attributes = Some { prompt = True, default = False }+ , monsters_killed = Some { prompt = False, default = True }+ , monsters_genocided = Some { prompt = False, default = False }+ , conduct = Some { prompt = False, default = False }+ , dungeon_overview = Some { prompt = False, default = False }+ }+ ) , dogname = Some "Cujo" , extmenu = Some False , fixinv = Some True
tests/format/letB.dhall view
tests/format/letCommentsB.dhall view
+ tests/format/letLineCommentsAfterVariableA.dhall view
@@ -0,0 +1,10 @@+let x -- xxxxxxxxxx xxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxx+ : Natural+ = 0++let y -- yyyyyyyyyy yyyyyyyyyyyyyyyyy yyyyyyyyyyyyyyyyy+ -- yyyyyyyyyyyyyy yyyyyyyyyyyyyyyyyyyyyyy+ : Natural+ = 1++in x + y
+ tests/format/letLineCommentsAfterVariableB.dhall view
@@ -0,0 +1,12 @@+let x+ -- xxxxxxxxxx xxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxx+ : Natural+ = 0++let y+ -- yyyyyyyyyy yyyyyyyyyyyyyyyyy yyyyyyyyyyyyyyyyy+ -- yyyyyyyyyyyyyy yyyyyyyyyyyyyyyyyyyyyyy+ : Natural+ = 1++in x + y
tests/format/letLongCommentsB.dhall view
tests/format/letNewlineCommentsB.dhall view
tests/format/letShortCommentsB.dhall view
@@ -1,1 +1,7 @@-let x = True let {- 0 -} y {- 1 -} : {- 2 -} Bool = {- 3-} False in x+let x = True++let {- 0 -} y+ {- 1 -} : {- 2 -} Bool+ = {- 3-} False++in x
tests/format/multilineB.dhall view
tests/format/parentB.dhall view
tests/format/projectionOrderB.dhall view
+ tests/format/recordCompletionA.dhall view
@@ -0,0 +1,25 @@+{ field0 =+T::{+, a = 1+, b = 1+, c = 1+, d = 1+, e = 1+, f = 1+, g = 1+, h = 1+, i = 1+, j = 1+, k = 1+, l = 1+, m = 1+, n = 1+, o = 1+, p = 1+, q = 1+, r = 1+, s = 1+, t = 1+}+, field1 = T::{ a = 1 }+}
+ tests/format/recordCompletionB.dhall view
@@ -0,0 +1,25 @@+{ field0 =+ T::{+ , a = 1+ , b = 1+ , c = 1+ , d = 1+ , e = 1+ , f = 1+ , g = 1+ , h = 1+ , i = 1+ , j = 1+ , k = 1+ , l = 1+ , m = 1+ , n = 1+ , o = 1+ , p = 1+ , q = 1+ , r = 1+ , s = 1+ , t = 1+ }+, field1 = T::{ a = 1 }+}
tests/format/sha256PrintingB.dhall view
tests/format/unicodeB.dhall view
tests/format/urlsB.dhall view
+ tests/lint/success/fixAssertA.dhall view
@@ -0,0 +1,1 @@+let example = 1 ≡ 1 in 1
+ tests/lint/success/fixAssertB.dhall view
@@ -0,0 +1,1 @@+let example = assert : 1 ≡ 1 in 1
+ tests/tags/all.tags view
@@ -0,0 +1,61 @@++./tests/tags/let.dhall,66+let.dhall1,1+/let.dhall1,1+let foo foo1,5+let bar bar3,22++./tests/tags/nonunicode.dhall,63+nonunicode.dhall1,1+/nonunicode.dhall1,1+let foo foo2,12++./tests/tags/record.dhall,335+record.dhall1,1+/record.dhall1,1+let Config Config6,81+ { home home8,161+ , privateKey privateKey9,181+ , publicKey publicKey10,207+ , name name11,232+ , name : { firstName firstName11,241+ , secondName secondName12,275+let makeUser makeUser16,323+let configs configs30,804++./tests/tags/recordlit.dhall,223+recordlit.dhall1,1+/recordlit.dhall1,1+{ home home8,143+, privateKey privateKey9,171+, publicKey publicKey10,215+, name name11,263+, name = { firstName firstName11,272+ , secondName secondName12,302++./tests/tags/simple.dhall,254+simple.dhall1,1+/simple.dhall1,1+let generate generate6,100+let makeUser makeUser17,409+let buildUser buildUser26,680+let Config Config32,880+ { home home33,897+ , privateKey privateKey34,917+ , publicKey publicKey35,943++./tests/tags/unicode.dhall,57+unicode.dhall1,1+/unicode.dhall1,1+let foo foo2,15++./tests/tags/union.dhall,394+union.dhall1,1+/union.dhall1,1+let Element Element1,5+let Element = < Left Left1,17+let Element = < Left : Natural | Right Right1,34+let Element = < Left : Natural | Right : Bool | Middle Middle1,49+let Element = < Left : Natural | Right : Bool | Middle : < Top Top1,60+let Element = < Left : Natural | Right : Bool | Middle : < Top : Text | Bottom Bottom1,73+let foo foo3,95
+ tests/tags/let.dhall view
@@ -0,0 +1,4 @@+let foo = "foo"++let bar = foo+in bar
+ tests/tags/let.tags view
@@ -0,0 +1,6 @@++./tests/tags/let.dhall,66+let.dhall1,1+/let.dhall1,1+let foo foo1,5+let bar bar3,22
+ tests/tags/nonunicode.dhall view
@@ -0,0 +1,3 @@+-- abc+let foo = "bar"+in foo
+ tests/tags/nonunicode.tags view
@@ -0,0 +1,5 @@++./tests/tags/nonunicode.dhall,63+nonunicode.dhall1,1+/nonunicode.dhall1,1+let foo foo2,12
+ tests/tags/record.dhall view
@@ -0,0 +1,35 @@+{- You can optionally add types++ `x : T` means that `x` has type `T`+-}++let Config : Type =+ {- What happens if you add another field here? -}+ { home : Text+ , privateKey : Text+ , publicKey : Text+ , name : { firstName : Text+ , secondName : Text+ }+ }++let makeUser : Text -> Config = \(user : Text) ->+ let home : Text = "/home/${user}"+ let privateKey : Text = "${home}/.ssh/id_ed25519"+ let publicKey : Text = "${privateKey}.pub"+ let config : Config =+ { home = home+ , privateKey = privateKey+ , publicKey = publicKey+ , name = { firstName = user+ , secondName = ""+ }+ }+ in config++let configs : List Config =+ [ makeUser "bill"+ , makeUser "jane"+ ]++in configs
+ tests/tags/record.tags view
@@ -0,0 +1,13 @@++./tests/tags/record.dhall,335+record.dhall1,1+/record.dhall1,1+let Config Config6,81+ { home home8,161+ , privateKey privateKey9,181+ , publicKey publicKey10,207+ , name name11,232+ , name : { firstName firstName11,241+ , secondName secondName12,275+let makeUser makeUser16,323+let configs configs30,804
+ tests/tags/recordlit.dhall view
@@ -0,0 +1,14 @@+{- This is an example Dhall configuration file++ Can you spot the mistake?++ Fix the typo, then move onto the "Definitions" example+-}++{ home = "/home/bill"+, privateKey = "/home/bill/.ssh/id_ed25519"+, publicKey = "/home/blil/.ssh/id_ed25519.pub"+, name = { firstName = "Bill"+ , secondName = "Bar"+ }+}
+ tests/tags/recordlit.tags view
@@ -0,0 +1,10 @@++./tests/tags/recordlit.dhall,223+recordlit.dhall1,1+/recordlit.dhall1,1+{ home home8,143+, privateKey privateKey9,171+, publicKey publicKey10,215+, name name11,263+, name = { firstName firstName11,272+ , secondName secondName12,302
+ tests/tags/simple.dhall view
@@ -0,0 +1,39 @@+{- Need to generate a lot of users?++ Use the `generate` function from the Dhall Prelude+-}++let generate = https://prelude.dhall-lang.org/List/generate++{- You can import Dhall expressions from URLs that support+ CORS++ The command-line tools also let you import from files,+ environment variables, and URLs without CORS support.++ Browse https://prelude.dhall-lang.org for more utilities+-}++let makeUser = \(user : Text) ->+ let home = "/home/${user}"+ let privateKey = "${home}/.ssh/id_ed25519"+ let publicKey = "${privateKey}.pub"+ in { home = home+ , privateKey = privateKey+ , publicKey = publicKey+ }++let buildUser = \(index : Natural) ->+ {- `Natural/show` is a "built-in", meaning that+ you can use `Natural/show` without an import+ -}+ makeUser "build${Natural/show index}"++let Config =+ { home : Text+ , privateKey : Text+ , publicKey : Text+ }++in {- Try generating 20 users instead of 10 -}+ generate 10 Config buildUser
+ tests/tags/simple.tags view
@@ -0,0 +1,11 @@++./tests/tags/simple.dhall,254+simple.dhall1,1+/simple.dhall1,1+let generate generate6,100+let makeUser makeUser17,409+let buildUser buildUser26,680+let Config Config32,880+ { home home33,897+ , privateKey privateKey34,917+ , publicKey publicKey35,943
+ tests/tags/unicode.dhall view
@@ -0,0 +1,3 @@+-- αβξ+let foo = "bar"+in foo
+ tests/tags/unicode.tags view
@@ -0,0 +1,5 @@++./tests/tags/unicode.dhall,57+unicode.dhall1,1+/unicode.dhall1,1+let foo foo2,15
+ tests/tags/union.dhall view
@@ -0,0 +1,4 @@+let Element = < Left : Natural | Right : Bool | Middle : < Top : Text | Bottom : Bool> >++let foo = Element.Left 6+in foo
+ tests/tags/union.tags view
@@ -0,0 +1,11 @@++./tests/tags/union.dhall,394+union.dhall1,1+/union.dhall1,1+let Element Element1,5+let Element = < Left Left1,17+let Element = < Left : Natural | Right Right1,34+let Element = < Left : Natural | Right : Bool | Middle Middle1,49+let Element = < Left : Natural | Right : Bool | Middle : < Top Top1,60+let Element = < Left : Natural | Right : Bool | Middle : < Top : Text | Bottom Bottom1,73+let foo foo3,95