dhall 1.4.2 → 1.5.0
raw patch · 12 files changed
+4177/−3884 lines, 12 filesdep +contravariantdep −neat-interpolationPVP ok
version bump matches the API change (PVP)
Dependencies added: contravariant
Dependencies removed: neat-interpolation
API changes (from Hackage documentation)
- Dhall: text :: Type Text
+ Dhall: Type :: (Expr Src X -> Maybe a) -> Expr Src X -> Type a
+ Dhall: [expected] :: Type a -> Expr Src X
+ Dhall: [extract] :: Type a -> Expr Src X -> Maybe a
+ Dhall: class Inject a where injectWith options = contramap from (genericInjectWith options)
+ Dhall: inject :: Inject a => InputType a
+ Dhall: injectWith :: (Inject a, Generic a, GenericInject (Rep a)) => InterpretOptions -> InputType a
+ Dhall: instance (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 (Dhall.GenericInject f, Dhall.GenericInject g) => Dhall.GenericInject (f GHC.Generics.:*: g)
+ Dhall: instance (Dhall.Inject a, Dhall.Interpret b) => Dhall.Interpret (a -> b)
+ Dhall: instance (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 (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 (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 (GHC.Generics.Selector s, Dhall.Inject a) => Dhall.GenericInject (GHC.Generics.M1 GHC.Generics.S s (GHC.Generics.K1 i a))
+ Dhall: instance Data.Functor.Contravariant.Contravariant Dhall.InputType
+ Dhall: instance Dhall.GenericInject GHC.Generics.U1
+ Dhall: instance Dhall.GenericInject f => Dhall.GenericInject (GHC.Generics.M1 GHC.Generics.C c f)
+ Dhall: instance Dhall.GenericInject f => Dhall.GenericInject (GHC.Generics.M1 GHC.Generics.D d f)
+ Dhall: instance Dhall.Inject Data.Text.Internal.Lazy.Text
+ Dhall: instance Dhall.Inject Data.Text.Internal.Text
+ 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 a => Dhall.Inject (Data.Vector.Vector a)
+ Dhall: instance Dhall.Inject a => Dhall.Inject (GHC.Base.Maybe a)
+ Dhall: instance Dhall.Interpret Data.Text.Internal.Text
+ Dhall: lazyText :: Type Text
+ Dhall: rawInput :: Alternative f => Type a -> Expr s X -> f a
+ Dhall: strictText :: Type Text
+ Dhall.Core: ListAppend :: (Expr s a) -> (Expr s a) -> Expr s a
+ Dhall.Core: isNormalizedWith :: (Eq s, Eq a) => Normalizer a -> Expr s a -> Bool
+ Dhall.Core: normalizeWith :: Normalizer a -> Expr s a -> Expr t a
+ Dhall.Core: type Normalizer a = forall s. Expr s a -> Maybe (Expr s a)
+ Dhall.Import: loadWith :: Context (Expr Src X) -> Expr Src Path -> IO (Expr Src X)
+ Dhall.TypeCheck: CantListAppend :: (Expr s X) -> (Expr s X) -> TypeMessage s
+ Dhall.TypeCheck: ListAppendMismatch :: (Expr s X) -> (Expr s X) -> TypeMessage s
Files
- CHANGELOG.md +15/−0
- dhall.cabal +2/−3
- src/Dhall.hs +298/−18
- src/Dhall/Core.hs +182/−110
- src/Dhall/Import.hs +31/−22
- src/Dhall/Parser.hs +7/−6
- src/Dhall/Tutorial.hs +84/−6
- src/Dhall/TypeCheck.hs +3131/−3130
- tests/Examples.hs +269/−501
- tests/Normalization.hs +91/−68
- tests/Tutorial.hs +44/−15
- tests/Util.hs +23/−5
CHANGELOG.md view
@@ -1,3 +1,18 @@+1.5.0++* BREAKING CHANGE: Add list concatenation operator: `(#)`+ * This is a breaking change because it adds a new constructor to the `Expr`+ type which breaks exhaustive pattern matches+* BREAKING CHANGE: Add `Interpret` support for lazy `Text`+ * This is a breaking change because it renames `text` to `strictText`+* Add `Interpret` instance for decoding (a limited subset of) Dhall functions+* Dhall no longer requires Template Haskell to compile+ * This helps with cross-compilation+* Add `rawInput` utility for decoding a Haskell value from the `Expr` type+* Add `loadWith`/`normalizeWith` utilities for normalizing/importing modules+ with a custom context+* Export `Type` constructor+ 1.4.2 * Fix missing `Prelude` files in package archive uploaded to Hackage
dhall.cabal view
@@ -1,5 +1,5 @@ Name: dhall-Version: 1.4.2+Version: 1.5.0 Cabal-Version: >=1.8.0.2 Build-Type: Simple Tested-With: GHC == 7.10.2, GHC == 8.0.1@@ -94,10 +94,10 @@ case-insensitive < 1.3 , charset < 0.4 , containers >= 0.5.0.0 && < 0.6 ,+ contravariant < 1.5 , http-client >= 0.4.30 && < 0.6 , http-client-tls >= 0.2.0 && < 0.4 , lens >= 2.4 && < 4.16,- neat-interpolation >= 0.3.2.1 && < 0.4 , parsers >= 0.12.4 && < 0.13, system-filepath >= 0.3.1 && < 0.5 , system-fileio >= 0.2.1 && < 0.4 ,@@ -143,7 +143,6 @@ Build-Depends: base >= 4 && < 5 , dhall ,- neat-interpolation >= 0.3.2.1 && < 0.4 , tasty >= 0.11.2 && < 0.12, tasty-hunit >= 0.9.2 && < 0.10, text >= 0.11.1.0 && < 1.3 ,
src/Dhall.hs view
@@ -3,7 +3,6 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeOperators #-}@@ -19,7 +18,7 @@ , detailed -- * Types- , Type+ , Type(..) , Interpret(..) , InvalidType(..) , auto@@ -29,11 +28,18 @@ , natural , integer , double- , text+ , lazyText+ , strictText , maybe , vector , GenericInterpret(..) + , Inject(..)+ , inject++ -- * Miscellaneous+ , rawInput+ -- * Re-exports , Natural , Text@@ -41,8 +47,9 @@ , Generic ) where -import Control.Applicative (empty, liftA2, (<|>))+import Control.Applicative (empty, liftA2, (<|>), Alternative) import Control.Exception (Exception)+import Data.Functor.Contravariant (Contravariant(..)) import Data.Monoid ((<>)) import Data.Text.Buildable (Buildable(..)) import Data.Text.Lazy (Text)@@ -69,7 +76,6 @@ import qualified Dhall.Import import qualified Dhall.Parser import qualified Dhall.TypeCheck-import qualified NeatInterpolation throws :: Exception e => Either e a -> IO a throws (Left e) = Control.Exception.throwIO e@@ -84,16 +90,15 @@ -} data InvalidType = InvalidType deriving (Typeable) -_ERROR :: Data.Text.Text+_ERROR :: String _ERROR = "\ESC[1;31mError\ESC[0m" instance Show InvalidType where- show InvalidType = Data.Text.unpack [NeatInterpolation.text|-$_ERROR: Invalid Dhall.Type--Every Type must provide an extract function that succeeds if an expression-matches the expected type. You provided a Type that disobeys this contract-|]+ show InvalidType =+ _ERROR <> ": Invalid Dhall.Type \n\+ \ \n\+ \Every Type must provide an extract function that succeeds if an expression \n\+ \matches the expected type. You provided a Type that disobeys this contract \n" instance Exception InvalidType @@ -141,6 +146,27 @@ Just x -> return x Nothing -> Control.Exception.throwIO InvalidType +-- | Use this function to extract Haskell values directly from Dhall AST.+-- The intended use case is to allow easy extraction of Dhall values for+-- making the function `Dhall.Core.normalizeWith` easier to use.+--+-- For other use cases, use `input` from `Dhall` module. It will give you+-- a much better user experience.+rawInput+ :: Alternative f + => Type a+ -- ^ The type of value to decode from Dhall to Haskell+ -> Expr s X+ -- ^ a closed form Dhall program, which evaluates to the expected type+ -> f a+ -- ^ The decoded value in Haskell+rawInput (Type {..}) expr = do+ case extract (Dhall.Core.normalize expr) of+ Just x -> pure x+ Nothing -> empty+++ {-| Use this to provide more detailed error messages >> input auto "True" :: IO Integer@@ -274,8 +300,10 @@ > input :: Type a -> Text -> IO a -} data Type a = Type- { extract :: Expr X X -> Maybe a+ { extract :: Expr Src X -> Maybe a+ -- ^ Extracts Haskell value from the Dhall expression , expected :: Expr Src X+ -- ^ Dhall type of the Haskell value } deriving (Functor) @@ -331,19 +359,27 @@ expected = Double -{-| Decode `Text`+{-| Decode lazy `Text` ->>> input text "\"Test\""+>>> input lazyText "\"Test\"" "Test" -}-text :: Type Text-text = Type {..}+lazyText :: Type Text+lazyText = Type {..} where extract (TextLit t) = pure (Data.Text.Lazy.Builder.toLazyText t) extract _ = empty expected = Text +{-| Decode strict `Text`++>>> input strictText "\"Test\""+"Test"+-}+strictText :: Type Data.Text.Text+strictText = fmap Data.Text.Lazy.toStrict lazyText+ {-| Decode a `Maybe` >>> input (maybe integer) "[1] : Optional Integer"@@ -401,14 +437,30 @@ autoWith _ = double instance Interpret Text where- autoWith _ = text+ autoWith _ = lazyText +instance Interpret Data.Text.Text where+ autoWith _ = strictText+ instance Interpret a => Interpret (Maybe a) where autoWith opts = maybe (autoWith opts) instance Interpret a => Interpret (Vector a) where autoWith opts = vector (autoWith opts) +instance (Inject a, Interpret b) => Interpret (a -> b) where+ autoWith opts = Type extractOut expectedOut+ where+ extractOut e = Just (\i -> case extractIn (Dhall.Core.normalize (App e (embed i))) of+ Just o -> o+ Nothing -> error "Interpret: You cannot decode a function if it does not have the correct type" )++ expectedOut = Pi "_" declared expectedIn++ InputType {..} = inject++ Type extractIn expectedIn = autoWith opts+ {-| Use the default options for interpreting a configuration file > auto = autoWith defaultInterpretOptions@@ -567,3 +619,231 @@ key = fieldModifier (Data.Text.Lazy.pack (selName n)) Type extract' expected' = autoWith opts++{-| An @(InputType a)@ represents a way to marshal a value of type @\'a\'@ from+ Haskell into Dhall+-}+data InputType a = InputType+ { embed :: a -> Expr Src X+ -- ^ Embeds a Haskell value as a Dhall expression+ , declared :: Expr Src X+ -- ^ Dhall type of the Haskell value+ }++instance Contravariant InputType where+ contramap f (InputType embed declared) = InputType embed' declared+ where+ embed' x = embed (f x)++{-| This class is used by `Interpret` instance for functions:++> instance (Inject a, Interpret b) => Interpret (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+ (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+ injectWith :: InterpretOptions -> InputType a+ default injectWith+ :: (Generic a, GenericInject (Rep a)) => InterpretOptions -> InputType a+ injectWith options = contramap GHC.Generics.from (genericInjectWith options)++{-| Use the default options for injecting a value++> inject = inject defaultInterpretOptions+-}+inject :: Inject a => InputType a+inject = injectWith defaultInterpretOptions++instance Inject Bool where+ injectWith _ = InputType {..}+ where+ embed = BoolLit++ declared = Bool++instance Inject Text where+ injectWith _ = InputType {..}+ where+ embed text = TextLit (Data.Text.Lazy.Builder.fromLazyText text)++ declared = Text++instance Inject Data.Text.Text where+ injectWith _ = InputType {..}+ where+ embed text = TextLit (Data.Text.Lazy.Builder.fromText text)++ declared = Text++instance Inject Natural where+ injectWith _ = InputType {..}+ where+ embed = NaturalLit++ declared = Natural++instance Inject Integer where+ injectWith _ = InputType {..}+ where+ embed = IntegerLit++ declared = Integer++instance Inject Double where+ injectWith _ = InputType {..}+ where+ embed = DoubleLit++ declared = Double++instance Inject a => Inject (Maybe a) where+ injectWith options = InputType embedOut declaredOut+ where+ embedOut (Just x) =+ OptionalLit declaredIn (Data.Vector.singleton (embedIn x))+ embedOut Nothing =+ OptionalLit declaredIn Data.Vector.empty++ InputType embedIn declaredIn = injectWith options++ declaredOut = App Optional declaredIn++instance Inject a => Inject (Vector a) where+ injectWith options = InputType embedOut declaredOut+ where+ embedOut xs = ListLit (Just declaredIn) (fmap embedIn xs)++ declaredOut = App List declaredIn++ InputType embedIn declaredIn = injectWith options++{-| This is the underlying class that powers the `Interpret` class's support+ for automatically deriving a generic implementation+-}+class GenericInject f where+ genericInjectWith :: InterpretOptions -> InputType (f a)++instance GenericInject f => GenericInject (M1 D d f) where+ genericInjectWith = fmap (contramap unM1) genericInjectWith++instance GenericInject f => GenericInject (M1 C c f) where+ genericInjectWith = fmap (contramap unM1) genericInjectWith++instance (Constructor c1, Constructor c2, GenericInject f1, GenericInject f2) => GenericInject (M1 C c1 f1 :+: M1 C c2 f2) where+ genericInjectWith options@(InterpretOptions {..}) = InputType {..}+ where+ embed (L1 (M1 l)) = UnionLit keyL (embedL l) Data.Map.empty+ embed (R1 (M1 r)) = UnionLit keyR (embedR r) Data.Map.empty++ declared =+ Union (Data.Map.fromList [(keyL, declaredL), (keyR, declaredR)])++ nL :: M1 i c1 f1 a+ nL = undefined++ nR :: M1 i c2 f2 a+ nR = undefined++ keyL = constructorModifier (Data.Text.Lazy.pack (conName nL))+ keyR = constructorModifier (Data.Text.Lazy.pack (conName nR))++ InputType embedL declaredL = genericInjectWith options+ InputType embedR declaredR = genericInjectWith options++instance (Constructor c, GenericInject (f :+: g), GenericInject h) => GenericInject ((f :+: g) :+: M1 C c h) where+ genericInjectWith options@(InterpretOptions {..}) = InputType {..}+ where+ embed (L1 l) = UnionLit keyL valL (Data.Map.insert keyR declaredR ktsL')+ where+ UnionLit keyL valL ktsL' = embedL l+ embed (R1 (M1 r)) = UnionLit keyR (embedR r) ktsL++ nR :: M1 i c h a+ nR = undefined++ keyR = constructorModifier (Data.Text.Lazy.pack (conName nR))++ declared = Union (Data.Map.insert keyR declaredR ktsL)++ InputType embedL (Union ktsL) = genericInjectWith options+ InputType embedR declaredR = genericInjectWith options++instance (Constructor c, GenericInject f, GenericInject (g :+: h)) => GenericInject (M1 C c f :+: (g :+: h)) where+ genericInjectWith options@(InterpretOptions {..}) = InputType {..}+ where+ embed (L1 (M1 l)) = UnionLit keyL (embedL l) ktsR+ embed (R1 r) = UnionLit keyR valR (Data.Map.insert keyL declaredL ktsR')+ where+ UnionLit keyR valR ktsR' = embedR r++ nL :: M1 i c f a+ nL = undefined++ keyL = constructorModifier (Data.Text.Lazy.pack (conName nL))++ declared = Union (Data.Map.insert keyL declaredL ktsR)++ InputType embedL declaredL = genericInjectWith options+ InputType embedR (Union ktsR) = genericInjectWith options++instance (GenericInject (f :+: g), GenericInject (h :+: i)) => GenericInject ((f :+: g) :+: (h :+: i)) where+ genericInjectWith options = InputType {..}+ where+ embed (L1 l) = UnionLit keyL valR (Data.Map.union ktsL' ktsR)+ where+ UnionLit keyL valR ktsL' = embedL l+ embed (R1 r) = UnionLit keyR valR (Data.Map.union ktsL ktsR')+ where+ UnionLit keyR valR ktsR' = embedR r++ declared = Union (Data.Map.union ktsL ktsR)++ InputType embedL (Union ktsL) = genericInjectWith options+ InputType embedR (Union ktsR) = genericInjectWith options++instance (GenericInject f, GenericInject g) => GenericInject (f :*: g) where+ genericInjectWith options = InputType embedOut declaredOut+ where+ embedOut (l :*: r) = RecordLit (Data.Map.union mapL mapR)+ where+ RecordLit mapL = embedInL l+ RecordLit mapR = embedInR r++ declaredOut = Record (Data.Map.union mapL mapR)+ where+ Record mapL = declaredInL+ Record mapR = declaredInR++ InputType embedInL declaredInL = genericInjectWith options++ InputType embedInR declaredInR = genericInjectWith options++instance GenericInject U1 where+ genericInjectWith _ = InputType {..}+ where+ embed _ = RecordLit Data.Map.empty++ declared = Record Data.Map.empty++instance (Selector s, Inject a) => GenericInject (M1 S s (K1 i a)) where+ genericInjectWith opts@(InterpretOptions {..}) =+ InputType embedOut declaredOut+ where+ n :: M1 i s f a+ n = undefined++ name = fieldModifier (Data.Text.Lazy.pack (selName n))++ embedOut (M1 (K1 x)) = RecordLit (Data.Map.singleton name (embedIn x))++ declaredOut = Record (Data.Map.singleton name declaredIn)++ InputType embedIn declaredIn = injectWith opts
src/Dhall/Core.hs view
@@ -4,7 +4,6 @@ {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} {-# OPTIONS_GHC -Wall #-}@@ -27,9 +26,12 @@ -- * Normalization , normalize+ , normalizeWith+ , Normalizer , subst , shift , isNormalized+ , isNormalizedWith -- * Pretty-printing , pretty@@ -69,7 +71,6 @@ import qualified Data.Vector import qualified Data.Vector.Mutable import qualified Filesystem.Path.CurrentOS as Filesystem-import qualified NeatInterpolation {-| Constants for a pure type system @@ -259,6 +260,8 @@ -- | > ListLit (Just t ) [x, y, z] ~ [x, y, z] : List t -- > ListLit Nothing [x, y, z] ~ [x, y, z] | ListLit (Maybe (Expr s a)) (Vector (Expr s a))+ -- | > ListAppend x y ~ x # y+ | ListAppend (Expr s a) (Expr s a) -- | > ListBuild ~ List/build | ListBuild -- | > ListFold ~ List/fold@@ -349,6 +352,7 @@ TextAppend a b >>= k = TextAppend (a >>= k) (b >>= k) 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@@ -408,6 +412,7 @@ first k (TextAppend a b ) = TextAppend (first k a) (first k b) 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@@ -565,40 +570,46 @@ -- | Builder corresponding to the @exprC3@ parser in "Dhall.Parser" buildExprC3 :: Buildable a => Expr s a -> Builder-buildExprC3 (BoolAnd a b) = buildExprC4 a <> " && " <> buildExprC3 b-buildExprC3 (Note _ b) = buildExprC3 b-buildExprC3 a = buildExprC4 a+buildExprC3 (ListAppend a b) = buildExprC4 a <> " # " <> buildExprC3 b+buildExprC3 (Note _ b) = buildExprC3 b+buildExprC3 a = buildExprC4 a -- | Builder corresponding to the @exprC4@ parser in "Dhall.Parser" buildExprC4 :: Buildable a => Expr s a -> Builder-buildExprC4 (Combine a b) = buildExprC5 a <> " ∧ " <> buildExprC4 b-buildExprC4 (Note _ b) = buildExprC4 b-buildExprC4 a = buildExprC5 a+buildExprC4 (BoolAnd a b) = buildExprC5 a <> " && " <> buildExprC4 b+buildExprC4 (Note _ b) = buildExprC4 b+buildExprC4 a = buildExprC5 a -- | Builder corresponding to the @exprC5@ parser in "Dhall.Parser" buildExprC5 :: Buildable a => Expr s a -> Builder-buildExprC5 (Prefer a b) = buildExprC6 a <> " ⫽ " <> buildExprC5 b-buildExprC5 (Note _ b) = buildExprC5 b-buildExprC5 a = buildExprC6 a+buildExprC5 (Combine a b) = buildExprC6 a <> " ∧ " <> buildExprC5 b+buildExprC5 (Note _ b) = buildExprC5 b+buildExprC5 a = buildExprC6 a -- | Builder corresponding to the @exprC6@ parser in "Dhall.Parser" buildExprC6 :: Buildable a => Expr s a -> Builder-buildExprC6 (NaturalTimes a b) = buildExprC7 a <> " * " <> buildExprC6 b-buildExprC6 (Note _ b) = buildExprC6 b-buildExprC6 a = buildExprC7 a+buildExprC6 (Prefer a b) = buildExprC7 a <> " ⫽ " <> buildExprC6 b+buildExprC6 (Note _ b) = buildExprC6 b+buildExprC6 a = buildExprC7 a -- | Builder corresponding to the @exprC7@ parser in "Dhall.Parser" buildExprC7 :: Buildable a => Expr s a -> Builder-buildExprC7 (BoolEQ a b) = buildExprC8 a <> " == " <> buildExprC7 b-buildExprC7 (Note _ b) = buildExprC7 b-buildExprC7 a = buildExprC8 a+buildExprC7 (NaturalTimes a b) = buildExprC8 a <> " * " <> buildExprC7 b+buildExprC7 (Note _ b) = buildExprC7 b+buildExprC7 a = buildExprC8 a -- | Builder corresponding to the @exprC8@ parser in "Dhall.Parser" buildExprC8 :: Buildable a => Expr s a -> Builder-buildExprC8 (BoolNE a b) = buildExprD a <> " != " <> buildExprC8 b+buildExprC8 (BoolEQ a b) = buildExprC9 a <> " == " <> buildExprC8 b buildExprC8 (Note _ b) = buildExprC8 b-buildExprC8 a = buildExprD a+buildExprC8 a = buildExprC9 a +-- | Builder corresponding to the @exprC9@ parser in "Dhall.Parser"+buildExprC9 :: Buildable a => Expr s a -> Builder+buildExprC9 (BoolNE a b) = buildExprD a <> " != " <> buildExprC9 b+buildExprC9 (Note _ b) = buildExprC9 b+buildExprC9 a = buildExprD a+ -- | Builder corresponding to the @exprD@ parser in "Dhall.Parser" buildExprD :: Buildable a => Expr s a -> Builder buildExprD (App a b) = buildExprD a <> " " <> buildExprE b@@ -687,6 +698,8 @@ buildUnion a buildExprF (UnionLit a b c) = buildUnionLit a b c+buildExprF (ListLit Nothing b) =+ "[" <> buildElems (Data.Vector.toList b) <> "]" buildExprF (Embed a) = build a buildExprF (Note _ b) =@@ -940,6 +953,10 @@ 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@@ -1079,6 +1096,10 @@ 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@@ -1129,38 +1150,67 @@ However, `normalize` will not fail if the expression is ill-typed and will leave ill-typed sub-expressions unevaluated. -}-normalize :: Expr s a -> Expr t a-normalize e = case e of+normalize :: Expr s a -> Expr t a+normalize = normalizeWith (const Nothing)+++{-| 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 :: Normalizer a -> Expr s a -> Expr t a+normalizeWith ctx e0 = loop (shift 0 "_" e0)+ where+ -- This is to avoid a `Show` constraint on the @a@ and @s@ in the type of+ -- `loop`. In theory, this might change a failing repro case into+ -- a successful one, but the risk of that is low enough to not warrant+ -- the `Show` constraint. I care more about proving at the type level+ -- that the @a@ and @s@ type parameters are never used+ e'' = bimap (\_ -> ()) (\_ -> ()) e0++ text = "NormalizeWith.loop (" <> Data.Text.pack (show e'') <> ")"+ loop e = case e of Const k -> Const k Var v -> Var v Lam x _A b -> Lam x _A' b' where- _A' = normalize _A- b' = normalize b+ _A' = loop _A+ b' = loop b Pi x _A _B -> Pi x _A' _B' where- _A' = normalize _A- _B' = normalize _B- App f a -> case normalize f of- Lam x _A b -> normalize b'' -- Beta reduce+ _A' = loop _A+ _B' = loop _B+ App f a -> case loop f of+ Lam x _A b -> loop b'' -- Beta reduce where a' = shift 1 (V x 0) a b' = subst (V x 0) a' b b'' = shift (-1) (V x 0) b' f' -> case App f' a' of -- fold/build fusion for `List`- App (App ListBuild _) (App (App ListFold _) e') -> normalize e'- App (App ListFold _) (App (App ListBuild _) e') -> normalize e'+ App (App ListBuild _) (App (App ListFold _) e') -> loop e'+ App (App ListFold _) (App (App ListBuild _) e') -> loop e' -- fold/build fusion for `Natural`- App NaturalBuild (App NaturalFold e') -> normalize e'- App NaturalFold (App NaturalBuild e') -> normalize e'+ App NaturalBuild (App NaturalFold e') -> loop e'+ App NaturalFold (App NaturalBuild e') -> loop e' -- fold/build fusion for `Optional`- App (App OptionalBuild _) (App (App OptionalFold _) e') -> normalize e'- App (App OptionalFold _) (App (App OptionalBuild _) e') -> normalize e'+ App (App OptionalBuild _) (App (App OptionalFold _) e') -> loop e'+ App (App OptionalFold _) (App (App OptionalBuild _) e') -> loop e' App (App (App (App NaturalFold (NaturalLit n0)) _) succ') zero ->- normalize (go n0)+ loop (go n0) where go !0 = zero go !n = App succ' (go (n - 1))@@ -1169,7 +1219,7 @@ | otherwise -> App f' a' where labeled =- normalize (App (App (App k Natural) "Succ") "Zero")+ loop (App (App (App k Natural) "Succ") "Zero") n = go 0 labeled where@@ -1192,7 +1242,7 @@ | check -> OptionalLit t k' | otherwise -> App f' a' where- labeled = normalize (App (App (App k (App Optional t)) "Just") "Nothing")+ labeled = loop (App (App (App k (App Optional t)) "Just") "Nothing") k' = go labeled where@@ -1209,7 +1259,7 @@ | otherwise -> App f' a' where labeled =- normalize (App (App (App k (App List t)) "Cons") "Nil")+ loop (App (App (App k (App List t)) "Cons") "Nil") k' cons nil = go labeled where@@ -1222,21 +1272,21 @@ go (Var "Nil") = True go _ = False App (App (App (App (App ListFold _) (ListLit _ xs)) _) cons) nil ->- normalize (Data.Vector.foldr cons' nil xs)+ loop (Data.Vector.foldr cons' nil xs) where cons' y ys = App (App cons y) ys App (App ListLength _) (ListLit _ ys) -> NaturalLit (fromIntegral (Data.Vector.length ys)) App (App ListHead t) (ListLit _ ys) ->- normalize (OptionalLit t (Data.Vector.take 1 ys))+ loop (OptionalLit t (Data.Vector.take 1 ys)) App (App ListLast t) (ListLit _ ys) ->- normalize (OptionalLit t y)+ loop (OptionalLit t y) where y = if Data.Vector.null ys then Data.Vector.empty else Data.Vector.singleton (Data.Vector.last ys) App (App ListIndexed t) (ListLit _ xs) ->- normalize (ListLit (Just t') (fmap adapt (Data.Vector.indexed xs)))+ loop (ListLit (Just t') (fmap adapt (Data.Vector.indexed xs))) where t' = Record (Data.Map.fromList kts) where@@ -1249,21 +1299,23 @@ , ("value", x) ] App (App ListReverse t) (ListLit _ xs) ->- normalize (ListLit (Just t) (Data.Vector.reverse xs))+ loop (ListLit (Just t) (Data.Vector.reverse xs)) App (App (App (App (App OptionalFold _) (OptionalLit _ xs)) _) just) nothing ->- normalize (maybe nothing just' (toMaybe xs))+ loop (maybe nothing just' (toMaybe xs)) where just' y = App just y toMaybe = Data.Maybe.listToMaybe . Data.Vector.toList- _ -> App f' a'+ _ -> case ctx (App f' a') of+ Nothing -> App f' a'+ Just app' -> loop app' where- a' = normalize a- Let f _ r b -> normalize b''+ a' = loop a+ Let 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 _ -> normalize x+ Annot x _ -> loop x Bool -> Bool BoolLit b -> BoolLit b BoolAnd x y ->@@ -1274,8 +1326,8 @@ _ -> BoolAnd x' y' _ -> BoolAnd x' y' where- x' = normalize x- y' = normalize y+ x' = loop x+ y' = loop y BoolOr x y -> case x' of BoolLit xn ->@@ -1284,8 +1336,8 @@ _ -> BoolOr x' y' _ -> BoolOr x' y' where- x' = normalize x- y' = normalize y+ x' = loop x+ y' = loop y BoolEQ x y -> case x' of BoolLit xn ->@@ -1294,8 +1346,8 @@ _ -> BoolEQ x' y' _ -> BoolEQ x' y' where- x' = normalize x- y' = normalize y+ x' = loop x+ y' = loop y BoolNE x y -> case x' of BoolLit xn ->@@ -1304,15 +1356,15 @@ _ -> BoolNE x' y' _ -> BoolNE x' y' where- x' = normalize x- y' = normalize y- BoolIf b true false -> case normalize b of+ x' = loop x+ y' = loop y+ BoolIf b true false -> case loop b of BoolLit True -> true' BoolLit False -> false' b' -> BoolIf b' true' false' where- true' = normalize true- false' = normalize false+ true' = loop true+ false' = loop false Natural -> Natural NaturalLit n -> NaturalLit n NaturalFold -> NaturalFold@@ -1330,8 +1382,8 @@ _ -> NaturalPlus x' y' _ -> NaturalPlus x' y' where- x' = normalize x- y' = normalize y+ x' = loop x+ y' = loop y NaturalTimes x y -> case x' of NaturalLit xn ->@@ -1340,8 +1392,8 @@ _ -> NaturalTimes x' y' _ -> NaturalTimes x' y' where- x' = normalize x- y' = normalize y+ x' = loop x+ y' = loop y Integer -> Integer IntegerLit n -> IntegerLit n IntegerShow -> IntegerShow@@ -1358,13 +1410,23 @@ _ -> TextAppend x' y' _ -> TextAppend x' y' where- x' = normalize x- y' = normalize y+ x' = loop x+ y' = loop y List -> List ListLit t es -> ListLit t' es' where- t' = fmap normalize t- es' = fmap normalize es+ t' = fmap loop t+ es' = fmap loop es+ ListAppend x y ->+ case x' of+ ListLit t xs ->+ case y' of+ ListLit _ ys -> ListLit t (xs <> ys)+ _ -> ListAppend x' y'+ _ -> ListAppend x' y'+ where+ x' = loop x+ y' = loop y ListBuild -> ListBuild ListFold -> ListFold ListLength -> ListLength@@ -1375,76 +1437,79 @@ Optional -> Optional OptionalLit t es -> OptionalLit t' es' where- t' = normalize t- es' = fmap normalize es+ t' = loop t+ es' = fmap loop es OptionalFold -> OptionalFold OptionalBuild -> OptionalBuild Record kts -> Record kts' where- kts' = fmap normalize kts+ kts' = fmap loop kts RecordLit kvs -> RecordLit kvs' where- kvs' = fmap normalize kvs+ kvs' = fmap loop kvs Union kts -> Union kts' where- kts' = fmap normalize kts+ kts' = fmap loop kts UnionLit k v kvs -> UnionLit k v' kvs' where- v' = normalize v- kvs' = fmap normalize kvs+ v' = loop v+ kvs' = fmap loop kvs Combine x0 y0 -> let combine x y = case x of RecordLit kvsX -> case y of RecordLit kvsY -> let kvs = Data.Map.unionWith combine kvsX kvsY- in RecordLit (fmap normalize kvs)+ in RecordLit (fmap loop kvs) _ -> Combine x y _ -> Combine x y- in combine (normalize x0) (normalize y0)+ in combine (loop x0) (loop y0) Prefer x y -> case x' of RecordLit kvsX -> case y' of RecordLit kvsY ->- RecordLit (fmap normalize (Data.Map.union kvsY kvsX))+ RecordLit (fmap loop (Data.Map.union kvsY kvsX)) _ -> Prefer x' y' _ -> Prefer x' y' where- x' = normalize x- y' = normalize y+ x' = loop x+ y' = loop y Merge x y t -> case x' of RecordLit kvsX -> case y' of UnionLit kY vY _ -> case Data.Map.lookup kY kvsX of- Just vX -> normalize (App vX vY)+ Just vX -> loop (App vX vY) Nothing -> Merge x' y' t' _ -> Merge x' y' t' _ -> Merge x' y' t' where- x' = normalize x- y' = normalize y- t' = fmap normalize t+ x' = loop x+ y' = loop y+ t' = fmap loop t Field r x ->- case normalize r of+ case loop r of RecordLit kvs -> case Data.Map.lookup x kvs of- Just v -> normalize v- Nothing -> Field (RecordLit (fmap normalize kvs)) x+ Just v -> loop v+ Nothing -> Field (RecordLit (fmap loop kvs)) x r' -> Field r' x- Note _ e' -> normalize e'+ Note _ e' -> loop e' Embed a -> Embed a- where- -- This is to avoid a `Show` constraint on the @a@ and @s@ in the type of- -- `normalize`. In theory, this might change a failing repro case into- -- a successful one, but the risk of that is low enough to not warrant- -- the `Show` constraint. I care more about proving at the type level- -- that the @a@ and @s@ type parameters are never used- e'' = bimap (\_ -> ()) (\_ -> ()) e - text = "normalize (" <> Data.Text.pack (show e'') <> ")"+-- | Use this to wrap you embedded functions (see `normalizeWith`) to make them+-- polymorphic enough to be used.+type Normalizer a = forall s. Expr s a -> Maybe (Expr s 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 :: (Eq s, Eq a) => Normalizer a -> Expr s a -> Bool+isNormalizedWith ctx e = e == (normalizeWith ctx e)++ -- | Quickly check if an expression is in normal form isNormalized :: Expr s a -> Bool isNormalized e = case shift 0 "_" e of -- `shift` is a hack to delete `Note`@@ -1587,6 +1652,13 @@ _ -> True List -> True ListLit t es -> all isNormalized t && all isNormalized es+ ListAppend x y -> isNormalized x && isNormalized y &&+ case x of+ ListLit _ _ ->+ case y of+ ListLit _ _ -> False+ _ -> True+ _ -> True ListBuild -> True ListFold -> True ListLength -> True@@ -1636,28 +1708,28 @@ Note _ e' -> isNormalized e' Embed _ -> True -_ERROR :: Data.Text.Text+_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 (Data.Text.unpack [NeatInterpolation.text|-$_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/Gabriel439/Haskell-Dhall-Library/issues--Please include the following text in your bug report:--```-$text-```-|])+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/Gabriel439/Haskell-Dhall-Library/issues "+ , " "+ , "Please include the following text in your bug report: "+ , " "+ , "``` "+ , Data.Text.unpack text <> " "+ , "``` "+ ] ) buildVector :: (forall x . (a -> x -> x) -> x -> x) -> Vector a buildVector f = Data.Vector.reverse (Data.Vector.create (do
src/Dhall/Import.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RecordWildCards #-} {-# OPTIONS_GHC -Wall #-} @@ -103,6 +102,7 @@ -- * Import exprFromPath , load+ , loadWith , Cycle(..) , ReferentiallyOpaque(..) , Imported(..)@@ -150,7 +150,6 @@ import qualified Data.CaseInsensitive import qualified Data.List as List import qualified Data.Map.Strict as Map-import qualified Data.Text import qualified Data.Text.Encoding import qualified Data.Text.Lazy as Text import qualified Data.Text.Lazy.Builder as Builder@@ -158,10 +157,10 @@ import qualified Data.Vector import qualified Dhall.Core import qualified Dhall.Parser+import qualified Dhall.Context import qualified Dhall.TypeCheck import qualified Filesystem import qualified Filesystem.Path.CurrentOS-import qualified NeatInterpolation import qualified Network.HTTP.Client as HTTP import qualified Network.HTTP.Client.TLS as HTTP import qualified Filesystem.Path.CurrentOS as Filesystem@@ -479,25 +478,25 @@ -} data InternalError = InternalError deriving (Typeable) -_ERROR :: Data.Text.Text+_ERROR :: String _ERROR = "\ESC[1;31mError\ESC[0m" instance Show InternalError where- show InternalError = Data.Text.unpack [NeatInterpolation.text|-$_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/Gabriel439/Haskell-Dhall-Library/issues--Please include the following text in your bug report:--```-Header extraction failed even though the header type-checked-```-|]+ show InternalError = 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/Gabriel439/Haskell-Dhall-Library/issues "+ , " "+ , "Please include the following text in your bug report: "+ , " "+ , "``` "+ , "Header extraction failed even though the header type-checked "+ , "``` "+ ] instance Exception InternalError @@ -674,7 +673,10 @@ -- | Load a `Path` as a \"static\" expression (with all imports resolved) loadStatic :: Path -> StateT Status IO (Expr Src X)-loadStatic path = do+loadStatic = loadStaticWith Dhall.Context.empty++loadStaticWith :: Dhall.Context.Context (Expr Src X) -> Path -> StateT Status IO (Expr Src X)+loadStaticWith ctx path = do paths <- zoom stack State.get let local (Path (URL url _) _) =@@ -711,7 +713,8 @@ Nothing -> do let paths' = path:paths zoom stack (State.put paths')- expr'' <- fmap join (traverse loadStatic expr')+ expr'' <- fmap join (traverse (loadStaticWith ctx)+ expr') zoom stack (State.put paths) return expr'' return (expr'', False)@@ -725,7 +728,7 @@ -- have already been checked if cached then return ()- else case Dhall.TypeCheck.typeOf expr of+ else case Dhall.TypeCheck.typeWith ctx expr of Left err -> liftIO (throwIO (Imported (path:paths) err)) Right _ -> return () @@ -734,5 +737,11 @@ -- | Resolve all imports within an expression load :: Expr Src Path -> IO (Expr Src X) load expr = State.evalStateT (fmap join (traverse loadStatic expr)) status+ where+ status = Status [] Map.empty Nothing++-- | Resolve all imports within an expression using a custom typing context+loadWith :: Dhall.Context.Context (Expr Src X) -> Expr Src Path -> IO (Expr Src X)+loadWith ctx expr = State.evalStateT (fmap join (traverse (loadStaticWith ctx) expr)) status where status = Status [] Map.empty Nothing
src/Dhall/Parser.hs view
@@ -481,12 +481,13 @@ exprC0 = chain exprC1 (symbol "||") BoolOr exprC0 exprC1 = chain exprC2 (symbol "+" ) NaturalPlus exprC1 exprC2 = chain exprC3 (symbol "++") TextAppend exprC2- exprC3 = chain exprC4 (symbol "&&") BoolAnd exprC3- exprC4 = chain exprC5 combine Combine exprC4- exprC5 = chain exprC6 prefer Prefer exprC5- exprC6 = chain exprC7 (symbol "*" ) NaturalTimes exprC6- exprC7 = chain exprC8 (symbol "==") BoolEQ exprC7- exprC8 = chain (exprD embedded) (symbol "!=") BoolNE exprC8+ exprC3 = chain exprC4 (symbol "#" ) ListAppend exprC3+ exprC4 = chain exprC5 (symbol "&&") BoolAnd exprC4+ exprC5 = chain exprC6 combine Combine exprC5+ exprC6 = chain exprC7 prefer Prefer exprC6+ exprC7 = chain exprC8 (symbol "*" ) NaturalTimes exprC7+ exprC8 = chain exprC9 (symbol "==") BoolEQ exprC8+ exprC9 = chain (exprD embedded) (symbol "!=") BoolNE exprC9 -- We can't use left-recursion to define `exprD` otherwise the parser will -- loop infinitely. However, I'd still like to use left-recursion in the
src/Dhall/Tutorial.hs view
@@ -26,6 +26,9 @@ -- * Functions -- $functions + -- * Compiler+ -- $compiler+ -- * Strings -- $strings @@ -113,6 +116,9 @@ -- ** @List@ -- $list + -- *** @(#)@+ -- $listAppend+ -- *** @List/fold@ -- $listFold @@ -627,9 +633,56 @@ -- functions in Haskell. The only difference is that Dhall requires you to -- annotate the type of the function's input. ----- We can 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+-- 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: --@@ -655,9 +708,9 @@ -- -- > 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 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: --@@ -1817,6 +1870,31 @@ -- Also, every @List@ must end with a mandatory type annotation -- -- The built-in operations on @List@s are:++-- $listAppend+--+-- Example:+--+-- > $ dhall+-- > [1, 2, 3] # [5, 6, 7]+-- > <Ctrl-D>+-- > List Integer+-- >+-- > [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 --
src/Dhall/TypeCheck.hs view
@@ -1,3135 +1,3136 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE RecordWildCards #-}-{-# OPTIONS_GHC -Wall #-}---- | This module contains the logic for type checking Dhall code--module Dhall.TypeCheck (- -- * Type-checking- typeWith- , typeOf-- -- * Types- , X(..)- , TypeError(..)- , DetailedTypeError(..)- , TypeMessage(..)- ) where--import Control.Exception (Exception)-import Data.Foldable (forM_, toList)-import Data.Monoid ((<>))-import Data.Set (Set)-import Data.Text.Buildable (Buildable(..))-import Data.Text.Lazy (Text)-import Data.Text.Lazy.Builder (Builder)-import Data.Traversable (forM)-import Data.Typeable (Typeable)-import Dhall.Core (Const(..), Expr(..), Var(..))-import Dhall.Context (Context)--import qualified Control.Monad.Trans.State.Strict as State-import qualified Data.Map-import qualified Data.Set-import qualified Data.Text-import qualified Data.Text.Lazy as Text-import qualified Data.Text.Lazy.Builder as Builder-import qualified Data.Vector-import qualified Dhall.Context-import qualified Dhall.Core-import qualified NeatInterpolation--axiom :: Const -> Either (TypeError s) Const-axiom Type = return Kind-axiom Kind = Left (TypeError Dhall.Context.empty (Const Kind) Untyped)--rule :: Const -> Const -> Either () Const-rule Type Kind = Left ()-rule Type Type = return Type-rule Kind Kind = return Kind-rule Kind Type = return Type--match :: Var -> Var -> [(Text, Text)] -> Bool-match (V xL nL) (V xR nR) [] =- xL == xR && nL == nR-match (V xL 0 ) (V xR 0 ) ((xL', xR'):_ )- | xL == xL' && xR == xR' = True-match (V xL nL) (V xR nR) ((xL', xR'):xs) =- match (V xL nL') (V xR nR') xs- where- nL' = if xL == xL' then nL - 1 else nL- nR' = if xR == xR' then nR - 1 else nR--propEqual :: Expr s X -> Expr t X -> Bool-propEqual eL0 eR0 =- State.evalState- (go (Dhall.Core.normalize eL0) (Dhall.Core.normalize eR0))- []- where- go (Const Type) (Const Type) = return True- go (Const Kind) (Const Kind) = return True- go (Var vL) (Var vR) = do- ctx <- State.get- return (match vL vR ctx)- go (Pi xL tL bL) (Pi xR tR bR) = do- ctx <- State.get- eq1 <- go tL tR- if eq1- then do- State.put ((xL, xR):ctx)- eq2 <- go bL bR- State.put ctx- return eq2- else return False- go (App fL aL) (App fR aR) = do- b1 <- go fL fR- if b1 then go aL aR else return False- go Bool Bool = return True- go Natural Natural = return True- go Integer Integer = return True- go Double Double = return True- go Text Text = return True- go List List = return True- go Optional Optional = return True- go (Record ktsL0) (Record ktsR0) = do- let loop ((kL, tL):ktsL) ((kR, tR):ktsR)- | kL == kR = do- b <- go tL tR- if b- then loop ktsL ktsR- else return False- loop [] [] = return True- loop _ _ = return False- loop (Data.Map.toList ktsL0) (Data.Map.toList ktsR0)- go (Union ktsL0) (Union ktsR0) = do- let loop ((kL, tL):ktsL) ((kR, tR):ktsR)- | kL == kR = do- b <- go tL tR- if b- then loop ktsL ktsR- else return False- loop [] [] = return True- loop _ _ = return False- loop (Data.Map.toList ktsL0) (Data.Map.toList ktsR0)- go _ _ = return False--{-| Type-check an expression and return the expression's type if type-checking- succeeds or an error if type-checking fails-- `typeWith` does not necessarily normalize the type since full normalization- is not necessary for just type-checking. If you actually care about the- returned type then you may want to `Dhall.Core.normalize` it afterwards.--}-typeWith :: Context (Expr s X) -> Expr s X -> Either (TypeError s) (Expr s X)-typeWith _ (Const c ) = do- fmap Const (axiom c)-typeWith ctx e@(Var (V x n) ) = do- case Dhall.Context.lookup x n ctx of- Nothing -> Left (TypeError ctx e UnboundVariable)- Just a -> return a-typeWith ctx (Lam x _A b ) = do- let ctx' = fmap (Dhall.Core.shift 1 (V x 0)) (Dhall.Context.insert x _A ctx)- _B <- typeWith ctx' b- let p = Pi x _A _B- _t <- typeWith ctx p- return p-typeWith ctx e@(Pi x _A _B ) = do- tA <- fmap Dhall.Core.normalize (typeWith ctx _A)- kA <- case tA of- Const k -> return k- _ -> Left (TypeError ctx e (InvalidInputType _A))-- let ctx' = fmap (Dhall.Core.shift 1 (V x 0)) (Dhall.Context.insert x _A ctx)- tB <- fmap Dhall.Core.normalize (typeWith ctx' _B)- kB <- case tB of- Const k -> return k- _ -> Left (TypeError ctx' e (InvalidOutputType _B))-- case rule kA kB of- Left () -> Left (TypeError ctx e (NoDependentTypes _A _B))- Right k -> Right (Const k)-typeWith ctx e@(App f a ) = do- tf <- fmap Dhall.Core.normalize (typeWith ctx f)- (x, _A, _B) <- case tf of- Pi x _A _B -> return (x, _A, _B)- _ -> Left (TypeError ctx e (NotAFunction f tf))- _A' <- typeWith ctx a- if propEqual _A _A'- then do- let a' = Dhall.Core.shift 1 (V x 0) a- let _B' = Dhall.Core.subst (V x 0) a' _B- let _B'' = Dhall.Core.shift (-1) (V x 0) _B'- return _B''- else do- let nf_A = Dhall.Core.normalize _A- let nf_A' = Dhall.Core.normalize _A'- Left (TypeError ctx e (TypeMismatch f nf_A a nf_A'))-typeWith ctx e@(Let f mt r b ) = do- tR <- typeWith ctx r- ttR <- fmap Dhall.Core.normalize (typeWith ctx tR)- kR <- case ttR of- Const k -> return k- -- Don't bother to provide a `let`-specific version of this error- -- message because this should never happen anyway- _ -> Left (TypeError ctx e (InvalidInputType tR))-- let ctx' = Dhall.Context.insert f tR ctx- tB <- typeWith ctx' b- ttB <- fmap Dhall.Core.normalize (typeWith ctx tB)- kB <- case ttB of- Const k -> return k- -- Don't bother to provide a `let`-specific version of this error- -- message because this should never happen anyway- _ -> Left (TypeError ctx e (InvalidOutputType tB))-- case rule kR kB of- Left () -> Left (TypeError ctx e (NoDependentLet tR tB))- Right _ -> return ()-- case mt of- Nothing -> do- return ()- Just t -> do- let nf_t = Dhall.Core.normalize t- let nf_tR = Dhall.Core.normalize tR- if propEqual nf_tR nf_t- then return ()- else Left (TypeError ctx e (AnnotMismatch r nf_t nf_tR))-- return tB-typeWith ctx e@(Annot x t ) = do- -- This is mainly just to check that `t` is not `Kind`- _ <- typeWith ctx t-- t' <- typeWith ctx x- if propEqual t t'- then do- return t- else do- let nf_t = Dhall.Core.normalize t- let nf_t' = Dhall.Core.normalize t'- Left (TypeError ctx e (AnnotMismatch x nf_t nf_t'))-typeWith _ Bool = do- return (Const Type)-typeWith _ (BoolLit _ ) = do- return Bool-typeWith ctx e@(BoolAnd l r ) = do- tl <- fmap Dhall.Core.normalize (typeWith ctx l)- case tl of- Bool -> return ()- _ -> Left (TypeError ctx e (CantAnd l tl))-- tr <- fmap Dhall.Core.normalize (typeWith ctx r)- case tr of- Bool -> return ()- _ -> Left (TypeError ctx e (CantAnd r tr))-- return Bool-typeWith ctx e@(BoolOr l r ) = do- tl <- fmap Dhall.Core.normalize (typeWith ctx l)- case tl of- Bool -> return ()- _ -> Left (TypeError ctx e (CantOr l tl))-- tr <- fmap Dhall.Core.normalize (typeWith ctx r)- case tr of- Bool -> return ()- _ -> Left (TypeError ctx e (CantOr r tr))-- return Bool-typeWith ctx e@(BoolEQ l r ) = do- tl <- fmap Dhall.Core.normalize (typeWith ctx l)- case tl of- Bool -> return ()- _ -> Left (TypeError ctx e (CantEQ l tl))-- tr <- fmap Dhall.Core.normalize (typeWith ctx r)- case tr of- Bool -> return ()- _ -> Left (TypeError ctx e (CantEQ r tr))-- return Bool-typeWith ctx e@(BoolNE l r ) = do- tl <- fmap Dhall.Core.normalize (typeWith ctx l)- case tl of- Bool -> return ()- _ -> Left (TypeError ctx e (CantNE l tl))-- tr <- fmap Dhall.Core.normalize (typeWith ctx r)- case tr of- Bool -> return ()- _ -> Left (TypeError ctx e (CantNE r tr))-- return Bool-typeWith ctx e@(BoolIf x y z ) = do- tx <- fmap Dhall.Core.normalize (typeWith ctx x)- case tx of- Bool -> return ()- _ -> Left (TypeError ctx e (InvalidPredicate x tx))- ty <- fmap Dhall.Core.normalize (typeWith ctx y )- tty <- fmap Dhall.Core.normalize (typeWith ctx ty)- case tty of- Const Type -> return ()- _ -> Left (TypeError ctx e (IfBranchMustBeTerm True y ty tty))-- tz <- fmap Dhall.Core.normalize (typeWith ctx z)- ttz <- fmap Dhall.Core.normalize (typeWith ctx tz)- case ttz of- Const Type -> return ()- _ -> Left (TypeError ctx e (IfBranchMustBeTerm False z tz ttz))-- if propEqual ty tz- then return ()- else Left (TypeError ctx e (IfBranchMismatch y z ty tz))- return ty-typeWith _ Natural = do- return (Const Type)-typeWith _ (NaturalLit _ ) = do- return Natural-typeWith _ NaturalFold = do- return- (Pi "_" Natural- (Pi "natural" (Const Type)- (Pi "succ" (Pi "_" "natural" "natural")- (Pi "zero" "natural" "natural") ) ) )-typeWith _ NaturalBuild = do- return- (Pi "_"- (Pi "natural" (Const Type)- (Pi "succ" (Pi "_" "natural" "natural")- (Pi "zero" "natural" "natural") ) )- Natural )-typeWith _ NaturalIsZero = do- return (Pi "_" Natural Bool)-typeWith _ NaturalEven = do- return (Pi "_" Natural Bool)-typeWith _ NaturalOdd = do- return (Pi "_" Natural Bool)-typeWith _ NaturalToInteger = do- return (Pi "_" Natural Integer)-typeWith _ NaturalShow = do- return (Pi "_" Natural Text)-typeWith ctx e@(NaturalPlus l r) = do- tl <- fmap Dhall.Core.normalize (typeWith ctx l)- case tl of- Natural -> return ()- _ -> Left (TypeError ctx e (CantAdd l tl))-- tr <- fmap Dhall.Core.normalize (typeWith ctx r)- case tr of- Natural -> return ()- _ -> Left (TypeError ctx e (CantAdd r tr))- return Natural-typeWith ctx e@(NaturalTimes l r) = do- tl <- fmap Dhall.Core.normalize (typeWith ctx l)- case tl of- Natural -> return ()- _ -> Left (TypeError ctx e (CantMultiply l tl))-- tr <- fmap Dhall.Core.normalize (typeWith ctx r)- case tr of- Natural -> return ()- _ -> Left (TypeError ctx e (CantMultiply r tr))- return Natural-typeWith _ Integer = do- return (Const Type)-typeWith _ (IntegerLit _ ) = do- return Integer-typeWith _ IntegerShow = do- return (Pi "_" Integer Text)-typeWith _ Double = do- return (Const Type)-typeWith _ (DoubleLit _ ) = do- return Double-typeWith _ DoubleShow = do- return (Pi "_" Double Text)-typeWith _ Text = do- return (Const Type)-typeWith _ (TextLit _ ) = do- return Text-typeWith ctx e@(TextAppend l r ) = do- tl <- fmap Dhall.Core.normalize (typeWith ctx l)- case tl of- Text -> return ()- _ -> Left (TypeError ctx e (CantTextAppend l tl))-- tr <- fmap Dhall.Core.normalize (typeWith ctx r)- case tr of- Text -> return ()- _ -> Left (TypeError ctx e (CantTextAppend r tr))- return Text-typeWith _ List = do- return (Pi "_" (Const Type) (Const Type))-typeWith ctx e@(ListLit Nothing xs) = do- if Data.Vector.null xs- then Left (TypeError ctx e MissingListType)- else do- t <- typeWith ctx (Data.Vector.head xs)- s <- fmap Dhall.Core.normalize (typeWith ctx t)- case s of- Const Type -> return ()- _ -> Left (TypeError ctx e (InvalidListType t))- flip Data.Vector.imapM_ xs (\i x -> do- t' <- typeWith ctx x- if propEqual t t'- then return ()- else do- let nf_t = Dhall.Core.normalize t- let nf_t' = Dhall.Core.normalize t'- let err = MismatchedListElements i nf_t x nf_t'- Left (TypeError ctx e err) )- return (App List t)-typeWith ctx e@(ListLit (Just t ) xs) = do- s <- fmap Dhall.Core.normalize (typeWith ctx t)- case s of- Const Type -> return ()- _ -> Left (TypeError ctx e (InvalidListType t))- flip Data.Vector.imapM_ xs (\i x -> do- t' <- typeWith ctx x- if propEqual t t'- then return ()- else do- let nf_t = Dhall.Core.normalize t- let nf_t' = Dhall.Core.normalize t'- Left (TypeError ctx e (InvalidListElement i nf_t x nf_t')) )- return (App List t)-typeWith _ ListBuild = do- return- (Pi "a" (Const Type)- (Pi "_"- (Pi "list" (Const Type)- (Pi "cons" (Pi "_" "a" (Pi "_" "list" "list"))- (Pi "nil" "list" "list") ) )- (App List "a") ) )-typeWith _ ListFold = do- return- (Pi "a" (Const Type)- (Pi "_" (App List "a")- (Pi "list" (Const Type)- (Pi "cons" (Pi "_" "a" (Pi "_" "list" "list"))- (Pi "nil" "list" "list")) ) ) )-typeWith _ ListLength = do- return (Pi "a" (Const Type) (Pi "_" (App List "a") Natural))-typeWith _ ListHead = do- return (Pi "a" (Const Type) (Pi "_" (App List "a") (App Optional "a")))-typeWith _ ListLast = do- return (Pi "a" (Const Type) (Pi "_" (App List "a") (App Optional "a")))-typeWith _ ListIndexed = do- let kts = [("index", Natural), ("value", "a")]- return- (Pi "a" (Const Type)- (Pi "_" (App List "a")- (App List (Record (Data.Map.fromList kts))) ) )-typeWith _ ListReverse = do- return (Pi "a" (Const Type) (Pi "_" (App List "a") (App List "a")))-typeWith _ Optional = do- return (Pi "_" (Const Type) (Const Type))-typeWith ctx e@(OptionalLit t xs) = do- s <- fmap Dhall.Core.normalize (typeWith ctx t)- case s of- Const Type -> return ()- _ -> Left (TypeError ctx e (InvalidOptionalType t))- let n = Data.Vector.length xs- if 2 <= n- then Left (TypeError ctx e (InvalidOptionalLiteral n))- else return ()- forM_ xs (\x -> do- t' <- typeWith ctx x- if propEqual t t'- then return ()- else do- let nf_t = Dhall.Core.normalize t- let nf_t' = Dhall.Core.normalize t'- Left (TypeError ctx e (InvalidOptionalElement nf_t x nf_t')) )- return (App Optional t)-typeWith _ OptionalFold = do- return- (Pi "a" (Const Type)- (Pi "_" (App Optional "a")- (Pi "optional" (Const Type)- (Pi "just" (Pi "_" "a" "optional")- (Pi "nothing" "optional" "optional") ) ) ) )-typeWith _ OptionalBuild = do- return- (Pi "a" (Const Type)- (Pi "_" f (App Optional "a") ) )- where f = Pi "optional" (Const Type)- (Pi "just" (Pi "_" "a" "optional")- (Pi "nothing" "optional" "optional") )-typeWith ctx e@(Record kts ) = do- let process (k, t) = do- s <- fmap Dhall.Core.normalize (typeWith ctx t)- case s of- Const Type -> return ()- _ -> Left (TypeError ctx e (InvalidFieldType k t))- mapM_ process (Data.Map.toList kts)- return (Const Type)-typeWith ctx e@(RecordLit kvs ) = do- let process (k, v) = do- t <- typeWith ctx v- s <- fmap Dhall.Core.normalize (typeWith ctx t)- case s of- Const Type -> return ()- _ -> Left (TypeError ctx e (InvalidField k v))- return (k, t)- kts <- mapM process (Data.Map.toAscList kvs)- return (Record (Data.Map.fromAscList kts))-typeWith ctx e@(Union kts ) = do- let process (k, t) = do- s <- fmap Dhall.Core.normalize (typeWith ctx t)- case s of- Const Type -> return ()- _ -> Left (TypeError ctx e (InvalidAlternativeType k t))- mapM_ process (Data.Map.toList kts)- return (Const Type)-typeWith ctx e@(UnionLit k v kts) = do- case Data.Map.lookup k kts of- Just _ -> Left (TypeError ctx e (DuplicateAlternative k))- Nothing -> return ()- t <- typeWith ctx v- let union = Union (Data.Map.insert k t kts)- _ <- typeWith ctx union- return union-typeWith ctx e@(Combine kvsX kvsY) = do- tKvsX <- fmap Dhall.Core.normalize (typeWith ctx kvsX)- ktsX <- case tKvsX of- Record kts -> return kts- _ -> Left (TypeError ctx e (MustCombineARecord '∧' kvsX tKvsX))-- tKvsY <- fmap Dhall.Core.normalize (typeWith ctx kvsY)- ktsY <- case tKvsY of- Record kts -> return kts- _ -> Left (TypeError ctx e (MustCombineARecord '∧' kvsY tKvsY))-- let combineTypes ktsL ktsR = do- let ks =- Data.Set.union (Data.Map.keysSet ktsL) (Data.Map.keysSet ktsR)- kts <- forM (toList ks) (\k -> do- case (Data.Map.lookup k ktsL, Data.Map.lookup k ktsR) of- (Just (Record ktsL'), Just (Record ktsR')) -> do- t <- combineTypes ktsL' ktsR'- return (k, t)- (Nothing, Just t) -> do- return (k, t)- (Just t, Nothing) -> do- return (k, t)- _ -> do- Left (TypeError ctx e (FieldCollision k)) )- return (Record (Data.Map.fromList kts))-- combineTypes ktsX ktsY-typeWith ctx e@(Prefer kvsX kvsY) = do- tKvsX <- fmap Dhall.Core.normalize (typeWith ctx kvsX)- ktsX <- case tKvsX of- Record kts -> return kts- _ -> Left (TypeError ctx e (MustCombineARecord '⫽' kvsX tKvsX))-- tKvsY <- fmap Dhall.Core.normalize (typeWith ctx kvsY)- ktsY <- case tKvsY of- Record kts -> return kts- _ -> Left (TypeError ctx e (MustCombineARecord '⫽' kvsY tKvsY))- return (Record (Data.Map.union ktsY ktsX))-typeWith ctx e@(Merge kvsX kvsY (Just t)) = do- tKvsX <- fmap Dhall.Core.normalize (typeWith ctx kvsX)- ktsX <- case tKvsX of- Record kts -> return kts- _ -> Left (TypeError ctx e (MustMergeARecord kvsX tKvsX))- let ksX = Data.Map.keysSet ktsX-- tKvsY <- fmap Dhall.Core.normalize (typeWith ctx kvsY)- ktsY <- case tKvsY of- Union kts -> return kts- _ -> Left (TypeError ctx e (MustMergeUnion kvsY tKvsY))- let ksY = Data.Map.keysSet ktsY-- let diffX = Data.Set.difference ksX ksY- let diffY = Data.Set.difference ksY ksX-- if Data.Set.null diffX- then return ()- else Left (TypeError ctx e (UnusedHandler diffX))-- let process (kY, tY) = do- case Data.Map.lookup kY ktsX of- Nothing -> Left (TypeError ctx e (MissingHandler diffY))- Just tX ->- case tX of- Pi _ tY' t' -> do- if propEqual tY tY'- then return ()- else Left (TypeError ctx e (HandlerInputTypeMismatch kY tY tY'))- if propEqual t t'- then return ()- else Left (TypeError ctx e (InvalidHandlerOutputType kY t t'))- _ -> Left (TypeError ctx e (HandlerNotAFunction kY tX))- mapM_ process (Data.Map.toList ktsY)- return t-typeWith ctx e@(Merge kvsX kvsY Nothing) = do- tKvsX <- fmap Dhall.Core.normalize (typeWith ctx kvsX)- ktsX <- case tKvsX of- Record kts -> return kts- _ -> Left (TypeError ctx e (MustMergeARecord kvsX tKvsX))- let ksX = Data.Map.keysSet ktsX-- tKvsY <- fmap Dhall.Core.normalize (typeWith ctx kvsY)- ktsY <- case tKvsY of- Union kts -> return kts- _ -> Left (TypeError ctx e (MustMergeUnion kvsY tKvsY))- let ksY = Data.Map.keysSet ktsY-- let diffX = Data.Set.difference ksX ksY- let diffY = Data.Set.difference ksY ksX-- if Data.Set.null diffX- then return ()- else Left (TypeError ctx e (UnusedHandler diffX))-- (kX, t) <- case Data.Map.assocs ktsX of- [] -> Left (TypeError ctx e MissingMergeType)- (kX, Pi _ _ t):_ -> return (kX, t)- (kX, tX ):_ -> Left (TypeError ctx e (HandlerNotAFunction kX tX))- let process (kY, tY) = do- case Data.Map.lookup kY ktsX of- Nothing -> Left (TypeError ctx e (MissingHandler diffY))- Just tX ->- case tX of- Pi _ tY' t' -> do- if propEqual tY tY'- then return ()- else Left (TypeError ctx e (HandlerInputTypeMismatch kY tY tY'))- if propEqual t t'- then return ()- else Left (TypeError ctx e (HandlerOutputTypeMismatch kX t kY t'))- _ -> Left (TypeError ctx e (HandlerNotAFunction kY tX))- mapM_ process (Data.Map.toList ktsY)- return t-typeWith ctx e@(Field r x ) = do- t <- fmap Dhall.Core.normalize (typeWith ctx r)- case t of- Record kts ->- case Data.Map.lookup x kts of- Just t' -> return t'- Nothing -> Left (TypeError ctx e (MissingField x t))- _ -> Left (TypeError ctx e (NotARecord x r t))-typeWith ctx (Note s e' ) = case typeWith ctx e' of- Left (TypeError ctx' (Note s' e'') m) -> Left (TypeError ctx' (Note s' e'') m)- Left (TypeError ctx' e'' m) -> Left (TypeError ctx' (Note s e'') m)- Right r -> Right r-typeWith _ (Embed p ) = do- absurd p--{-| `typeOf` is the same as `typeWith` with an empty context, meaning that the- expression must be closed (i.e. no free variables), otherwise type-checking- will fail.--}-typeOf :: Expr s X -> Either (TypeError s) (Expr s X)-typeOf = typeWith Dhall.Context.empty---- | Like `Data.Void.Void`, except with a shorter inferred type-newtype X = X { absurd :: forall a . a }--instance Show X where- show = absurd--instance Eq X where- _ == _ = True--instance Buildable X where- build = absurd---- | The specific type error-data TypeMessage s- = UnboundVariable- | InvalidInputType (Expr s X)- | InvalidOutputType (Expr s X)- | NotAFunction (Expr s X) (Expr s X)- | TypeMismatch (Expr s X) (Expr s X) (Expr s X) (Expr s X)- | AnnotMismatch (Expr s X) (Expr s X) (Expr s X)- | Untyped- | MissingListType- | MismatchedListElements Int (Expr s X) (Expr s X) (Expr s X)- | InvalidListElement Int (Expr s X) (Expr s X) (Expr s X)- | InvalidListType (Expr s X)- | InvalidOptionalElement (Expr s X) (Expr s X) (Expr s X)- | InvalidOptionalLiteral Int- | InvalidOptionalType (Expr s X)- | InvalidPredicate (Expr s X) (Expr s X)- | IfBranchMismatch (Expr s X) (Expr s X) (Expr s X) (Expr s X)- | IfBranchMustBeTerm Bool (Expr s X) (Expr s X) (Expr s X)- | InvalidField Text (Expr s X)- | InvalidFieldType Text (Expr s X)- | InvalidAlternative Text (Expr s X)- | InvalidAlternativeType Text (Expr s X)- | DuplicateAlternative Text- | MustCombineARecord Char (Expr s X) (Expr s X)- | FieldCollision Text- | MustMergeARecord (Expr s X) (Expr s X)- | MustMergeUnion (Expr s X) (Expr s X)- | UnusedHandler (Set Text)- | MissingHandler (Set Text)- | HandlerInputTypeMismatch Text (Expr s X) (Expr s X)- | HandlerOutputTypeMismatch Text (Expr s X) Text (Expr s X)- | InvalidHandlerOutputType Text (Expr s X) (Expr s X)- | MissingMergeType- | HandlerNotAFunction Text (Expr s X)- | NotARecord Text (Expr s X) (Expr s X)- | MissingField Text (Expr s X)- | CantAnd (Expr s X) (Expr s X)- | CantOr (Expr s X) (Expr s X)- | CantEQ (Expr s X) (Expr s X)- | CantNE (Expr s X) (Expr s X)- | CantTextAppend (Expr s X) (Expr s X)- | CantAdd (Expr s X) (Expr s X)- | CantMultiply (Expr s X) (Expr s X)- | NoDependentLet (Expr s X) (Expr s X)- | NoDependentTypes (Expr s X) (Expr s X)- deriving (Show)--shortTypeMessage :: TypeMessage s -> Builder-shortTypeMessage msg =- "\ESC[1;31mError\ESC[0m: " <> build short <> "\n"- where- ErrorMessages {..} = prettyTypeMessage msg--longTypeMessage :: TypeMessage s -> Builder-longTypeMessage msg =- "\ESC[1;31mError\ESC[0m: " <> build short <> "\n"- <> "\n"- <> long- where- ErrorMessages {..} = prettyTypeMessage msg--data ErrorMessages = ErrorMessages- { short :: Builder- -- ^ Default succinct 1-line explanation of what went wrong- , long :: Builder- -- ^ Longer and more detailed explanation of the error- }--_NOT :: Data.Text.Text-_NOT = "\ESC[1mnot\ESC[0m"--prettyTypeMessage :: TypeMessage s -> ErrorMessages-prettyTypeMessage UnboundVariable = ErrorMessages {..}- where- short = "Unbound variable"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: Expressions can only reference previously introduced (i.e. "bound")-variables that are still "in scope"--For example, the following valid expressions introduce a "bound" variable named-❰x❱:--- ┌─────────────────┐- │ λ(x : Bool) → x │ Anonymous functions introduce "bound" variables- └─────────────────┘- ⇧- This is the bound variable--- ┌─────────────────┐- │ let x = 1 in x │ ❰let❱ expressions introduce "bound" variables- └─────────────────┘- ⇧- This is the bound variable---However, the following expressions are not valid because they all reference a-variable that has not been introduced yet (i.e. an "unbound" variable):--- ┌─────────────────┐- │ λ(x : Bool) → y │ The variable ❰y❱ hasn't been introduced yet- └─────────────────┘- ⇧- This is the unbound variable--- ┌──────────────────────────┐- │ (let x = True in x) && x │ ❰x❱ is undefined outside the parentheses- └──────────────────────────┘- ⇧- This is the unbound variable--- ┌────────────────┐- │ let x = x in x │ The definition for ❰x❱ cannot reference itself- └────────────────┘- ⇧- This is the unbound variable---Some common reasons why you might get this error:--● You misspell a variable name, like this:--- ┌────────────────────────────────────────────────────┐- │ λ(empty : Bool) → if emty then "Empty" else "Full" │- └────────────────────────────────────────────────────┘- ⇧- Typo---● You misspell a reserved identifier, like this:--- ┌──────────────────────────┐- │ foral (a : Type) → a → a │- └──────────────────────────┘- ⇧- Typo---● You tried to define a recursive value, like this:--- ┌─────────────────────┐- │ let x = x + +1 in x │- └─────────────────────┘- ⇧- Recursive definitions are not allowed---● You accidentally forgot a ❰λ❱ or ❰∀❱/❰forall❱--- Unbound variable- ⇩- ┌─────────────────┐- │ (x : Bool) → x │- └─────────────────┘- ⇧- A ❰λ❱ here would transform this into a valid anonymous function --- Unbound variable- ⇩- ┌────────────────────┐- │ (x : Bool) → Bool │- └────────────────────┘- ⇧- A ❰∀❱ or ❰forall❱ here would transform this into a valid function type-|]--prettyTypeMessage (InvalidInputType expr) = ErrorMessages {..}- where- short = "Invalid function input"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: A function can accept an input "term" that has a given "type", like-this:--- This is the input term that the function accepts- ⇩- ┌───────────────────────┐- │ ∀(x : Natural) → Bool │ This is the type of a function that accepts an- └───────────────────────┘ input term named ❰x❱ that has type ❰Natural❱- ⇧- This is the type of the input term--- ┌────────────────┐- │ Bool → Integer │ This is the type of a function that accepts an anonymous- └────────────────┘ input term that has type ❰Bool❱- ⇧- This is the type of the input term---... or a function can accept an input "type" that has a given "kind", like this:--- This is the input type that the function accepts- ⇩- ┌────────────────────┐- │ ∀(a : Type) → Type │ This is the type of a function that accepts an input- └────────────────────┘ type named ❰a❱ that has kind ❰Type❱- ⇧- This is the kind of the input type--- ┌──────────────────────┐- │ (Type → Type) → Type │ This is the type of a function that accepts an- └──────────────────────┘ anonymous input type that has kind ❰Type → Type❱- ⇧- This is the kind of the input type---Other function inputs are $_NOT valid, like this:--- ┌──────────────┐- │ ∀(x : 1) → x │ ❰1❱ is a "term" and not a "type" nor a "kind" so ❰x❱- └──────────────┘ cannot have "type" ❰1❱ or "kind" ❰1❱- ⇧- This is not a type or kind--- ┌──────────┐- │ True → x │ ❰True❱ is a "term" and not a "type" nor a "kind" so the- └──────────┘ anonymous input cannot have "type" ❰True❱ or "kind" ❰True❱- ⇧- This is not a type or kind---You annotated a function input with the following expression:--↳ $txt--... which is neither a type nor a kind-|]- where- txt = Text.toStrict (Dhall.Core.pretty expr)--prettyTypeMessage (InvalidOutputType expr) = ErrorMessages {..}- where- short = "Invalid function output"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: A function can return an output "term" that has a given "type",-like this:--- ┌────────────────────┐- │ ∀(x : Text) → Bool │ This is the type of a function that returns an- └────────────────────┘ output term that has type ❰Bool❱- ⇧- This is the type of the output term--- ┌────────────────┐- │ Bool → Integer │ This is the type of a function that returns an output- └────────────────┘ term that has type ❰Int❱- ⇧- This is the type of the output term---... or a function can return an output "type" that has a given "kind", like-this:-- ┌────────────────────┐- │ ∀(a : Type) → Type │ This is the type of a function that returns an- └────────────────────┘ output type that has kind ❰Type❱- ⇧- This is the kind of the output type--- ┌──────────────────────┐- │ (Type → Type) → Type │ This is the type of a function that returns an- └──────────────────────┘ output type that has kind ❰Type❱- ⇧- This is the kind of the output type---Other outputs are $_NOT valid, like this:--- ┌─────────────────┐- │ ∀(x : Bool) → x │ ❰x❱ is a "term" and not a "type" nor a "kind" so the- └─────────────────┘ output cannot have "type" ❰x❱ or "kind" ❰x❱- ⇧- This is not a type or kind--- ┌─────────────┐- │ Text → True │ ❰True❱ is a "term" and not a "type" nor a "kind" so the- └─────────────┘ output cannot have "type" ❰True❱ or "kind" ❰True❱- ⇧- This is not a type or kind---Some common reasons why you might get this error:--● You use ❰∀❱ instead of ❰λ❱ by mistake, like this:--- ┌────────────────┐- │ ∀(x: Bool) → x │- └────────────────┘- ⇧- Using ❰λ❱ here instead of ❰∀❱ would transform this into a valid function---────────────────────────────────────────────────────────────────────────────────--You specified that your function outputs a:--↳ $txt--... which is neither a type nor a kind:-|]- where- txt = Text.toStrict (Dhall.Core.pretty expr)--prettyTypeMessage (NotAFunction expr0 expr1) = ErrorMessages {..}- where- short = "Not a function"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: Expressions separated by whitespace denote function application,-like this:--- ┌─────┐- │ f x │ This denotes the function ❰f❱ applied to an argument named ❰x❱ - └─────┘---A function is a term that has type ❰a → b❱ for some ❰a❱ or ❰b❱. For example,-the following expressions are all functions because they have a function type:--- The function's input type is ❰Bool❱- ⇩- ┌───────────────────────────────┐- │ λ(x : Bool) → x : Bool → Bool │ User-defined anonymous function- └───────────────────────────────┘- ⇧- The function's output type is ❰Bool❱--- The function's input type is ❰Natural❱- ⇩- ┌───────────────────────────────┐- │ Natural/even : Natural → Bool │ Built-in function- └───────────────────────────────┘- ⇧- The function's output type is ❰Bool❱--- The function's input kind is ❰Type❱- ⇩- ┌───────────────────────────────┐- │ λ(a : Type) → a : Type → Type │ Type-level functions are still functions- └───────────────────────────────┘- ⇧- The function's output kind is ❰Type❱--- The function's input kind is ❰Type❱- ⇩- ┌────────────────────┐- │ List : Type → Type │ Built-in type-level function- └────────────────────┘- ⇧- The function's output kind is ❰Type❱--- Function's input has kind ❰Type❱- ⇩- ┌─────────────────────────────────────────────────┐- │ List/head : ∀(a : Type) → (List a → Optional a) │ A function can return- └─────────────────────────────────────────────────┘ another function- ⇧- Function's output has type ❰List a → Optional a❱--- The function's input type is ❰List Text❱- ⇩- ┌────────────────────────────────────────────┐- │ List/head Text : List Text → Optional Text │ A function applied to an- └────────────────────────────────────────────┘ argument can be a function- ⇧- The function's output type is ❰Optional Text❱---An expression is not a function if the expression's type is not of the form-❰a → b❱. For example, these are $_NOT functions:--- ┌─────────────┐- │ 1 : Integer │ ❰1❱ is not a function because ❰Integer❱ is not the type of- └─────────────┘ a function--- ┌────────────────────────┐- │ Natural/even +2 : Bool │ ❰Natural/even +2❱ is not a function because- └────────────────────────┘ ❰Bool❱ is not the type of a function--- ┌──────────────────┐- │ List Text : Type │ ❰List Text❱ is not a function because ❰Type❱ is not- └──────────────────┘ the type of a function---Some common reasons why you might get this error:--● You tried to add two ❰Integer❱s without a space around the ❰+❱, like this:--- ┌─────┐- │ 2+2 │- └─────┘--- The above code is parsed as:--- ┌────────┐- │ 2 (+2) │- └────────┘- ⇧- The compiler thinks that this ❰2❱ is a function whose argument is ❰+2❱--- This is because the ❰+❱ symbol has two meanings: you use ❰+❱ to add two- numbers, but you also can prefix ❰Integer❱ literals with a ❰+❱ to turn them- into ❰Natural❱ literals (like ❰+2❱)-- To fix the code, you need to put spaces around the ❰+❱ and also prefix each- ❰2❱ with a ❰+❱, like this:--- ┌─────────┐- │ +2 + +2 │- └─────────┘--- You can only add ❰Natural❱ numbers, which is why you must also change each- ❰2❱ to ❰+2❱--────────────────────────────────────────────────────────────────────────────────--You tried to use the following expression as a function:--↳ $txt0--... but this expression's type is:--↳ $txt1--... which is not a function type-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty expr1)--prettyTypeMessage (TypeMismatch expr0 expr1 expr2 expr3) = ErrorMessages {..}- where- short = "Wrong type of function argument"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: Every function declares what type or kind of argument to accept--For example:--- ┌───────────────────────────────┐- │ λ(x : Bool) → x : Bool → Bool │ This anonymous function only accepts- └───────────────────────────────┘ arguments that have type ❰Bool❱- ⇧- The function's input type--- ┌───────────────────────────────┐- │ Natural/even : Natural → Bool │ This built-in function only accepts- └───────────────────────────────┘ arguments that have type ❰Natural❱- ⇧- The function's input type--- ┌───────────────────────────────┐- │ λ(a : Type) → a : Type → Type │ This anonymous function only accepts- └───────────────────────────────┘ arguments that have kind ❰Type❱- ⇧- The function's input kind--- ┌────────────────────┐- │ List : Type → Type │ This built-in function only accepts arguments that- └────────────────────┘ have kind ❰Type❱- ⇧- The function's input kind---For example, the following expressions are valid:--- ┌────────────────────────┐- │ (λ(x : Bool) → x) True │ ❰True❱ has type ❰Bool❱, which matches the type- └────────────────────────┘ of argument that the anonymous function accepts--- ┌─────────────────┐- │ Natural/even +2 │ ❰+2❱ has type ❰Natural❱, which matches the type of- └─────────────────┘ argument that the ❰Natural/even❱ function accepts,--- ┌────────────────────────┐- │ (λ(a : Type) → a) Bool │ ❰Bool❱ has kind ❰Type❱, which matches the kind- └────────────────────────┘ of argument that the anonymous function accepts--- ┌───────────┐- │ List Text │ ❰Text❱ has kind ❰Type❱, which matches the kind of argument- └───────────┘ that that the ❰List❱ function accepts---However, you can $_NOT apply a function to the wrong type or kind of argument--For example, the following expressions are not valid:--- ┌───────────────────────┐- │ (λ(x : Bool) → x) "A" │ ❰"A"❱ has type ❰Text❱, but the anonymous function- └───────────────────────┘ expects an argument that has type ❰Bool❱--- ┌──────────────────┐- │ Natural/even "A" │ ❰"A"❱ has type ❰Text❱, but the ❰Natural/even❱ function- └──────────────────┘ expects an argument that has type ❰Natural❱--- ┌────────────────────────┐- │ (λ(a : Type) → a) True │ ❰True❱ has type ❰Bool❱, but the anonymous- └────────────────────────┘ function expects an argument of kind ❰Type❱--- ┌────────┐- │ List 1 │ ❰1❱ has type ❰Integer❱, but the ❰List❱ function expects an- └────────┘ argument that has kind ❰Type❱---Some common reasons why you might get this error:--● You omit a function argument by mistake:--- ┌───────────────────────┐- │ List/head [1, 2, 3] │- └───────────────────────┘- ⇧- ❰List/head❱ is missing the first argument,- which should be: ❰Integer❱---● You supply an ❰Integer❱ literal to a function that expects a ❰Natural❱--- ┌────────────────┐- │ Natural/even 2 │- └────────────────┘- ⇧- This should be ❰+2❱---────────────────────────────────────────────────────────────────────────────────--You tried to invoke the following function:--↳ $txt0--... which expects an argument of type or kind:--↳ $txt1--... on the following argument:--↳ $txt2--... which has a different type or kind:--↳ $txt3-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty expr1)- txt2 = Text.toStrict (Dhall.Core.pretty expr2)- txt3 = Text.toStrict (Dhall.Core.pretty expr3)--prettyTypeMessage (AnnotMismatch expr0 expr1 expr2) = ErrorMessages {..}- where- short = "Expression doesn't match annotation"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: You can annotate an expression with its type or kind using the-❰:❱ symbol, like this:--- ┌───────┐- │ x : t │ ❰x❱ is an expression and ❰t❱ is the annotated type or kind of ❰x❱- └───────┘--The type checker verifies that the expression's type or kind matches the-provided annotation--For example, all of the following are valid annotations that the type checker-accepts:--- ┌─────────────┐- │ 1 : Integer │ ❰1❱ is an expression that has type ❰Integer❱, so the type- └─────────────┘ checker accepts the annotation--- ┌────────────────────────┐- │ Natural/even +2 : Bool │ ❰Natural/even +2❱ has type ❰Bool❱, so the type- └────────────────────────┘ checker accepts the annotation--- ┌────────────────────┐- │ List : Type → Type │ ❰List❱ is an expression that has kind ❰Type → Type❱,- └────────────────────┘ so the type checker accepts the annotation--- ┌──────────────────┐- │ List Text : Type │ ❰List Text❱ is an expression that has kind ❰Type❱, so- └──────────────────┘ the type checker accepts the annotation---However, the following annotations are $_NOT valid and the type checker will-reject them:--- ┌──────────┐- │ 1 : Text │ The type checker rejects this because ❰1❱ does not have type- └──────────┘ ❰Text❱--- ┌─────────────┐- │ List : Type │ ❰List❱ does not have kind ❰Type❱- └─────────────┘---Some common reasons why you might get this error:--● The Haskell Dhall interpreter implicitly inserts a top-level annotation- matching the expected type-- For example, if you run the following Haskell code:--- ┌───────────────────────────────┐- │ >>> input auto "1" :: IO Text │- └───────────────────────────────┘--- ... then the interpreter will actually type check the following annotated- expression:--- ┌──────────┐- │ 1 : Text │- └──────────┘--- ... and then type-checking will fail--────────────────────────────────────────────────────────────────────────────────--You or the interpreter annotated this expression:--↳ $txt0--... with this type or kind:--↳ $txt1--... but the inferred type or kind of the expression is actually:--↳ $txt2-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty expr1)- txt2 = Text.toStrict (Dhall.Core.pretty expr2)--prettyTypeMessage Untyped = ErrorMessages {..}- where- short = "❰Kind❱ has no type or kind"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: There are four levels of expressions that form a heirarchy:--● terms-● types-● kinds-● sorts--The following example illustrates this heirarchy:-- ┌────────────────────────────┐- │ "ABC" : Text : Type : Kind │- └────────────────────────────┘- ⇧ ⇧ ⇧ ⇧- term type kind sort--There is nothing above ❰Kind❱ in this hierarchy, so if you try to type check any-expression containing ❰Kind❱ anywhere in the expression then type checking fails--Some common reasons why you might get this error:--● You supplied a kind where a type was expected-- For example, the following expression will fail to type check:-- ┌────────────────┐- │ [] : List Type │- └────────────────┘- ⇧- ❰Type❱ is a kind, not a type-|]--prettyTypeMessage (InvalidPredicate expr0 expr1) = ErrorMessages {..}- where- short = "Invalid predicate for ❰if❱"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: Every ❰if❱ expression begins with a predicate which must have type-❰Bool❱--For example, these are valid ❰if❱ expressions:--- ┌──────────────────────────────┐- │ if True then "Yes" else "No" │- └──────────────────────────────┘- ⇧- Predicate--- ┌─────────────────────────────────────────┐- │ λ(x : Bool) → if x then False else True │- └─────────────────────────────────────────┘- ⇧- Predicate---... but these are $_NOT valid ❰if❱ expressions:--- ┌───────────────────────────┐- │ if 0 then "Yes" else "No" │ ❰0❱ does not have type ❰Bool❱- └───────────────────────────┘--- ┌────────────────────────────┐- │ if "" then False else True │ ❰""❱ does not have type ❰Bool❱- └────────────────────────────┘---Some common reasons why you might get this error:--● You might be used to other programming languages that accept predicates other- than ❰Bool❱-- For example, some languages permit ❰0❱ or ❰""❱ as valid predicates and treat- them as equivalent to ❰False❱. However, the Dhall language does not permit- this--────────────────────────────────────────────────────────────────────────────────--Your ❰if❱ expression begins with the following predicate:--↳ $txt0--... that has type:--↳ $txt1--... but the predicate must instead have type ❰Bool❱-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty expr1)--prettyTypeMessage (IfBranchMustBeTerm b expr0 expr1 expr2) =- ErrorMessages {..}- where- short = "❰if❱ branch is not a term"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: Every ❰if❱ expression has a ❰then❱ and ❰else❱ branch, each of which-is an expression:--- Expression for ❰then❱ branch- ⇩- ┌────────────────────────────────┐- │ if True then "Hello, world!" │- │ else "Goodbye, world!" │- └────────────────────────────────┘- ⇧- Expression for ❰else❱ branch---These expressions must be a "term", where a "term" is defined as an expression-that has a type thas has kind ❰Type❱--For example, the following expressions are all valid "terms":--- ┌────────────────────┐- │ 1 : Integer : Type │ ❰1❱ is a term with a type (❰Integer❱) of kind ❰Type❱- └────────────────────┘- ⇧- term--- ┌─────────────────────────────────────┐- │ Natural/odd : Natural → Bool : Type │ ❰Natural/odd❱ is a term with a type- └─────────────────────────────────────┘ (❰Natural → Bool❱) of kind ❰Type❱- ⇧- term---However, the following expressions are $_NOT valid terms:--- ┌────────────────────┐- │ Text : Type : Kind │ ❰Text❱ has kind (❰Type❱) of sort ❰Kind❱ and is- └────────────────────┘ therefore not a term- ⇧- type--- ┌───────────────────────────┐- │ List : Type → Type : Kind │ ❰List❱ has kind (❰Type → Type❱) of sort- └───────────────────────────┘ ❰Kind❱ and is therefore not a term- ⇧- type-level function---This means that you cannot define an ❰if❱ expression that returns a type. For-example, the following ❰if❱ expression is $_NOT valid:--- ┌─────────────────────────────┐- │ if True then Text else Bool │ Invalid ❰if❱ expression- └─────────────────────────────┘- ⇧ ⇧- type type---Your ❰$txt0❱ branch of your ❰if❱ expression is:--↳ $txt1--... which has kind:--↳ $txt2--... of sort:--↳ $txt3--... and is not a term. Therefore your ❰if❱ expression is not valid-|]- where- txt0 = if b then "then" else "else"- txt1 = Text.toStrict (Dhall.Core.pretty expr0)- txt2 = Text.toStrict (Dhall.Core.pretty expr1)- txt3 = Text.toStrict (Dhall.Core.pretty expr2)--prettyTypeMessage (IfBranchMismatch expr0 expr1 expr2 expr3) =- ErrorMessages {..}- where- short = "❰if❱ branches must have matching types"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: Every ❰if❱ expression has a ❰then❱ and ❰else❱ branch, each of which-is an expression:--- Expression for ❰then❱ branch- ⇩- ┌────────────────────────────────┐- │ if True then "Hello, world!" │- │ else "Goodbye, world!" │- └────────────────────────────────┘- ⇧- Expression for ❰else❱ branch---These two expressions must have the same type. For example, the following ❰if❱-expressions are all valid:--- ┌──────────────────────────────────┐- │ λ(b : Bool) → if b then 0 else 1 │ Both branches have type ❰Integer❱- └──────────────────────────────────┘--- ┌────────────────────────────┐- │ λ(b : Bool) → │- │ if b then Natural/even │ Both branches have type ❰Natural → Bool❱- │ else Natural/odd │- └────────────────────────────┘---However, the following expression is $_NOT valid:--- This branch has type ❰Integer❱- ⇩- ┌────────────────────────┐- │ if True then 0 │- │ else "ABC" │- └────────────────────────┘- ⇧- This branch has type ❰Text❱---The ❰then❱ and ❰else❱ branches must have matching types, even if the predicate is-always ❰True❱ or ❰False❱--Your ❰if❱ expression has the following ❰then❱ branch:--↳ $txt0--... which has type:--↳ $txt2--... and the following ❰else❱ branch:--↳ $txt1--... which has a different type:--↳ $txt3--Fix your ❰then❱ and ❰else❱ branches to have matching types-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty expr1)- txt2 = Text.toStrict (Dhall.Core.pretty expr2)- txt3 = Text.toStrict (Dhall.Core.pretty expr3)--prettyTypeMessage (InvalidListType expr0) = ErrorMessages {..}- where- short = "Invalid type for ❰List❱ elements"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: ❰List❱s can optionally document the type of their elements with a-type annotation, like this:--- ┌──────────────────────────┐- │ [1, 2, 3] : List Integer │ A ❰List❱ of three ❰Integer❱s- └──────────────────────────┘- ⇧- The type of the ❰List❱'s elements, which are ❰Integer❱s--- ┌───────────────────┐- │ [] : List Integer │ An empty ❰List❱- └───────────────────┘- ⇧- You must specify the type when the ❰List❱ is empty---The element type must be a type and not something else. For example, the-following element types are $_NOT valid:--- ┌──────────────┐- │ ... : List 1 │- └──────────────┘- ⇧- This is an ❰Integer❱ and not a ❰Type❱--- ┌─────────────────┐- │ ... : List Type │- └─────────────────┘- ⇧- This is a ❰Kind❱ and not a ❰Type❱---You declared that the ❰List❱'s elements should have type:--↳ $txt0--... which is not a ❰Type❱-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)--prettyTypeMessage MissingListType = do- ErrorMessages {..}- where- short = "An empty list requires a type annotation"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: Lists do not require a type annotation if they have at least one-element:--- ┌───────────┐- │ [1, 2, 3] │ The compiler can infer that this list has type ❰List Integer❱- └───────────┘---However, empty lists still require a type annotation:--- ┌───────────────────┐- │ [] : List Integer │ This type annotation is mandatory- └───────────────────┘---You cannot supply an empty list without a type annotation-|]--prettyTypeMessage (MismatchedListElements i expr0 expr1 expr2) =- ErrorMessages {..}- where- short = "List elements should have the same type"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: Every element in a list must have the same type--For example, this is a valid ❰List❱:--- ┌───────────┐- │ [1, 2, 3] │ Every element in this ❰List❱ is an ❰Integer❱- └───────────┘---.. but this is $_NOT a valid ❰List❱:--- ┌───────────────┐- │ [1, "ABC", 3] │ The first and second element have different types- └───────────────┘---Your first ❰List❱ elements has this type:--↳ $txt0--... but the following element at index $txt1:--↳ $txt2--... has this type instead:--↳ $txt3-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty i )- txt2 = Text.toStrict (Dhall.Core.pretty expr1)- txt3 = Text.toStrict (Dhall.Core.pretty expr2)--prettyTypeMessage (InvalidListElement i expr0 expr1 expr2) =- ErrorMessages {..}- where- short = "List element has the wrong type"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: Every element in the list must have a type matching the type-annotation at the end of the list--For example, this is a valid ❰List❱:--- ┌──────────────────────────┐- │ [1, 2, 3] : List Integer │ Every element in this ❰List❱ is an ❰Integer❱- └──────────────────────────┘---.. but this is $_NOT a valid ❰List❱:--- ┌──────────────────────────────┐- │ [1, "ABC", 3] : List Integer │ The second element is not an ❰Integer❱- └──────────────────────────────┘---Your ❰List❱ elements should have this type:--↳ $txt0--... but the following element at index $txt1:--↳ $txt2--... has this type instead:--↳ $txt3-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty i )- txt2 = Text.toStrict (Dhall.Core.pretty expr1)- txt3 = Text.toStrict (Dhall.Core.pretty expr2)--prettyTypeMessage (InvalidOptionalType expr0) = ErrorMessages {..}- where- short = "Invalid type for ❰Optional❱ element"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: Every optional element ends with a type annotation for the element-that might be present, like this:--- ┌────────────────────────┐- │ [1] : Optional Integer │ An optional element that's present- └────────────────────────┘- ⇧- The type of the ❰Optional❱ element, which is an ❰Integer❱--- ┌────────────────────────┐- │ [] : Optional Integer │ An optional element that's absent- └────────────────────────┘- ⇧- You still specify the type even when the element is absent---The element type must be a type and not something else. For example, the-following element types are $_NOT valid:--- ┌──────────────────┐- │ ... : Optional 1 │- └──────────────────┘- ⇧- This is an ❰Integer❱ and not a ❰Type❱--- ┌─────────────────────┐- │ ... : Optional Type │- └─────────────────────┘- ⇧- This is a ❰Kind❱ and not a ❰Type❱---Even if the element is absent you still must specify a valid type--You declared that the ❰Optional❱ element should have type:--↳ $txt0--... which is not a ❰Type❱--|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)--prettyTypeMessage (InvalidOptionalElement expr0 expr1 expr2) = ErrorMessages {..}- where- short = "❰Optional❱ element has the wrong type"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: An ❰Optional❱ element must have a type matching the type annotation--For example, this is a valid ❰Optional❱ value:--- ┌────────────────────────┐- │ [1] : Optional Integer │ ❰1❱ is an ❰Integer❱, which matches the type- └────────────────────────┘---... but this is $_NOT a valid ❰Optional❱ value:--- ┌────────────────────────────┐- │ ["ABC"] : Optional Integer │ ❰"ABC"❱ is not an ❰Integer❱- └────────────────────────────┘---Your ❰Optional❱ element should have this type:--↳ $txt0--... but the element you provided:--↳ $txt1--... has this type instead:--↳ $txt2-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty expr1)- txt2 = Text.toStrict (Dhall.Core.pretty expr2)--prettyTypeMessage (InvalidOptionalLiteral n) = ErrorMessages {..}- where- short = "Multiple ❰Optional❱ elements not allowed"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: The syntax for ❰Optional❱ values resembles the syntax for ❰List❱s:--- ┌───────────────────────┐- │ [] : Optional Integer │ An ❰Optional❱ value which is absent- └───────────────────────┘--- ┌───────────────────────┐- │ [] : List Integer │ An empty (0-element) ❰List❱- └───────────────────────┘--- ┌────────────────────────┐- │ [1] : Optional Integer │ An ❰Optional❱ value which is present- └────────────────────────┘--- ┌────────────────────────┐- │ [1] : List Integer │ A singleton (1-element) ❰List❱- └────────────────────────┘---However, an ❰Optional❱ value can $_NOT have more than one element, whereas a-❰List❱ can have multiple elements:--- ┌───────────────────────────┐- │ [1, 2] : Optional Integer │ Invalid: multiple elements $_NOT allowed- └───────────────────────────┘--- ┌───────────────────────────┐- │ [1, 2] : List Integer │ Valid: multiple elements allowed- └───────────────────────────┘---Some common reasons why you might get this error:--● You accidentally typed ❰Optional❱ when you meant ❰List❱, like this:--- ┌────────────────────────────────────────────────────┐- │ List/length Integer ([1, 2, 3] : Optional Integer) │- └────────────────────────────────────────────────────┘- ⇧- This should be ❰List❱ instead---────────────────────────────────────────────────────────────────────────────────--Your ❰Optional❱ value had this many elements:--↳ $txt0--... when an ❰Optional❱ value can only have at most one element-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty n)--prettyTypeMessage (InvalidFieldType k expr0) = ErrorMessages {..}- where- short = "Invalid field type"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: Every record type documents the type of each field, like this:-- ┌──────────────────────────────────────────────┐- │ { foo : Integer, bar : Integer, baz : Text } │- └──────────────────────────────────────────────┘--However, fields cannot be annotated with expressions other than types--For example, these record types are $_NOT valid:--- ┌────────────────────────────┐- │ { foo : Integer, bar : 1 } │- └────────────────────────────┘- ⇧- ❰1❱ is an ❰Integer❱ and not a ❰Type❱--- ┌───────────────────────────────┐- │ { foo : Integer, bar : Type } │- └───────────────────────────────┘- ⇧- ❰Type❱ is a ❰Kind❱ and not a ❰Type❱---You provided a record type with a key named:--↳ $txt0--... annotated with the following expression:--↳ $txt1--... which is not a type-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty k )- txt1 = Text.toStrict (Dhall.Core.pretty expr0)--prettyTypeMessage (InvalidField k expr0) = ErrorMessages {..}- where- short = "Invalid field"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: Every record literal is a set of fields assigned to values, like-this:-- ┌────────────────────────────────────────┐- │ { foo = 100, bar = True, baz = "ABC" } │- └────────────────────────────────────────┘--However, fields can only be terms and cannot be types or kinds--For example, these record literals are $_NOT valid:--- ┌───────────────────────────┐- │ { foo = 100, bar = Text } │- └───────────────────────────┘- ⇧- ❰Text❱ is a type and not a term--- ┌───────────────────────────┐- │ { foo = 100, bar = Type } │- └───────────────────────────┘- ⇧- ❰Type❱ is a kind and not a term---You provided a record literal with a key named:--↳ $txt0--... whose value is:--↳ $txt1--... which is not a term-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty k )- txt1 = Text.toStrict (Dhall.Core.pretty expr0)--prettyTypeMessage (InvalidAlternativeType k expr0) = ErrorMessages {..}- where- short = "Invalid alternative"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: Every union literal begins by selecting one alternative and-specifying the value for that alternative, like this:--- Select the ❰Left❱ alternative, whose value is ❰True❱- ⇩- ┌──────────────────────────────────┐- │ < Left = True, Right : Natural > │ A union literal with two alternatives- └──────────────────────────────────┘---However, this value must be a term and not a type. For example, the following-values are $_NOT valid:--- ┌──────────────────────────────────┐- │ < Left = Text, Right : Natural > │ Invalid union literal- └──────────────────────────────────┘- ⇧- This is a type and not a term--- ┌───────────────────────────────┐- │ < Left = Type, Right : Type > │ Invalid union type- └───────────────────────────────┘- ⇧- This is a kind and not a term---Some common reasons why you might get this error:--● You accidentally typed ❰=❱ instead of ❰:❱ for a union literal with one- alternative:--- ┌────────────────────┐- │ < Example = Text > │- └────────────────────┘- ⇧- This could be ❰:❱ instead---────────────────────────────────────────────────────────────────────────────────--You provided a union literal with an alternative named:--↳ $txt0--... whose value is:--↳ $txt1--... which is not a term-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty k )- txt1 = Text.toStrict (Dhall.Core.pretty expr0)--prettyTypeMessage (InvalidAlternative k expr0) = ErrorMessages {..}- where- short = "Invalid alternative"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: Every union type specifies the type of each alternative, like this:--- The type of the first alternative is ❰Bool❱- ⇩- ┌──────────────────────────────────┐- │ < Left : Bool, Right : Natural > │ A union type with two alternatives- └──────────────────────────────────┘- ⇧- The type of the second alternative is ❰Natural❱---However, these alternatives can only be annotated with types. For example, the-following union types are $_NOT valid:--- ┌────────────────────────────┐- │ < Left : Bool, Right : 1 > │ Invalid union type- └────────────────────────────┘- ⇧- This is a term and not a type--- ┌───────────────────────────────┐- │ < Left : Bool, Right : Type > │ Invalid union type- └───────────────────────────────┘- ⇧- This is a kind and not a type---Some common reasons why you might get this error:--● You accidentally typed ❰:❱ instead of ❰=❱ for a union literal with one- alternative:-- ┌─────────────────┐- │ < Example : 1 > │- └─────────────────┘- ⇧- This could be ❰=❱ instead---────────────────────────────────────────────────────────────────────────────────--You provided a union type with an alternative named:--↳ $txt0--... annotated with the following expression which is not a type:--↳ $txt1-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty k )- txt1 = Text.toStrict (Dhall.Core.pretty expr0)--prettyTypeMessage (DuplicateAlternative k) = ErrorMessages {..}- where- short = "Duplicate union alternative"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: Unions may not have two alternatives that share the same name--For example, the following expressions are $_NOT valid:--- ┌─────────────────────────────┐- │ < foo = True | foo : Text > │ Invalid: ❰foo❱ appears twice- └─────────────────────────────┘--- ┌───────────────────────────────────────┐- │ < foo = 1 | bar : Bool | bar : Text > │ Invalid: ❰bar❱ appears twice- └───────────────────────────────────────┘---You have more than one alternative named:--↳ $txt0-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty k)--prettyTypeMessage (MustCombineARecord c expr0 expr1) = ErrorMessages {..}- where- short = "You can only combine records"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: You can combine records using the ❰$op❱ operator, like this:--- ┌───────────────────────────────────────────┐- │ { foo = 1, bar = "ABC" } $op { baz = True } │- └───────────────────────────────────────────┘--- ┌─────────────────────────────────────────────┐- │ λ(r : { foo : Bool }) → r $op { bar = "ABC" } │- └─────────────────────────────────────────────┘---... but you cannot combine values that are not records.--For example, the following expressions are $_NOT valid:--- ┌──────────────────────────────┐- │ { foo = 1, bar = "ABC" } $op 1 │- └──────────────────────────────┘- ⇧- Invalid: Not a record--- ┌───────────────────────────────────────────┐- │ { foo = 1, bar = "ABC" } $op { baz : Bool } │- └───────────────────────────────────────────┘- ⇧- Invalid: This is a record type and not a record--- ┌───────────────────────────────────────────┐- │ { foo = 1, bar = "ABC" } $op < baz = True > │- └───────────────────────────────────────────┘- ⇧- Invalid: This is a union and not a record---You tried to combine the following value:--↳ $txt0--... which is not a record, but is actually a:--↳ $txt1-|]- where- op = Data.Text.singleton c- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty expr1)--prettyTypeMessage (FieldCollision k) = ErrorMessages {..}- where- short = "Field collision"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: You can combine records if they don't share any fields in common,-like this:--- ┌───────────────────────────────────────────┐- │ { foo = 1, bar = "ABC" } ∧ { baz = True } │- └───────────────────────────────────────────┘--- ┌────────────────────────────────────────┐- │ λ(r : { baz : Bool}) → { foo = 1 } ∧ r │- └────────────────────────────────────────┘---... but you cannot merge two records that share the same field--For example, the following expression is $_NOT valid:--- ┌───────────────────────────────────────────┐- │ { foo = 1, bar = "ABC" } ∧ { foo = True } │ Invalid: Colliding ❰foo❱ fields- └───────────────────────────────────────────┘---Some common reasons why you might get this error:--● You tried to use ❰∧❱ to update a field's value, like this:--- ┌────────────────────────────────────────┐- │ { foo = 1, bar = "ABC" } ∧ { foo = 2 } │- └────────────────────────────────────────┘- ⇧- Invalid attempt to update ❰foo❱'s value to ❰2❱-- Field updates are intentionally not allowed as the Dhall language discourages- patch-oriented programming--────────────────────────────────────────────────────────────────────────────────--You combined two records that share the following field:--↳ $txt0--... which is not allowed-|]- where- txt0 = Text.toStrict k--prettyTypeMessage (MustMergeARecord expr0 expr1) = ErrorMessages {..}- where- short = "❰merge❱ expects a record of handlers"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: You can ❰merge❱ the alternatives of a union using a record with one-handler per alternative, like this:--- ┌─────────────────────────────────────────────────────────────────────┐- │ let union = < Left = +2 | Right : Bool > │- │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │- │ in merge handlers union : Bool │- └─────────────────────────────────────────────────────────────────────┘---... but the first argument to ❰merge❱ must be a record and not some other type.--For example, the following expression is $_NOT valid:--- ┌─────────────────────────────────────────┐- │ let handler = λ(x : Bool) → x │- │ in merge handler < Foo = True > : True │- └─────────────────────────────────────────┘- ⇧- Invalid: ❰handler❱ isn't a record---Some common reasons why you might get this error:--● You accidentally provide an empty record type instead of an empty record when- you ❰merge❱ an empty union:--- ┌──────────────────────────────────────────┐- │ λ(x : <>) → λ(a : Type) → merge {} x : a │- └──────────────────────────────────────────┘- ⇧- This should be ❰{=}❱ instead---────────────────────────────────────────────────────────────────────────────────--You provided the following handler:--↳ $txt0--... which is not a record, but is actually a value of type:--↳ $txt1-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty expr1)--prettyTypeMessage (MustMergeUnion expr0 expr1) = ErrorMessages {..}- where- short = "❰merge❱ expects a union"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: You can ❰merge❱ the alternatives of a union using a record with one-handler per alternative, like this:--- ┌─────────────────────────────────────────────────────────────────────┐- │ let union = < Left = +2 | Right : Bool > │- │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │- │ in merge handlers union : Bool │- └─────────────────────────────────────────────────────────────────────┘---... but the second argument to ❰merge❱ must be a union and not some other type.--For example, the following expression is $_NOT valid:--- ┌──────────────────────────────────────────┐- │ let handlers = { Foo = λ(x : Bool) → x } │- │ in merge handlers True : True │- └──────────────────────────────────────────┘- ⇧- Invalid: ❰True❱ isn't a union---You tried to ❰merge❱ this expression:--↳ $txt0--... which is not a union, but is actually a value of type:--↳ $txt1-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty expr1)--prettyTypeMessage (UnusedHandler ks) = ErrorMessages {..}- where- short = "Unused handler"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: You can ❰merge❱ the alternatives of a union using a record with one-handler per alternative, like this:--- ┌─────────────────────────────────────────────────────────────────────┐- │ let union = < Left = +2 | Right : Bool > │- │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │- │ in merge handlers union : Bool │- └─────────────────────────────────────────────────────────────────────┘---... but you must provide exactly one handler per alternative in the union. You-cannot supply extra handlers--For example, the following expression is $_NOT valid:--- ┌───────────────────────────────────────┐- │ let union = < Left = +2 > │ The ❰Right❱ alternative is missing- │ in let handlers = │ - │ { Left = Natural/even │- │ , Right = λ(x : Bool) → x │ Invalid: ❰Right❱ handler isn't used- │ } │- │ in merge handlers union : Bool │- └───────────────────────────────────────┘---You provided the following handlers:--↳ $txt0--... which had no matching alternatives in the union you tried to ❰merge❱-|]- where- txt0 = Text.toStrict (Text.intercalate ", " (Data.Set.toList ks))--prettyTypeMessage (MissingHandler ks) = ErrorMessages {..}- where- short = "Missing handler"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: You can ❰merge❱ the alternatives of a union using a record with one-handler per alternative, like this:--- ┌─────────────────────────────────────────────────────────────────────┐- │ let union = < Left = +2 | Right : Bool > │- │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │- │ in merge handlers union : Bool │- └─────────────────────────────────────────────────────────────────────┘---... but you must provide exactly one handler per alternative in the union. You-cannot omit any handlers--For example, the following expression is $_NOT valid:--- Invalid: Missing ❰Right❱ handler- ⇩- ┌─────────────────────────────────────────────────┐- │ let handlers = { Left = Natural/even } │- │ in let union = < Left = +2 | Right : Bool > │- │ in merge handlers union : Bool │- └─────────────────────────────────────────────────┘---Note that you need to provide handlers for other alternatives even if those-alternatives are never used--You need to supply the following handlers:--↳ $txt0-|]- where- txt0 = Text.toStrict (Text.intercalate ", " (Data.Set.toList ks))--prettyTypeMessage MissingMergeType =- ErrorMessages {..}- where- short = "An empty ❰merge❱ requires a type annotation"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: A ❰merge❱ does not require a type annotation if the union has at-least one alternative, like this--- ┌─────────────────────────────────────────────────────────────────────┐- │ let union = < Left = +2 | Right : Bool > │- │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │- │ in merge handlers union │- └─────────────────────────────────────────────────────────────────────┘---However, you must provide a type annotation when merging an empty union:--- ┌────────────────────────────────┐- │ λ(a : <>) → merge {=} a : Bool │- └────────────────────────────────┘- ⇧- This can be any type---You can provide any type at all as the annotation, since merging an empty-union can produce any type of output-|]--prettyTypeMessage (HandlerInputTypeMismatch expr0 expr1 expr2) =- ErrorMessages {..}- where- short = "Wrong handler input type"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: You can ❰merge❱ the alternatives of a union using a record with one-handler per alternative, like this:--- ┌─────────────────────────────────────────────────────────────────────┐- │ let union = < Left = +2 | Right : Bool > │- │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │- │ in merge handlers union : Bool │- └─────────────────────────────────────────────────────────────────────┘---... as long as the input type of each handler function matches the type of the-corresponding alternative:--- ┌───────────────────────────────────────────────────────────┐- │ union : < Left : Natural | Right : Bool > │- └───────────────────────────────────────────────────────────┘- ⇧ ⇧- These must match These must match- ⇩ ⇩- ┌───────────────────────────────────────────────────────────┐- │ handlers : { Left : Natural → Bool, Right : Bool → Bool } │- └───────────────────────────────────────────────────────────┘---For example, the following expression is $_NOT valid:--- Invalid: Doesn't match the type of the ❰Right❱ alternative- ⇩- ┌──────────────────────────────────────────────────────────────────────┐- │ let handlers = { Left = Natural/even | Right = λ(x : Text) → x } │- │ in let union = < Left = +2 | Right : Bool > │- │ in merge handlers union : Bool │- └──────────────────────────────────────────────────────────────────────┘---Your handler for the following alternative:--↳ $txt0--... needs to accept an input value of type:--↳ $txt1--... but actually accepts an input value of a different type:--↳ $txt2-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty expr1)- txt2 = Text.toStrict (Dhall.Core.pretty expr2)--prettyTypeMessage (InvalidHandlerOutputType expr0 expr1 expr2) =- ErrorMessages {..}- where- short = "Wrong handler output type"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: You can ❰merge❱ the alternatives of a union using a record with one-handler per alternative, like this:--- ┌─────────────────────────────────────────────────────────────────────┐- │ let union = < Left = +2 | Right : Bool > │- │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │- │ in merge handlers union : Bool │- └─────────────────────────────────────────────────────────────────────┘---... as long as the output type of each handler function matches the declared type-of the result:--- ┌───────────────────────────────────────────────────────────┐- │ handlers : { Left : Natural → Bool, Right : Bool → Bool } │- └───────────────────────────────────────────────────────────┘- ⇧ ⇧- These output types ...-- ... must match the declared type of the ❰merge❱- ⇩- ┌─────────────────────────────┐- │ merge handlers union : Bool │- └─────────────────────────────┘---For example, the following expression is $_NOT valid:--- ┌──────────────────────────────────────────────────────────────────────┐- │ let union = < Left = +2 | Right : Bool > │- │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │- │ in merge handlers union : Text │- └──────────────────────────────────────────────────────────────────────┘- ⇧- Invalid: Doesn't match output of either handler---Your handler for the following alternative:--↳ $txt0--... needs to return an output value of type:--↳ $txt1--... but actually returns an output value of a different type:--↳ $txt2-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty expr1)- txt2 = Text.toStrict (Dhall.Core.pretty expr2)--prettyTypeMessage (HandlerOutputTypeMismatch key0 expr0 key1 expr1) =- ErrorMessages {..}- where- short = "Handlers should have the same output type"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: You can ❰merge❱ the alternatives of a union using a record with one-handler per alternative, like this:--- ┌─────────────────────────────────────────────────────────────────────┐- │ let union = < Left = +2 | Right : Bool > │- │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │- │ in merge handlers union │- └─────────────────────────────────────────────────────────────────────┘---... as long as the output type of each handler function is the same:--- ┌───────────────────────────────────────────────────────────┐- │ handlers : { Left : Natural → Bool, Right : Bool → Bool } │- └───────────────────────────────────────────────────────────┘- ⇧ ⇧- These output types both match---For example, the following expression is $_NOT valid:--- ┌─────────────────────────────────────────────────┐- │ let union = < Left = +2 | Right : Bool > │- │ in let handlers = │- │ { Left = λ(x : Natural) → x │ This outputs ❰Natural❱- │ , Right = λ(x : Bool ) → x │ This outputs ❰Bool❱- │ } │- │ in merge handlers union │- └─────────────────────────────────────────────────┘- ⇧- Invalid: The handlers in this record don't have matching outputs---The handler for the ❰$txt0❱ alternative has this output type:--↳ $txt1--... but the handler for the ❰$txt2❱ alternative has this output type instead:--↳ $txt3-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty key0 )- txt1 = Text.toStrict (Dhall.Core.pretty expr0)- txt2 = Text.toStrict (Dhall.Core.pretty key1 )- txt3 = Text.toStrict (Dhall.Core.pretty expr1)-prettyTypeMessage (HandlerNotAFunction k expr0) = ErrorMessages {..}- where- short = "Handler is not a function"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: You can ❰merge❱ the alternatives of a union using a record with one-handler per alternative, like this:--- ┌─────────────────────────────────────────────────────────────────────┐- │ let union = < Left = +2 | Right : Bool > │- │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │- │ in merge handlers union : Bool │- └─────────────────────────────────────────────────────────────────────┘---... as long as each handler is a function--For example, the following expression is $_NOT valid:--- ┌─────────────────────────────────────────┐- │ merge { Foo = True } < Foo = 1 > : Bool │- └─────────────────────────────────────────┘- ⇧- Invalid: Not a function---Your handler for this alternative:--↳ $txt0--... has the following type:--↳ $txt1--... which is not the type of a function-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty k)- txt1 = Text.toStrict (Dhall.Core.pretty expr0)--prettyTypeMessage (NotARecord k expr0 expr1) = ErrorMessages {..}- where- short = "Not a record"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: You can only access fields on records, like this:--- ┌─────────────────────────────────┐- │ { foo = True, bar = "ABC" }.foo │ This is valid ...- └─────────────────────────────────┘--- ┌───────────────────────────────────────────┐- │ λ(r : { foo : Bool, bar : Text }) → r.foo │ ... and so is this- └───────────────────────────────────────────┘---... but you cannot access fields on non-record expressions--For example, the following expression is $_NOT valid:--- ┌───────┐- │ 1.foo │- └───────┘- ⇧- Invalid: Not a record---Some common reasons why you might get this error:--● You accidentally try to access a field of a union instead of a record, like- this:--- ┌─────────────────┐- │ < foo : a >.foo │- └─────────────────┘- ⇧- This is a union, not a record---────────────────────────────────────────────────────────────────────────────────--You tried to access a field named:--↳ $txt0--... on the following expression which is not a record:--↳ $txt1--... but is actually an expression of type:--↳ $txt2-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty k )- txt1 = Text.toStrict (Dhall.Core.pretty expr0)- txt2 = Text.toStrict (Dhall.Core.pretty expr1)--prettyTypeMessage (MissingField k expr0) = ErrorMessages {..}- where- short = "Missing record field"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: You can only access fields on records, like this:--- ┌─────────────────────────────────┐- │ { foo = True, bar = "ABC" }.foo │ This is valid ...- └─────────────────────────────────┘--- ┌───────────────────────────────────────────┐- │ λ(r : { foo : Bool, bar : Text }) → r.foo │ ... and so is this- └───────────────────────────────────────────┘---... but you can only access fields if they are present--For example, the following expression is $_NOT valid:-- ┌─────────────────────────────────┐- │ { foo = True, bar = "ABC" }.qux │- └─────────────────────────────────┘- ⇧- Invalid: the record has no ❰qux❱ field--You tried to access a field named:--↳ $txt0--... but the field is missing because the record only defines the following fields:--↳ $txt1-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty k )- txt1 = Text.toStrict (Dhall.Core.pretty expr0)--prettyTypeMessage (CantAnd expr0 expr1) =- buildBooleanOperator "&&" expr0 expr1--prettyTypeMessage (CantOr expr0 expr1) =- buildBooleanOperator "||" expr0 expr1--prettyTypeMessage (CantEQ expr0 expr1) =- buildBooleanOperator "==" expr0 expr1--prettyTypeMessage (CantNE expr0 expr1) =- buildBooleanOperator "/=" expr0 expr1--prettyTypeMessage (CantTextAppend expr0 expr1) = ErrorMessages {..}- where- short = "❰++❱ only works on ❰Text❱"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: The ❰++❱ operator expects two arguments that have type ❰Text❱--For example, this is a valid use of ❰++❱: --- ┌────────────────┐- │ "ABC" ++ "DEF" │- └────────────────┘---Some common reasons why you might get this error:--● You might have thought that ❰++❱ was the operator to combine two lists:--- ┌────────────────────────┐- │ [1, 2, 3] ++ [4, 5, 6] │ Not valid- └────────────────────────┘--- The Dhall programming language does not provide a built-in operator for- combining two lists--────────────────────────────────────────────────────────────────────────────────--You provided this argument:--↳ $txt0--... which does not have type ❰Text❱ but instead has type:--↳ $txt1-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty expr1)--prettyTypeMessage (CantAdd expr0 expr1) =- buildNaturalOperator "+" expr0 expr1--prettyTypeMessage (CantMultiply expr0 expr1) =- buildNaturalOperator "*" expr0 expr1--prettyTypeMessage (NoDependentTypes expr0 expr1) = ErrorMessages {..}- where- short = "No dependent types"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: The Dhall programming language does not allow functions from terms-to types. These function types are also known as "dependent function types"-because you have a type whose value "depends" on the value of a term.--For example, this is $_NOT a legal function type:--- ┌─────────────┐- │ Bool → Type │- └─────────────┘---Similarly, this is $_NOT legal code:--- ┌────────────────────────────────────────────────────┐- │ λ(Vector : Natural → Type → Type) → Vector +0 Text │- └────────────────────────────────────────────────────┘- ⇧- Invalid dependent type---Your function type is invalid because the input has type:--↳ $txt0--... and the output has kind:--↳ $txt1--... which makes this a forbidden dependent function type-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty expr1)--prettyTypeMessage (NoDependentLet expr0 expr1) = ErrorMessages {..}- where- short = "No dependent ❰let❱"-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: The Dhall programming language does not allow ❰let❱ expressions-from terms to types. These ❰let❱ expressions are also known as "dependent ❰let❱-expressions" because you have a type whose value depends on the value of a term.--The Dhall language forbids these dependent ❰let❱ expressions in order to-guarantee that ❰let❱ expressions of the form:--- ┌────────────────────┐- │ let x : t = r in e │- └────────────────────┘---... are always equivalent to:--- ┌──────────────────┐- │ (λ(x : t) → e) r │- └──────────────────┘---This means that both expressions should normalize to the same result and if one-of the two fails to type check then the other should fail to type check, too.--For this reason, the following is $_NOT legal code:--- ┌───────────────────┐- │ let x = 2 in Text │- └───────────────────┘---... because the above ❰let❱ expression is equivalent to:--- ┌─────────────────────────────┐- │ let x : Integer = 2 in Text │- └─────────────────────────────┘---... which in turn must be equivalent to:--- ┌───────────────────────────┐- │ (λ(x : Integer) → Text) 2 │- └───────────────────────────┘---... which in turn fails to type check because this sub-expression:--- ┌───────────────────────┐- │ λ(x : Integer) → Text │- └───────────────────────┘---... has type:--- ┌───────────────────────┐- │ ∀(x : Integer) → Text │- └───────────────────────┘---... which is a forbidden dependent function type (i.e. a function from a term to-a type). Therefore the equivalent ❰let❱ expression is also forbidden.--Your ❰let❱ expression is invalid because the input has type:--↳ $txt0--... and the output has kind:--↳ $txt1--... which makes this a forbidden dependent ❰let❱ expression-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty expr1)--buildBooleanOperator :: Text -> Expr s X -> Expr s X -> ErrorMessages-buildBooleanOperator operator expr0 expr1 = ErrorMessages {..}- where- short =- Builder.fromText- (Data.Text.strip- [NeatInterpolation.text|❰$txt2❱ only works on ❰Bool❱s|] )-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: The ❰$txt2❱ operator expects two arguments that have type ❰Bool❱--For example, this is a valid use of ❰$txt2❱: --- ┌───────────────┐- │ True $txt2 False │- └───────────────┘---You provided this argument:--↳ $txt0--... which does not have type ❰Bool❱ but instead has type:--↳ $txt1-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty expr1)-- txt2 = Text.toStrict operator--buildNaturalOperator :: Text -> Expr s X -> Expr s X -> ErrorMessages-buildNaturalOperator operator expr0 expr1 = ErrorMessages {..}- where- short =- Builder.fromText- (Data.Text.strip- [NeatInterpolation.text|❰$txt2❱ only works on ❰Natural❱s|] )-- long =- Builder.fromText [NeatInterpolation.text|-Explanation: The ❰$txt2❱ operator expects two arguments that have type ❰Natural❱--For example, this is a valid use of ❰$txt2❱: --- ┌─────────┐- │ +3 $txt2 +5 │- └─────────┘---Some common reasons why you might get this error:--● You might have tried to use an ❰Integer❱, which is $_NOT allowed:--- ┌─────────────────────────────────────────┐- │ λ(x : Integer) → λ(y : Integer) → x $txt2 y │ Not valid- └─────────────────────────────────────────┘--- You can only use ❰Natural❱ numbers---● You might have mistakenly used an ❰Integer❱ literal, which is $_NOT allowed:--- ┌───────┐- │ 2 $txt2 2 │ Not valid- └───────┘--- You need to prefix each literal with a ❰+❱ to transform them into ❰Natural❱- literals, like this:--- ┌─────────┐- │ +2 $txt2 +2 │ Valid- └─────────┘---────────────────────────────────────────────────────────────────────────────────--You provided this argument:--↳ $txt0--... which does not have type ❰Natural❱ but instead has type:--↳ $txt1-|]- where- txt0 = Text.toStrict (Dhall.Core.pretty expr0)- txt1 = Text.toStrict (Dhall.Core.pretty expr1)-- txt2 = Text.toStrict operator+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# OPTIONS_GHC -Wall #-}++-- | This module contains the logic for type checking Dhall code++module Dhall.TypeCheck (+ -- * Type-checking+ typeWith+ , typeOf++ -- * Types+ , X(..)+ , TypeError(..)+ , DetailedTypeError(..)+ , TypeMessage(..)+ ) where++import Control.Exception (Exception)+import Data.Foldable (forM_, toList)+import Data.Monoid ((<>))+import Data.Set (Set)+import Data.Text.Buildable (Buildable(..))+import Data.Text.Lazy (Text)+import Data.Text.Lazy.Builder (Builder)+import Data.Traversable (forM)+import Data.Typeable (Typeable)+import Dhall.Core (Const(..), Expr(..), Var(..))+import Dhall.Context (Context)++import qualified Control.Monad.Trans.State.Strict as State+import qualified Data.Map+import qualified Data.Set+import qualified Data.Text.Lazy as Text+import qualified Data.Text.Lazy.Builder as Builder+import qualified Data.Vector+import qualified Dhall.Context+import qualified Dhall.Core++axiom :: Const -> Either (TypeError s) Const+axiom Type = return Kind+axiom Kind = Left (TypeError Dhall.Context.empty (Const Kind) Untyped)++rule :: Const -> Const -> Either () Const+rule Type Kind = Left ()+rule Type Type = return Type+rule Kind Kind = return Kind+rule Kind Type = return Type++match :: Var -> Var -> [(Text, Text)] -> Bool+match (V xL nL) (V xR nR) [] =+ xL == xR && nL == nR+match (V xL 0 ) (V xR 0 ) ((xL', xR'):_ )+ | xL == xL' && xR == xR' = True+match (V xL nL) (V xR nR) ((xL', xR'):xs) =+ match (V xL nL') (V xR nR') xs+ where+ nL' = if xL == xL' then nL - 1 else nL+ nR' = if xR == xR' then nR - 1 else nR++propEqual :: Expr s X -> Expr t X -> Bool+propEqual eL0 eR0 =+ State.evalState+ (go (Dhall.Core.normalize eL0) (Dhall.Core.normalize eR0))+ []+ where+ go (Const Type) (Const Type) = return True+ go (Const Kind) (Const Kind) = return True+ go (Var vL) (Var vR) = do+ ctx <- State.get+ return (match vL vR ctx)+ go (Pi xL tL bL) (Pi xR tR bR) = do+ ctx <- State.get+ eq1 <- go tL tR+ if eq1+ then do+ State.put ((xL, xR):ctx)+ eq2 <- go bL bR+ State.put ctx+ return eq2+ else return False+ go (App fL aL) (App fR aR) = do+ b1 <- go fL fR+ if b1 then go aL aR else return False+ go Bool Bool = return True+ go Natural Natural = return True+ go Integer Integer = return True+ go Double Double = return True+ go Text Text = return True+ go List List = return True+ go Optional Optional = return True+ go (Record ktsL0) (Record ktsR0) = do+ let loop ((kL, tL):ktsL) ((kR, tR):ktsR)+ | kL == kR = do+ b <- go tL tR+ if b+ then loop ktsL ktsR+ else return False+ loop [] [] = return True+ loop _ _ = return False+ loop (Data.Map.toList ktsL0) (Data.Map.toList ktsR0)+ go (Union ktsL0) (Union ktsR0) = do+ let loop ((kL, tL):ktsL) ((kR, tR):ktsR)+ | kL == kR = do+ b <- go tL tR+ if b+ then loop ktsL ktsR+ else return False+ loop [] [] = return True+ loop _ _ = return False+ loop (Data.Map.toList ktsL0) (Data.Map.toList ktsR0)+ go _ _ = return False++{-| Type-check an expression and return the expression's type if type-checking+ succeeds or an error if type-checking fails++ `typeWith` does not necessarily normalize the type since full normalization+ is not necessary for just type-checking. If you actually care about the+ returned type then you may want to `Dhall.Core.normalize` it afterwards.+-}+typeWith :: Context (Expr s X) -> Expr s X -> Either (TypeError s) (Expr s X)+typeWith _ (Const c ) = do+ fmap Const (axiom c)+typeWith ctx e@(Var (V x n) ) = do+ case Dhall.Context.lookup x n ctx of+ Nothing -> Left (TypeError ctx e UnboundVariable)+ Just a -> return a+typeWith ctx (Lam x _A b ) = do+ let ctx' = fmap (Dhall.Core.shift 1 (V x 0)) (Dhall.Context.insert x _A ctx)+ _B <- typeWith ctx' b+ let p = Pi x _A _B+ _t <- typeWith ctx p+ return p+typeWith ctx e@(Pi x _A _B ) = do+ tA <- fmap Dhall.Core.normalize (typeWith ctx _A)+ kA <- case tA of+ Const k -> return k+ _ -> Left (TypeError ctx e (InvalidInputType _A))++ let ctx' = fmap (Dhall.Core.shift 1 (V x 0)) (Dhall.Context.insert x _A ctx)+ tB <- fmap Dhall.Core.normalize (typeWith ctx' _B)+ kB <- case tB of+ Const k -> return k+ _ -> Left (TypeError ctx' e (InvalidOutputType _B))++ case rule kA kB of+ Left () -> Left (TypeError ctx e (NoDependentTypes _A _B))+ Right k -> Right (Const k)+typeWith ctx e@(App f a ) = do+ tf <- fmap Dhall.Core.normalize (typeWith ctx f)+ (x, _A, _B) <- case tf of+ Pi x _A _B -> return (x, _A, _B)+ _ -> Left (TypeError ctx e (NotAFunction f tf))+ _A' <- typeWith ctx a+ if propEqual _A _A'+ then do+ let a' = Dhall.Core.shift 1 (V x 0) a+ let _B' = Dhall.Core.subst (V x 0) a' _B+ let _B'' = Dhall.Core.shift (-1) (V x 0) _B'+ return _B''+ else do+ let nf_A = Dhall.Core.normalize _A+ let nf_A' = Dhall.Core.normalize _A'+ Left (TypeError ctx e (TypeMismatch f nf_A a nf_A'))+typeWith ctx e@(Let f mt r b ) = do+ tR <- typeWith ctx r+ ttR <- fmap Dhall.Core.normalize (typeWith ctx tR)+ kR <- case ttR of+ Const k -> return k+ -- Don't bother to provide a `let`-specific version of this error+ -- message because this should never happen anyway+ _ -> Left (TypeError ctx e (InvalidInputType tR))++ let ctx' = Dhall.Context.insert f tR ctx+ tB <- typeWith ctx' b+ ttB <- fmap Dhall.Core.normalize (typeWith ctx tB)+ kB <- case ttB of+ Const k -> return k+ -- Don't bother to provide a `let`-specific version of this error+ -- message because this should never happen anyway+ _ -> Left (TypeError ctx e (InvalidOutputType tB))++ case rule kR kB of+ Left () -> Left (TypeError ctx e (NoDependentLet tR tB))+ Right _ -> return ()++ case mt of+ Nothing -> do+ return ()+ Just t -> do+ let nf_t = Dhall.Core.normalize t+ let nf_tR = Dhall.Core.normalize tR+ if propEqual nf_tR nf_t+ then return ()+ else Left (TypeError ctx e (AnnotMismatch r nf_t nf_tR))++ return tB+typeWith ctx e@(Annot x t ) = do+ -- This is mainly just to check that `t` is not `Kind`+ _ <- typeWith ctx t++ t' <- typeWith ctx x+ if propEqual t t'+ then do+ return t+ else do+ let nf_t = Dhall.Core.normalize t+ let nf_t' = Dhall.Core.normalize t'+ Left (TypeError ctx e (AnnotMismatch x nf_t nf_t'))+typeWith _ Bool = do+ return (Const Type)+typeWith _ (BoolLit _ ) = do+ return Bool+typeWith ctx e@(BoolAnd l r ) = do+ tl <- fmap Dhall.Core.normalize (typeWith ctx l)+ case tl of+ Bool -> return ()+ _ -> Left (TypeError ctx e (CantAnd l tl))++ tr <- fmap Dhall.Core.normalize (typeWith ctx r)+ case tr of+ Bool -> return ()+ _ -> Left (TypeError ctx e (CantAnd r tr))++ return Bool+typeWith ctx e@(BoolOr l r ) = do+ tl <- fmap Dhall.Core.normalize (typeWith ctx l)+ case tl of+ Bool -> return ()+ _ -> Left (TypeError ctx e (CantOr l tl))++ tr <- fmap Dhall.Core.normalize (typeWith ctx r)+ case tr of+ Bool -> return ()+ _ -> Left (TypeError ctx e (CantOr r tr))++ return Bool+typeWith ctx e@(BoolEQ l r ) = do+ tl <- fmap Dhall.Core.normalize (typeWith ctx l)+ case tl of+ Bool -> return ()+ _ -> Left (TypeError ctx e (CantEQ l tl))++ tr <- fmap Dhall.Core.normalize (typeWith ctx r)+ case tr of+ Bool -> return ()+ _ -> Left (TypeError ctx e (CantEQ r tr))++ return Bool+typeWith ctx e@(BoolNE l r ) = do+ tl <- fmap Dhall.Core.normalize (typeWith ctx l)+ case tl of+ Bool -> return ()+ _ -> Left (TypeError ctx e (CantNE l tl))++ tr <- fmap Dhall.Core.normalize (typeWith ctx r)+ case tr of+ Bool -> return ()+ _ -> Left (TypeError ctx e (CantNE r tr))++ return Bool+typeWith ctx e@(BoolIf x y z ) = do+ tx <- fmap Dhall.Core.normalize (typeWith ctx x)+ case tx of+ Bool -> return ()+ _ -> Left (TypeError ctx e (InvalidPredicate x tx))+ ty <- fmap Dhall.Core.normalize (typeWith ctx y )+ tty <- fmap Dhall.Core.normalize (typeWith ctx ty)+ case tty of+ Const Type -> return ()+ _ -> Left (TypeError ctx e (IfBranchMustBeTerm True y ty tty))++ tz <- fmap Dhall.Core.normalize (typeWith ctx z)+ ttz <- fmap Dhall.Core.normalize (typeWith ctx tz)+ case ttz of+ Const Type -> return ()+ _ -> Left (TypeError ctx e (IfBranchMustBeTerm False z tz ttz))++ if propEqual ty tz+ then return ()+ else Left (TypeError ctx e (IfBranchMismatch y z ty tz))+ return ty+typeWith _ Natural = do+ return (Const Type)+typeWith _ (NaturalLit _ ) = do+ return Natural+typeWith _ NaturalFold = do+ return+ (Pi "_" Natural+ (Pi "natural" (Const Type)+ (Pi "succ" (Pi "_" "natural" "natural")+ (Pi "zero" "natural" "natural") ) ) )+typeWith _ NaturalBuild = do+ return+ (Pi "_"+ (Pi "natural" (Const Type)+ (Pi "succ" (Pi "_" "natural" "natural")+ (Pi "zero" "natural" "natural") ) )+ Natural )+typeWith _ NaturalIsZero = do+ return (Pi "_" Natural Bool)+typeWith _ NaturalEven = do+ return (Pi "_" Natural Bool)+typeWith _ NaturalOdd = do+ return (Pi "_" Natural Bool)+typeWith _ NaturalToInteger = do+ return (Pi "_" Natural Integer)+typeWith _ NaturalShow = do+ return (Pi "_" Natural Text)+typeWith ctx e@(NaturalPlus l r) = do+ tl <- fmap Dhall.Core.normalize (typeWith ctx l)+ case tl of+ Natural -> return ()+ _ -> Left (TypeError ctx e (CantAdd l tl))++ tr <- fmap Dhall.Core.normalize (typeWith ctx r)+ case tr of+ Natural -> return ()+ _ -> Left (TypeError ctx e (CantAdd r tr))+ return Natural+typeWith ctx e@(NaturalTimes l r) = do+ tl <- fmap Dhall.Core.normalize (typeWith ctx l)+ case tl of+ Natural -> return ()+ _ -> Left (TypeError ctx e (CantMultiply l tl))++ tr <- fmap Dhall.Core.normalize (typeWith ctx r)+ case tr of+ Natural -> return ()+ _ -> Left (TypeError ctx e (CantMultiply r tr))+ return Natural+typeWith _ Integer = do+ return (Const Type)+typeWith _ (IntegerLit _ ) = do+ return Integer+typeWith _ IntegerShow = do+ return (Pi "_" Integer Text)+typeWith _ Double = do+ return (Const Type)+typeWith _ (DoubleLit _ ) = do+ return Double+typeWith _ DoubleShow = do+ return (Pi "_" Double Text)+typeWith _ Text = do+ return (Const Type)+typeWith _ (TextLit _ ) = do+ return Text+typeWith ctx e@(TextAppend l r ) = do+ tl <- fmap Dhall.Core.normalize (typeWith ctx l)+ case tl of+ Text -> return ()+ _ -> Left (TypeError ctx e (CantTextAppend l tl))++ tr <- fmap Dhall.Core.normalize (typeWith ctx r)+ case tr of+ Text -> return ()+ _ -> Left (TypeError ctx e (CantTextAppend r tr))+ return Text+typeWith _ List = do+ return (Pi "_" (Const Type) (Const Type))+typeWith ctx e@(ListLit Nothing xs) = do+ if Data.Vector.null xs+ then Left (TypeError ctx e MissingListType)+ else do+ t <- typeWith ctx (Data.Vector.head xs)+ s <- fmap Dhall.Core.normalize (typeWith ctx t)+ case s of+ Const Type -> return ()+ _ -> Left (TypeError ctx e (InvalidListType t))+ flip Data.Vector.imapM_ xs (\i x -> do+ t' <- typeWith ctx x+ if propEqual t t'+ then return ()+ else do+ let nf_t = Dhall.Core.normalize t+ let nf_t' = Dhall.Core.normalize t'+ let err = MismatchedListElements i nf_t x nf_t'+ Left (TypeError ctx e err) )+ return (App List t)+typeWith ctx e@(ListLit (Just t ) xs) = do+ s <- fmap Dhall.Core.normalize (typeWith ctx t)+ case s of+ Const Type -> return ()+ _ -> Left (TypeError ctx e (InvalidListType t))+ flip Data.Vector.imapM_ xs (\i x -> do+ t' <- typeWith ctx x+ if propEqual t t'+ then return ()+ else do+ let nf_t = Dhall.Core.normalize t+ let nf_t' = Dhall.Core.normalize t'+ Left (TypeError ctx e (InvalidListElement i nf_t x nf_t')) )+ return (App List t)+typeWith ctx e@(ListAppend l r ) = do+ tl <- fmap Dhall.Core.normalize (typeWith ctx l)+ el <- case tl of+ App List el -> return el+ _ -> Left (TypeError ctx e (CantListAppend l tl))++ tr <- fmap Dhall.Core.normalize (typeWith ctx r)+ er <- case tr of+ App List er -> return er+ _ -> Left (TypeError ctx e (CantListAppend r tr))++ if propEqual el er+ then return (App List el)+ else Left (TypeError ctx e (ListAppendMismatch el er))+typeWith _ ListBuild = do+ return+ (Pi "a" (Const Type)+ (Pi "_"+ (Pi "list" (Const Type)+ (Pi "cons" (Pi "_" "a" (Pi "_" "list" "list"))+ (Pi "nil" "list" "list") ) )+ (App List "a") ) )+typeWith _ ListFold = do+ return+ (Pi "a" (Const Type)+ (Pi "_" (App List "a")+ (Pi "list" (Const Type)+ (Pi "cons" (Pi "_" "a" (Pi "_" "list" "list"))+ (Pi "nil" "list" "list")) ) ) )+typeWith _ ListLength = do+ return (Pi "a" (Const Type) (Pi "_" (App List "a") Natural))+typeWith _ ListHead = do+ return (Pi "a" (Const Type) (Pi "_" (App List "a") (App Optional "a")))+typeWith _ ListLast = do+ return (Pi "a" (Const Type) (Pi "_" (App List "a") (App Optional "a")))+typeWith _ ListIndexed = do+ let kts = [("index", Natural), ("value", "a")]+ return+ (Pi "a" (Const Type)+ (Pi "_" (App List "a")+ (App List (Record (Data.Map.fromList kts))) ) )+typeWith _ ListReverse = do+ return (Pi "a" (Const Type) (Pi "_" (App List "a") (App List "a")))+typeWith _ Optional = do+ return (Pi "_" (Const Type) (Const Type))+typeWith ctx e@(OptionalLit t xs) = do+ s <- fmap Dhall.Core.normalize (typeWith ctx t)+ case s of+ Const Type -> return ()+ _ -> Left (TypeError ctx e (InvalidOptionalType t))+ let n = Data.Vector.length xs+ if 2 <= n+ then Left (TypeError ctx e (InvalidOptionalLiteral n))+ else return ()+ forM_ xs (\x -> do+ t' <- typeWith ctx x+ if propEqual t t'+ then return ()+ else do+ let nf_t = Dhall.Core.normalize t+ let nf_t' = Dhall.Core.normalize t'+ Left (TypeError ctx e (InvalidOptionalElement nf_t x nf_t')) )+ return (App Optional t)+typeWith _ OptionalFold = do+ return+ (Pi "a" (Const Type)+ (Pi "_" (App Optional "a")+ (Pi "optional" (Const Type)+ (Pi "just" (Pi "_" "a" "optional")+ (Pi "nothing" "optional" "optional") ) ) ) )+typeWith _ OptionalBuild = do+ return+ (Pi "a" (Const Type)+ (Pi "_" f (App Optional "a") ) )+ where f = Pi "optional" (Const Type)+ (Pi "just" (Pi "_" "a" "optional")+ (Pi "nothing" "optional" "optional") )+typeWith ctx e@(Record kts ) = do+ let process (k, t) = do+ s <- fmap Dhall.Core.normalize (typeWith ctx t)+ case s of+ Const Type -> return ()+ _ -> Left (TypeError ctx e (InvalidFieldType k t))+ mapM_ process (Data.Map.toList kts)+ return (Const Type)+typeWith ctx e@(RecordLit kvs ) = do+ let process (k, v) = do+ t <- typeWith ctx v+ s <- fmap Dhall.Core.normalize (typeWith ctx t)+ case s of+ Const Type -> return ()+ _ -> Left (TypeError ctx e (InvalidField k v))+ return (k, t)+ kts <- mapM process (Data.Map.toAscList kvs)+ return (Record (Data.Map.fromAscList kts))+typeWith ctx e@(Union kts ) = do+ let process (k, t) = do+ s <- fmap Dhall.Core.normalize (typeWith ctx t)+ case s of+ Const Type -> return ()+ _ -> Left (TypeError ctx e (InvalidAlternativeType k t))+ mapM_ process (Data.Map.toList kts)+ return (Const Type)+typeWith ctx e@(UnionLit k v kts) = do+ case Data.Map.lookup k kts of+ Just _ -> Left (TypeError ctx e (DuplicateAlternative k))+ Nothing -> return ()+ t <- typeWith ctx v+ let union = Union (Data.Map.insert k t kts)+ _ <- typeWith ctx union+ return union+typeWith ctx e@(Combine kvsX kvsY) = do+ tKvsX <- fmap Dhall.Core.normalize (typeWith ctx kvsX)+ ktsX <- case tKvsX of+ Record kts -> return kts+ _ -> Left (TypeError ctx e (MustCombineARecord '∧' kvsX tKvsX))++ tKvsY <- fmap Dhall.Core.normalize (typeWith ctx kvsY)+ ktsY <- case tKvsY of+ Record kts -> return kts+ _ -> Left (TypeError ctx e (MustCombineARecord '∧' kvsY tKvsY))++ let combineTypes ktsL ktsR = do+ let ks =+ Data.Set.union (Data.Map.keysSet ktsL) (Data.Map.keysSet ktsR)+ kts <- forM (toList ks) (\k -> do+ case (Data.Map.lookup k ktsL, Data.Map.lookup k ktsR) of+ (Just (Record ktsL'), Just (Record ktsR')) -> do+ t <- combineTypes ktsL' ktsR'+ return (k, t)+ (Nothing, Just t) -> do+ return (k, t)+ (Just t, Nothing) -> do+ return (k, t)+ _ -> do+ Left (TypeError ctx e (FieldCollision k)) )+ return (Record (Data.Map.fromList kts))++ combineTypes ktsX ktsY+typeWith ctx e@(Prefer kvsX kvsY) = do+ tKvsX <- fmap Dhall.Core.normalize (typeWith ctx kvsX)+ ktsX <- case tKvsX of+ Record kts -> return kts+ _ -> Left (TypeError ctx e (MustCombineARecord '⫽' kvsX tKvsX))++ tKvsY <- fmap Dhall.Core.normalize (typeWith ctx kvsY)+ ktsY <- case tKvsY of+ Record kts -> return kts+ _ -> Left (TypeError ctx e (MustCombineARecord '⫽' kvsY tKvsY))+ return (Record (Data.Map.union ktsY ktsX))+typeWith ctx e@(Merge kvsX kvsY (Just t)) = do+ tKvsX <- fmap Dhall.Core.normalize (typeWith ctx kvsX)+ ktsX <- case tKvsX of+ Record kts -> return kts+ _ -> Left (TypeError ctx e (MustMergeARecord kvsX tKvsX))+ let ksX = Data.Map.keysSet ktsX++ tKvsY <- fmap Dhall.Core.normalize (typeWith ctx kvsY)+ ktsY <- case tKvsY of+ Union kts -> return kts+ _ -> Left (TypeError ctx e (MustMergeUnion kvsY tKvsY))+ let ksY = Data.Map.keysSet ktsY++ let diffX = Data.Set.difference ksX ksY+ let diffY = Data.Set.difference ksY ksX++ if Data.Set.null diffX+ then return ()+ else Left (TypeError ctx e (UnusedHandler diffX))++ let process (kY, tY) = do+ case Data.Map.lookup kY ktsX of+ Nothing -> Left (TypeError ctx e (MissingHandler diffY))+ Just tX ->+ case tX of+ Pi _ tY' t' -> do+ if propEqual tY tY'+ then return ()+ else Left (TypeError ctx e (HandlerInputTypeMismatch kY tY tY'))+ if propEqual t t'+ then return ()+ else Left (TypeError ctx e (InvalidHandlerOutputType kY t t'))+ _ -> Left (TypeError ctx e (HandlerNotAFunction kY tX))+ mapM_ process (Data.Map.toList ktsY)+ return t+typeWith ctx e@(Merge kvsX kvsY Nothing) = do+ tKvsX <- fmap Dhall.Core.normalize (typeWith ctx kvsX)+ ktsX <- case tKvsX of+ Record kts -> return kts+ _ -> Left (TypeError ctx e (MustMergeARecord kvsX tKvsX))+ let ksX = Data.Map.keysSet ktsX++ tKvsY <- fmap Dhall.Core.normalize (typeWith ctx kvsY)+ ktsY <- case tKvsY of+ Union kts -> return kts+ _ -> Left (TypeError ctx e (MustMergeUnion kvsY tKvsY))+ let ksY = Data.Map.keysSet ktsY++ let diffX = Data.Set.difference ksX ksY+ let diffY = Data.Set.difference ksY ksX++ if Data.Set.null diffX+ then return ()+ else Left (TypeError ctx e (UnusedHandler diffX))++ (kX, t) <- case Data.Map.assocs ktsX of+ [] -> Left (TypeError ctx e MissingMergeType)+ (kX, Pi _ _ t):_ -> return (kX, t)+ (kX, tX ):_ -> Left (TypeError ctx e (HandlerNotAFunction kX tX))+ let process (kY, tY) = do+ case Data.Map.lookup kY ktsX of+ Nothing -> Left (TypeError ctx e (MissingHandler diffY))+ Just tX ->+ case tX of+ Pi _ tY' t' -> do+ if propEqual tY tY'+ then return ()+ else Left (TypeError ctx e (HandlerInputTypeMismatch kY tY tY'))+ if propEqual t t'+ then return ()+ else Left (TypeError ctx e (HandlerOutputTypeMismatch kX t kY t'))+ _ -> Left (TypeError ctx e (HandlerNotAFunction kY tX))+ mapM_ process (Data.Map.toList ktsY)+ return t+typeWith ctx e@(Field r x ) = do+ t <- fmap Dhall.Core.normalize (typeWith ctx r)+ case t of+ Record kts ->+ case Data.Map.lookup x kts of+ Just t' -> return t'+ Nothing -> Left (TypeError ctx e (MissingField x t))+ _ -> Left (TypeError ctx e (NotARecord x r t))+typeWith ctx (Note s e' ) = case typeWith ctx e' of+ Left (TypeError ctx' (Note s' e'') m) -> Left (TypeError ctx' (Note s' e'') m)+ Left (TypeError ctx' e'' m) -> Left (TypeError ctx' (Note s e'') m)+ Right r -> Right r+typeWith _ (Embed p ) = do+ absurd p++{-| `typeOf` is the same as `typeWith` with an empty context, meaning that the+ expression must be closed (i.e. no free variables), otherwise type-checking+ will fail.+-}+typeOf :: Expr s X -> Either (TypeError s) (Expr s X)+typeOf = typeWith Dhall.Context.empty++-- | Like `Data.Void.Void`, except with a shorter inferred type+newtype X = X { absurd :: forall a . a }++instance Show X where+ show = absurd++instance Eq X where+ _ == _ = True++instance Buildable X where+ build = absurd++-- | The specific type error+data TypeMessage s+ = UnboundVariable+ | InvalidInputType (Expr s X)+ | InvalidOutputType (Expr s X)+ | NotAFunction (Expr s X) (Expr s X)+ | TypeMismatch (Expr s X) (Expr s X) (Expr s X) (Expr s X)+ | AnnotMismatch (Expr s X) (Expr s X) (Expr s X)+ | Untyped+ | MissingListType+ | MismatchedListElements Int (Expr s X) (Expr s X) (Expr s X)+ | InvalidListElement Int (Expr s X) (Expr s X) (Expr s X)+ | InvalidListType (Expr s X)+ | InvalidOptionalElement (Expr s X) (Expr s X) (Expr s X)+ | InvalidOptionalLiteral Int+ | InvalidOptionalType (Expr s X)+ | InvalidPredicate (Expr s X) (Expr s X)+ | IfBranchMismatch (Expr s X) (Expr s X) (Expr s X) (Expr s X)+ | IfBranchMustBeTerm Bool (Expr s X) (Expr s X) (Expr s X)+ | InvalidField Text (Expr s X)+ | InvalidFieldType Text (Expr s X)+ | InvalidAlternative Text (Expr s X)+ | InvalidAlternativeType Text (Expr s X)+ | ListAppendMismatch (Expr s X) (Expr s X)+ | DuplicateAlternative Text+ | MustCombineARecord Char (Expr s X) (Expr s X)+ | FieldCollision Text+ | MustMergeARecord (Expr s X) (Expr s X)+ | MustMergeUnion (Expr s X) (Expr s X)+ | UnusedHandler (Set Text)+ | MissingHandler (Set Text)+ | HandlerInputTypeMismatch Text (Expr s X) (Expr s X)+ | HandlerOutputTypeMismatch Text (Expr s X) Text (Expr s X)+ | InvalidHandlerOutputType Text (Expr s X) (Expr s X)+ | MissingMergeType+ | HandlerNotAFunction Text (Expr s X)+ | NotARecord Text (Expr s X) (Expr s X)+ | MissingField Text (Expr s X)+ | CantAnd (Expr s X) (Expr s X)+ | CantOr (Expr s X) (Expr s X)+ | CantEQ (Expr s X) (Expr s X)+ | CantNE (Expr s X) (Expr s X)+ | CantTextAppend (Expr s X) (Expr s X)+ | CantListAppend (Expr s X) (Expr s X)+ | CantAdd (Expr s X) (Expr s X)+ | CantMultiply (Expr s X) (Expr s X)+ | NoDependentLet (Expr s X) (Expr s X)+ | NoDependentTypes (Expr s X) (Expr s X)+ deriving (Show)++shortTypeMessage :: TypeMessage s -> Builder+shortTypeMessage msg =+ "\ESC[1;31mError\ESC[0m: " <> build short <> "\n"+ where+ ErrorMessages {..} = prettyTypeMessage msg++longTypeMessage :: TypeMessage s -> Builder+longTypeMessage msg =+ "\ESC[1;31mError\ESC[0m: " <> build short <> "\n"+ <> "\n"+ <> long+ where+ ErrorMessages {..} = prettyTypeMessage msg++data ErrorMessages = ErrorMessages+ { short :: Builder+ -- ^ Default succinct 1-line explanation of what went wrong+ , long :: Builder+ -- ^ Longer and more detailed explanation of the error+ }++_NOT :: Builder+_NOT = "\ESC[1mnot\ESC[0m"++prettyTypeMessage :: TypeMessage s -> ErrorMessages+prettyTypeMessage UnboundVariable = ErrorMessages {..}+ where+ short = "Unbound variable"++ long =+ "Explanation: Expressions can only reference previously introduced (i.e. \"bound\")\n\+ \variables that are still \"in scope\" \n\+ \ \n\+ \For example, the following valid expressions introduce a \"bound\" variable named\n\+ \❰x❱: \n\+ \ \n\+ \ \n\+ \ ┌─────────────────┐ \n\+ \ │ λ(x : Bool) → x │ Anonymous functions introduce \"bound\" variables \n\+ \ └─────────────────┘ \n\+ \ ⇧ \n\+ \ This is the bound variable \n\+ \ \n\+ \ \n\+ \ ┌─────────────────┐ \n\+ \ │ let x = 1 in x │ ❰let❱ expressions introduce \"bound\" variables \n\+ \ └─────────────────┘ \n\+ \ ⇧ \n\+ \ This is the bound variable \n\+ \ \n\+ \ \n\+ \However, the following expressions are not valid because they all reference a \n\+ \variable that has not been introduced yet (i.e. an \"unbound\" variable): \n\+ \ \n\+ \ \n\+ \ ┌─────────────────┐ \n\+ \ │ λ(x : Bool) → y │ The variable ❰y❱ hasn't been introduced yet \n\+ \ └─────────────────┘ \n\+ \ ⇧ \n\+ \ This is the unbound variable \n\+ \ \n\+ \ \n\+ \ ┌──────────────────────────┐ \n\+ \ │ (let x = True in x) && x │ ❰x❱ is undefined outside the parentheses \n\+ \ └──────────────────────────┘ \n\+ \ ⇧ \n\+ \ This is the unbound variable \n\+ \ \n\+ \ \n\+ \ ┌────────────────┐ \n\+ \ │ let x = x in x │ The definition for ❰x❱ cannot reference itself \n\+ \ └────────────────┘ \n\+ \ ⇧ \n\+ \ This is the unbound variable \n\+ \ \n\+ \ \n\+ \Some common reasons why you might get this error: \n\+ \ \n\+ \● You misspell a variable name, like this: \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────────────────────────────────┐ \n\+ \ │ λ(empty : Bool) → if emty then \"Empty\" else \"Full\" │ \n\+ \ └────────────────────────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ Typo \n\+ \ \n\+ \ \n\+ \● You misspell a reserved identifier, like this: \n\+ \ \n\+ \ \n\+ \ ┌──────────────────────────┐ \n\+ \ │ foral (a : Type) → a → a │ \n\+ \ └──────────────────────────┘ \n\+ \ ⇧ \n\+ \ Typo \n\+ \ \n\+ \ \n\+ \● You tried to define a recursive value, like this: \n\+ \ \n\+ \ \n\+ \ ┌─────────────────────┐ \n\+ \ │ let x = x + +1 in x │ \n\+ \ └─────────────────────┘ \n\+ \ ⇧ \n\+ \ Recursive definitions are not allowed \n\+ \ \n\+ \ \n\+ \● You accidentally forgot a ❰λ❱ or ❰∀❱/❰forall❱ \n\+ \ \n\+ \ \n\+ \ Unbound variable \n\+ \ ⇩ \n\+ \ ┌─────────────────┐ \n\+ \ │ (x : Bool) → x │ \n\+ \ └─────────────────┘ \n\+ \ ⇧ \n\+ \ A ❰λ❱ here would transform this into a valid anonymous function \n\+ \ \n\+ \ \n\+ \ Unbound variable \n\+ \ ⇩ \n\+ \ ┌────────────────────┐ \n\+ \ │ (x : Bool) → Bool │ \n\+ \ └────────────────────┘ \n\+ \ ⇧ \n\+ \ A ❰∀❱ or ❰forall❱ here would transform this into a valid function type \n"++prettyTypeMessage (InvalidInputType expr) = ErrorMessages {..}+ where+ short = "Invalid function input"++ long =+ "Explanation: A function can accept an input \"term\" that has a given \"type\", like\n\+ \this: \n\+ \ \n\+ \ \n\+ \ This is the input term that the function accepts \n\+ \ ⇩ \n\+ \ ┌───────────────────────┐ \n\+ \ │ ∀(x : Natural) → Bool │ This is the type of a function that accepts an \n\+ \ └───────────────────────┘ input term named ❰x❱ that has type ❰Natural❱ \n\+ \ ⇧ \n\+ \ This is the type of the input term \n\+ \ \n\+ \ \n\+ \ ┌────────────────┐ \n\+ \ │ Bool → Integer │ This is the type of a function that accepts an anonymous\n\+ \ └────────────────┘ input term that has type ❰Bool❱ \n\+ \ ⇧ \n\+ \ This is the type of the input term \n\+ \ \n\+ \ \n\+ \... or a function can accept an input \"type\" that has a given \"kind\", like this:\n\+ \ \n\+ \ \n\+ \ This is the input type that the function accepts \n\+ \ ⇩ \n\+ \ ┌────────────────────┐ \n\+ \ │ ∀(a : Type) → Type │ This is the type of a function that accepts an input\n\+ \ └────────────────────┘ type named ❰a❱ that has kind ❰Type❱ \n\+ \ ⇧ \n\+ \ This is the kind of the input type \n\+ \ \n\+ \ \n\+ \ ┌──────────────────────┐ \n\+ \ │ (Type → Type) → Type │ This is the type of a function that accepts an \n\+ \ └──────────────────────┘ anonymous input type that has kind ❰Type → Type❱ \n\+ \ ⇧ \n\+ \ This is the kind of the input type \n\+ \ \n\+ \ \n\+ \Other function inputs are " <> _NOT <> " valid, like this: \n\+ \ \n\+ \ \n\+ \ ┌──────────────┐ \n\+ \ │ ∀(x : 1) → x │ ❰1❱ is a \"term\" and not a \"type\" nor a \"kind\" so ❰x❱\n\+ \ └──────────────┘ cannot have \"type\" ❰1❱ or \"kind\" ❰1❱ \n\+ \ ⇧ \n\+ \ This is not a type or kind \n\+ \ \n\+ \ \n\+ \ ┌──────────┐ \n\+ \ │ True → x │ ❰True❱ is a \"term\" and not a \"type\" nor a \"kind\" so the \n\+ \ └──────────┘ anonymous input cannot have \"type\" ❰True❱ or \"kind\" ❰True❱\n\+ \ ⇧ \n\+ \ This is not a type or kind \n\+ \ \n\+ \ \n\+ \You annotated a function input with the following expression: \n\+ \ \n\+ \↳ " <> txt <> " \n\+ \ \n\+ \... which is neither a type nor a kind \n"+ where+ txt = build expr++prettyTypeMessage (InvalidOutputType expr) = ErrorMessages {..}+ where+ short = "Invalid function output"++ long =+ "Explanation: A function can return an output \"term\" that has a given \"type\",\n\+ \like this: \n\+ \ \n\+ \ \n\+ \ ┌────────────────────┐ \n\+ \ │ ∀(x : Text) → Bool │ This is the type of a function that returns an \n\+ \ └────────────────────┘ output term that has type ❰Bool❱ \n\+ \ ⇧ \n\+ \ This is the type of the output term \n\+ \ \n\+ \ \n\+ \ ┌────────────────┐ \n\+ \ │ Bool → Integer │ This is the type of a function that returns an output \n\+ \ └────────────────┘ term that has type ❰Int❱ \n\+ \ ⇧ \n\+ \ This is the type of the output term \n\+ \ \n\+ \ \n\+ \... or a function can return an output \"type\" that has a given \"kind\", like \n\+ \this: \n\+ \ \n\+ \ ┌────────────────────┐ \n\+ \ │ ∀(a : Type) → Type │ This is the type of a function that returns an \n\+ \ └────────────────────┘ output type that has kind ❰Type❱ \n\+ \ ⇧ \n\+ \ This is the kind of the output type \n\+ \ \n\+ \ \n\+ \ ┌──────────────────────┐ \n\+ \ │ (Type → Type) → Type │ This is the type of a function that returns an \n\+ \ └──────────────────────┘ output type that has kind ❰Type❱ \n\+ \ ⇧ \n\+ \ This is the kind of the output type \n\+ \ \n\+ \ \n\+ \Other outputs are " <> _NOT <> " valid, like this: \n\+ \ \n\+ \ \n\+ \ ┌─────────────────┐ \n\+ \ │ ∀(x : Bool) → x │ ❰x❱ is a \"term\" and not a \"type\" nor a \"kind\" so the\n\+ \ └─────────────────┘ output cannot have \"type\" ❰x❱ or \"kind\" ❰x❱ \n\+ \ ⇧ \n\+ \ This is not a type or kind \n\+ \ \n\+ \ \n\+ \ ┌─────────────┐ \n\+ \ │ Text → True │ ❰True❱ is a \"term\" and not a \"type\" nor a \"kind\" so the\n\+ \ └─────────────┘ output cannot have \"type\" ❰True❱ or \"kind\" ❰True❱ \n\+ \ ⇧ \n\+ \ This is not a type or kind \n\+ \ \n\+ \ \n\+ \Some common reasons why you might get this error: \n\+ \ \n\+ \● You use ❰∀❱ instead of ❰λ❱ by mistake, like this: \n\+ \ \n\+ \ \n\+ \ ┌────────────────┐ \n\+ \ │ ∀(x: Bool) → x │ \n\+ \ └────────────────┘ \n\+ \ ⇧ \n\+ \ Using ❰λ❱ here instead of ❰∀❱ would transform this into a valid function \n\+ \ \n\+ \ \n\+ \────────────────────────────────────────────────────────────────────────────────\n\+ \ \n\+ \You specified that your function outputs a: \n\+ \ \n\+ \↳ " <> txt <> " \n\+ \ \n\+ \... which is neither a type nor a kind: \n"+ where+ txt = build expr++prettyTypeMessage (NotAFunction expr0 expr1) = ErrorMessages {..}+ where+ short = "Not a function"++ long =+ "Explanation: Expressions separated by whitespace denote function application, \n\+ \like this: \n\+ \ \n\+ \ \n\+ \ ┌─────┐ \n\+ \ │ f x │ This denotes the function ❰f❱ applied to an argument named ❰x❱ \n\+ \ └─────┘ \n\+ \ \n\+ \ \n\+ \A function is a term that has type ❰a → b❱ for some ❰a❱ or ❰b❱. For example, \n\+ \the following expressions are all functions because they have a function type: \n\+ \ \n\+ \ \n\+ \ The function's input type is ❰Bool❱ \n\+ \ ⇩ \n\+ \ ┌───────────────────────────────┐ \n\+ \ │ λ(x : Bool) → x : Bool → Bool │ User-defined anonymous function \n\+ \ └───────────────────────────────┘ \n\+ \ ⇧ \n\+ \ The function's output type is ❰Bool❱ \n\+ \ \n\+ \ \n\+ \ The function's input type is ❰Natural❱ \n\+ \ ⇩ \n\+ \ ┌───────────────────────────────┐ \n\+ \ │ Natural/even : Natural → Bool │ Built-in function \n\+ \ └───────────────────────────────┘ \n\+ \ ⇧ \n\+ \ The function's output type is ❰Bool❱ \n\+ \ \n\+ \ \n\+ \ The function's input kind is ❰Type❱ \n\+ \ ⇩ \n\+ \ ┌───────────────────────────────┐ \n\+ \ │ λ(a : Type) → a : Type → Type │ Type-level functions are still functions \n\+ \ └───────────────────────────────┘ \n\+ \ ⇧ \n\+ \ The function's output kind is ❰Type❱ \n\+ \ \n\+ \ \n\+ \ The function's input kind is ❰Type❱ \n\+ \ ⇩ \n\+ \ ┌────────────────────┐ \n\+ \ │ List : Type → Type │ Built-in type-level function \n\+ \ └────────────────────┘ \n\+ \ ⇧ \n\+ \ The function's output kind is ❰Type❱ \n\+ \ \n\+ \ \n\+ \ Function's input has kind ❰Type❱ \n\+ \ ⇩ \n\+ \ ┌─────────────────────────────────────────────────┐ \n\+ \ │ List/head : ∀(a : Type) → (List a → Optional a) │ A function can return \n\+ \ └─────────────────────────────────────────────────┘ another function \n\+ \ ⇧ \n\+ \ Function's output has type ❰List a → Optional a❱\n\+ \ \n\+ \ \n\+ \ The function's input type is ❰List Text❱ \n\+ \ ⇩ \n\+ \ ┌────────────────────────────────────────────┐ \n\+ \ │ List/head Text : List Text → Optional Text │ A function applied to an \n\+ \ └────────────────────────────────────────────┘ argument can be a function \n\+ \ ⇧ \n\+ \ The function's output type is ❰Optional Text❱\n\+ \ \n\+ \ \n\+ \An expression is not a function if the expression's type is not of the form \n\+ \❰a → b❱. For example, these are " <> _NOT <> " functions: \n\+ \ \n\+ \ \n\+ \ ┌─────────────┐ \n\+ \ │ 1 : Integer │ ❰1❱ is not a function because ❰Integer❱ is not the type of \n\+ \ └─────────────┘ a function \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────┐ \n\+ \ │ Natural/even +2 : Bool │ ❰Natural/even +2❱ is not a function because \n\+ \ └────────────────────────┘ ❰Bool❱ is not the type of a function \n\+ \ \n\+ \ \n\+ \ ┌──────────────────┐ \n\+ \ │ List Text : Type │ ❰List Text❱ is not a function because ❰Type❱ is not \n\+ \ └──────────────────┘ the type of a function \n\+ \ \n\+ \ \n\+ \Some common reasons why you might get this error: \n\+ \ \n\+ \● You tried to add two ❰Integer❱s without a space around the ❰+❱, like this: \n\+ \ \n\+ \ \n\+ \ ┌─────┐ \n\+ \ │ 2+2 │ \n\+ \ └─────┘ \n\+ \ \n\+ \ \n\+ \ The above code is parsed as: \n\+ \ \n\+ \ \n\+ \ ┌────────┐ \n\+ \ │ 2 (+2) │ \n\+ \ └────────┘ \n\+ \ ⇧ \n\+ \ The compiler thinks that this ❰2❱ is a function whose argument is ❰+2❱ \n\+ \ \n\+ \ \n\+ \ This is because the ❰+❱ symbol has two meanings: you use ❰+❱ to add two \n\+ \ numbers, but you also can prefix ❰Integer❱ literals with a ❰+❱ to turn them \n\+ \ into ❰Natural❱ literals (like ❰+2❱) \n\+ \ \n\+ \ To fix the code, you need to put spaces around the ❰+❱ and also prefix each \n\+ \ ❰2❱ with a ❰+❱, like this: \n\+ \ \n\+ \ \n\+ \ ┌─────────┐ \n\+ \ │ +2 + +2 │ \n\+ \ └─────────┘ \n\+ \ \n\+ \ \n\+ \ You can only add ❰Natural❱ numbers, which is why you must also change each \n\+ \ ❰2❱ to ❰+2❱ \n\+ \ \n\+ \────────────────────────────────────────────────────────────────────────────────\n\+ \ \n\+ \You tried to use the following expression as a function: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... but this expression's type is: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... which is not a function type \n"+ where+ txt0 = build expr0+ txt1 = build expr1++prettyTypeMessage (TypeMismatch expr0 expr1 expr2 expr3) = ErrorMessages {..}+ where+ short = "Wrong type of function argument"++ long =+ "Explanation: Every function declares what type or kind of argument to accept \n\+ \ \n\+ \For example: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────┐ \n\+ \ │ λ(x : Bool) → x : Bool → Bool │ This anonymous function only accepts \n\+ \ └───────────────────────────────┘ arguments that have type ❰Bool❱ \n\+ \ ⇧ \n\+ \ The function's input type \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────┐ \n\+ \ │ Natural/even : Natural → Bool │ This built-in function only accepts \n\+ \ └───────────────────────────────┘ arguments that have type ❰Natural❱ \n\+ \ ⇧ \n\+ \ The function's input type \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────┐ \n\+ \ │ λ(a : Type) → a : Type → Type │ This anonymous function only accepts \n\+ \ └───────────────────────────────┘ arguments that have kind ❰Type❱ \n\+ \ ⇧ \n\+ \ The function's input kind \n\+ \ \n\+ \ \n\+ \ ┌────────────────────┐ \n\+ \ │ List : Type → Type │ This built-in function only accepts arguments that \n\+ \ └────────────────────┘ have kind ❰Type❱ \n\+ \ ⇧ \n\+ \ The function's input kind \n\+ \ \n\+ \ \n\+ \For example, the following expressions are valid: \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────┐ \n\+ \ │ (λ(x : Bool) → x) True │ ❰True❱ has type ❰Bool❱, which matches the type \n\+ \ └────────────────────────┘ of argument that the anonymous function accepts \n\+ \ \n\+ \ \n\+ \ ┌─────────────────┐ \n\+ \ │ Natural/even +2 │ ❰+2❱ has type ❰Natural❱, which matches the type of \n\+ \ └─────────────────┘ argument that the ❰Natural/even❱ function accepts, \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────┐ \n\+ \ │ (λ(a : Type) → a) Bool │ ❰Bool❱ has kind ❰Type❱, which matches the kind \n\+ \ └────────────────────────┘ of argument that the anonymous function accepts \n\+ \ \n\+ \ \n\+ \ ┌───────────┐ \n\+ \ │ List Text │ ❰Text❱ has kind ❰Type❱, which matches the kind of argument \n\+ \ └───────────┘ that that the ❰List❱ function accepts \n\+ \ \n\+ \ \n\+ \However, you can " <> _NOT <> " apply a function to the wrong type or kind of argument\n\+ \ \n\+ \For example, the following expressions are not valid: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────┐ \n\+ \ │ (λ(x : Bool) → x) \"A\" │ ❰\"A\"❱ has type ❰Text❱, but the anonymous function\n\+ \ └───────────────────────┘ expects an argument that has type ❰Bool❱ \n\+ \ \n\+ \ \n\+ \ ┌──────────────────┐ \n\+ \ │ Natural/even \"A\" │ ❰\"A\"❱ has type ❰Text❱, but the ❰Natural/even❱ function\n\+ \ └──────────────────┘ expects an argument that has type ❰Natural❱ \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────┐ \n\+ \ │ (λ(a : Type) → a) True │ ❰True❱ has type ❰Bool❱, but the anonymous \n\+ \ └────────────────────────┘ function expects an argument of kind ❰Type❱ \n\+ \ \n\+ \ \n\+ \ ┌────────┐ \n\+ \ │ List 1 │ ❰1❱ has type ❰Integer❱, but the ❰List❱ function expects an \n\+ \ └────────┘ argument that has kind ❰Type❱ \n\+ \ \n\+ \ \n\+ \Some common reasons why you might get this error: \n\+ \ \n\+ \● You omit a function argument by mistake: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────┐ \n\+ \ │ List/head [1, 2, 3] │ \n\+ \ └───────────────────────┘ \n\+ \ ⇧ \n\+ \ ❰List/head❱ is missing the first argument, \n\+ \ which should be: ❰Integer❱ \n\+ \ \n\+ \ \n\+ \● You supply an ❰Integer❱ literal to a function that expects a ❰Natural❱ \n\+ \ \n\+ \ \n\+ \ ┌────────────────┐ \n\+ \ │ Natural/even 2 │ \n\+ \ └────────────────┘ \n\+ \ ⇧ \n\+ \ This should be ❰+2❱ \n\+ \ \n\+ \ \n\+ \────────────────────────────────────────────────────────────────────────────────\n\+ \ \n\+ \You tried to invoke the following function: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... which expects an argument of type or kind: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... on the following argument: \n\+ \ \n\+ \↳ " <> txt2 <> " \n\+ \ \n\+ \... which has a different type or kind: \n\+ \ \n\+ \↳ " <> txt3 <> " \n"+ where+ txt0 = build expr0+ txt1 = build expr1+ txt2 = build expr2+ txt3 = build expr3++prettyTypeMessage (AnnotMismatch expr0 expr1 expr2) = ErrorMessages {..}+ where+ short = "Expression doesn't match annotation"++ long =+ "Explanation: You can annotate an expression with its type or kind using the \n\+ \❰:❱ symbol, like this: \n\+ \ \n\+ \ \n\+ \ ┌───────┐ \n\+ \ │ x : t │ ❰x❱ is an expression and ❰t❱ is the annotated type or kind of ❰x❱\n\+ \ └───────┘ \n\+ \ \n\+ \The type checker verifies that the expression's type or kind matches the \n\+ \provided annotation \n\+ \ \n\+ \For example, all of the following are valid annotations that the type checker \n\+ \accepts: \n\+ \ \n\+ \ \n\+ \ ┌─────────────┐ \n\+ \ │ 1 : Integer │ ❰1❱ is an expression that has type ❰Integer❱, so the type \n\+ \ └─────────────┘ checker accepts the annotation \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────┐ \n\+ \ │ Natural/even +2 : Bool │ ❰Natural/even +2❱ has type ❰Bool❱, so the type \n\+ \ └────────────────────────┘ checker accepts the annotation \n\+ \ \n\+ \ \n\+ \ ┌────────────────────┐ \n\+ \ │ List : Type → Type │ ❰List❱ is an expression that has kind ❰Type → Type❱,\n\+ \ └────────────────────┘ so the type checker accepts the annotation \n\+ \ \n\+ \ \n\+ \ ┌──────────────────┐ \n\+ \ │ List Text : Type │ ❰List Text❱ is an expression that has kind ❰Type❱, so \n\+ \ └──────────────────┘ the type checker accepts the annotation \n\+ \ \n\+ \ \n\+ \However, the following annotations are " <> _NOT <> " valid and the type checker will\n\+ \reject them: \n\+ \ \n\+ \ \n\+ \ ┌──────────┐ \n\+ \ │ 1 : Text │ The type checker rejects this because ❰1❱ does not have type \n\+ \ └──────────┘ ❰Text❱ \n\+ \ \n\+ \ \n\+ \ ┌─────────────┐ \n\+ \ │ List : Type │ ❰List❱ does not have kind ❰Type❱ \n\+ \ └─────────────┘ \n\+ \ \n\+ \ \n\+ \Some common reasons why you might get this error: \n\+ \ \n\+ \● The Haskell Dhall interpreter implicitly inserts a top-level annotation \n\+ \ matching the expected type \n\+ \ \n\+ \ For example, if you run the following Haskell code: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────┐ \n\+ \ │ >>> input auto \"1\" :: IO Text │ \n\+ \ └───────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \ ... then the interpreter will actually type check the following annotated \n\+ \ expression: \n\+ \ \n\+ \ \n\+ \ ┌──────────┐ \n\+ \ │ 1 : Text │ \n\+ \ └──────────┘ \n\+ \ \n\+ \ \n\+ \ ... and then type-checking will fail \n\+ \ \n\+ \────────────────────────────────────────────────────────────────────────────────\n\+ \ \n\+ \You or the interpreter annotated this expression: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... with this type or kind: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... but the inferred type or kind of the expression is actually: \n\+ \ \n\+ \↳ " <> txt2 <> " \n"+ where+ txt0 = build expr0+ txt1 = build expr1+ txt2 = build expr2++prettyTypeMessage Untyped = ErrorMessages {..}+ where+ short = "❰Kind❱ has no type or kind"++ long =+ "Explanation: There are four levels of expressions that form a heirarchy: \n\+ \ \n\+ \● terms \n\+ \● types \n\+ \● kinds \n\+ \● sorts \n\+ \ \n\+ \The following example illustrates this heirarchy: \n\+ \ \n\+ \ ┌────────────────────────────┐ \n\+ \ │ \"ABC\" : Text : Type : Kind │ \n\+ \ └────────────────────────────┘ \n\+ \ ⇧ ⇧ ⇧ ⇧ \n\+ \ term type kind sort \n\+ \ \n\+ \There is nothing above ❰Kind❱ in this hierarchy, so if you try to type check any\n\+ \expression containing ❰Kind❱ anywhere in the expression then type checking fails\n\+ \ \n\+ \Some common reasons why you might get this error: \n\+ \ \n\+ \● You supplied a kind where a type was expected \n\+ \ \n\+ \ For example, the following expression will fail to type check: \n\+ \ \n\+ \ ┌────────────────┐ \n\+ \ │ [] : List Type │ \n\+ \ └────────────────┘ \n\+ \ ⇧ \n\+ \ ❰Type❱ is a kind, not a type \n"++prettyTypeMessage (InvalidPredicate expr0 expr1) = ErrorMessages {..}+ where+ short = "Invalid predicate for ❰if❱"++ long =+ "Explanation: Every ❰if❱ expression begins with a predicate which must have type \n\+ \❰Bool❱ \n\+ \ \n\+ \For example, these are valid ❰if❱ expressions: \n\+ \ \n\+ \ \n\+ \ ┌──────────────────────────────┐ \n\+ \ │ if True then \"Yes\" else \"No\" │ \n\+ \ └──────────────────────────────┘ \n\+ \ ⇧ \n\+ \ Predicate \n\+ \ \n\+ \ \n\+ \ ┌─────────────────────────────────────────┐ \n\+ \ │ λ(x : Bool) → if x then False else True │ \n\+ \ └─────────────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ Predicate \n\+ \ \n\+ \ \n\+ \... but these are " <> _NOT <> " valid ❰if❱ expressions: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────┐ \n\+ \ │ if 0 then \"Yes\" else \"No\" │ ❰0❱ does not have type ❰Bool❱ \n\+ \ └───────────────────────────┘ \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────────┐ \n\+ \ │ if \"\" then False else True │ ❰\"\"❱ does not have type ❰Bool❱ \n\+ \ └────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \Some common reasons why you might get this error: \n\+ \ \n\+ \● You might be used to other programming languages that accept predicates other \n\+ \ than ❰Bool❱ \n\+ \ \n\+ \ For example, some languages permit ❰0❱ or ❰\"\"❱ as valid predicates and treat\n\+ \ them as equivalent to ❰False❱. However, the Dhall language does not permit \n\+ \ this \n\+ \ \n\+ \────────────────────────────────────────────────────────────────────────────────\n\+ \ \n\+ \Your ❰if❱ expression begins with the following predicate: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... that has type: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... but the predicate must instead have type ❰Bool❱ \n"+ where+ txt0 = build expr0+ txt1 = build expr1++prettyTypeMessage (IfBranchMustBeTerm b expr0 expr1 expr2) =+ ErrorMessages {..}+ where+ short = "❰if❱ branch is not a term"++ long =+ "Explanation: Every ❰if❱ expression has a ❰then❱ and ❰else❱ branch, each of which\n\+ \is an expression: \n\+ \ \n\+ \ \n\+ \ Expression for ❰then❱ branch \n\+ \ ⇩ \n\+ \ ┌────────────────────────────────┐ \n\+ \ │ if True then \"Hello, world!\" │ \n\+ \ │ else \"Goodbye, world!\" │ \n\+ \ └────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ Expression for ❰else❱ branch \n\+ \ \n\+ \ \n\+ \These expressions must be a \"term\", where a \"term\" is defined as an expression\n\+ \that has a type thas has kind ❰Type❱ \n\+ \ \n\+ \For example, the following expressions are all valid \"terms\": \n\+ \ \n\+ \ \n\+ \ ┌────────────────────┐ \n\+ \ │ 1 : Integer : Type │ ❰1❱ is a term with a type (❰Integer❱) of kind ❰Type❱\n\+ \ └────────────────────┘ \n\+ \ ⇧ \n\+ \ term \n\+ \ \n\+ \ \n\+ \ ┌─────────────────────────────────────┐ \n\+ \ │ Natural/odd : Natural → Bool : Type │ ❰Natural/odd❱ is a term with a type\n\+ \ └─────────────────────────────────────┘ (❰Natural → Bool❱) of kind ❰Type❱ \n\+ \ ⇧ \n\+ \ term \n\+ \ \n\+ \ \n\+ \However, the following expressions are " <> _NOT <> " valid terms: \n\+ \ \n\+ \ \n\+ \ ┌────────────────────┐ \n\+ \ │ Text : Type : Kind │ ❰Text❱ has kind (❰Type❱) of sort ❰Kind❱ and is \n\+ \ └────────────────────┘ therefore not a term \n\+ \ ⇧ \n\+ \ type \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────┐ \n\+ \ │ List : Type → Type : Kind │ ❰List❱ has kind (❰Type → Type❱) of sort \n\+ \ └───────────────────────────┘ ❰Kind❱ and is therefore not a term \n\+ \ ⇧ \n\+ \ type-level function \n\+ \ \n\+ \ \n\+ \This means that you cannot define an ❰if❱ expression that returns a type. For \n\+ \example, the following ❰if❱ expression is " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ ┌─────────────────────────────┐ \n\+ \ │ if True then Text else Bool │ Invalid ❰if❱ expression \n\+ \ └─────────────────────────────┘ \n\+ \ ⇧ ⇧ \n\+ \ type type \n\+ \ \n\+ \ \n\+ \Your ❰" <> txt0 <> "❱ branch of your ❰if❱ expression is: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... which has kind: \n\+ \ \n\+ \↳ " <> txt2 <> " \n\+ \ \n\+ \... of sort: \n\+ \ \n\+ \↳ " <> txt3 <> " \n\+ \ \n\+ \... and is not a term. Therefore your ❰if❱ expression is not valid \n"+ where+ txt0 = if b then "then" else "else"+ txt1 = build expr0+ txt2 = build expr1+ txt3 = build expr2++prettyTypeMessage (IfBranchMismatch expr0 expr1 expr2 expr3) =+ ErrorMessages {..}+ where+ short = "❰if❱ branches must have matching types"++ long =+ "Explanation: Every ❰if❱ expression has a ❰then❱ and ❰else❱ branch, each of which\n\+ \is an expression: \n\+ \ \n\+ \ \n\+ \ Expression for ❰then❱ branch \n\+ \ ⇩ \n\+ \ ┌────────────────────────────────┐ \n\+ \ │ if True then \"Hello, world!\" │ \n\+ \ │ else \"Goodbye, world!\" │ \n\+ \ └────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ Expression for ❰else❱ branch \n\+ \ \n\+ \ \n\+ \These two expressions must have the same type. For example, the following ❰if❱ \n\+ \expressions are all valid: \n\+ \ \n\+ \ \n\+ \ ┌──────────────────────────────────┐ \n\+ \ │ λ(b : Bool) → if b then 0 else 1 │ Both branches have type ❰Integer❱ \n\+ \ └──────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────────┐ \n\+ \ │ λ(b : Bool) → │ \n\+ \ │ if b then Natural/even │ Both branches have type ❰Natural → Bool❱ \n\+ \ │ else Natural/odd │ \n\+ \ └────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \However, the following expression is " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ This branch has type ❰Integer❱ \n\+ \ ⇩ \n\+ \ ┌────────────────────────┐ \n\+ \ │ if True then 0 │ \n\+ \ │ else \"ABC\" │ \n\+ \ └────────────────────────┘ \n\+ \ ⇧ \n\+ \ This branch has type ❰Text❱ \n\+ \ \n\+ \ \n\+ \The ❰then❱ and ❰else❱ branches must have matching types, even if the predicate \n\+ \is always ❰True❱ or ❰False❱ \n\+ \ \n\+ \Your ❰if❱ expression has the following ❰then❱ branch: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... which has type: \n\+ \ \n\+ \↳ " <> txt2 <> " \n\+ \ \n\+ \... and the following ❰else❱ branch: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... which has a different type: \n\+ \ \n\+ \↳ " <> txt3 <> " \n\+ \ \n\+ \Fix your ❰then❱ and ❰else❱ branches to have matching types \n"+ where+ txt0 = build expr0+ txt1 = build expr1+ txt2 = build expr2+ txt3 = build expr3++prettyTypeMessage (InvalidListType expr0) = ErrorMessages {..}+ where+ short = "Invalid type for ❰List❱ elements"++ long =+ "Explanation: ❰List❱s can optionally document the type of their elements with a \n\+ \type annotation, like this: \n\+ \ \n\+ \ \n\+ \ ┌──────────────────────────┐ \n\+ \ │ [1, 2, 3] : List Integer │ A ❰List❱ of three ❰Integer❱s \n\+ \ └──────────────────────────┘ \n\+ \ ⇧ \n\+ \ The type of the ❰List❱'s elements, which are ❰Integer❱s \n\+ \ \n\+ \ \n\+ \ ┌───────────────────┐ \n\+ \ │ [] : List Integer │ An empty ❰List❱ \n\+ \ └───────────────────┘ \n\+ \ ⇧ \n\+ \ You must specify the type when the ❰List❱ is empty \n\+ \ \n\+ \ \n\+ \The element type must be a type and not something else. For example, the \n\+ \following element types are " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ ┌──────────────┐ \n\+ \ │ ... : List 1 │ \n\+ \ └──────────────┘ \n\+ \ ⇧ \n\+ \ This is an ❰Integer❱ and not a ❰Type❱ \n\+ \ \n\+ \ \n\+ \ ┌─────────────────┐ \n\+ \ │ ... : List Type │ \n\+ \ └─────────────────┘ \n\+ \ ⇧ \n\+ \ This is a ❰Kind❱ and not a ❰Type❱ \n\+ \ \n\+ \ \n\+ \You declared that the ❰List❱'s elements should have type: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... which is not a ❰Type❱ \n"+ where+ txt0 = build expr0++prettyTypeMessage MissingListType = do+ ErrorMessages {..}+ where+ short = "An empty list requires a type annotation"++ long =+ "Explanation: Lists do not require a type annotation if they have at least one \n\+ \element: \n\+ \ \n\+ \ \n\+ \ ┌───────────┐ \n\+ \ │ [1, 2, 3] │ The compiler can infer that this list has type ❰List Integer❱\n\+ \ └───────────┘ \n\+ \ \n\+ \ \n\+ \However, empty lists still require a type annotation: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────┐ \n\+ \ │ [] : List Integer │ This type annotation is mandatory \n\+ \ └───────────────────┘ \n\+ \ \n\+ \ \n\+ \You cannot supply an empty list without a type annotation \n"++prettyTypeMessage (MismatchedListElements i expr0 expr1 expr2) =+ ErrorMessages {..}+ where+ short = "List elements should have the same type"++ long =+ "Explanation: Every element in a list must have the same type \n\+ \ \n\+ \For example, this is a valid ❰List❱: \n\+ \ \n\+ \ \n\+ \ ┌───────────┐ \n\+ \ │ [1, 2, 3] │ Every element in this ❰List❱ is an ❰Integer❱ \n\+ \ └───────────┘ \n\+ \ \n\+ \ \n\+ \.. but this is " <> _NOT <> " a valid ❰List❱: \n\+ \ \n\+ \ \n\+ \ ┌───────────────┐ \n\+ \ │ [1, \"ABC\", 3] │ The first and second element have different types \n\+ \ └───────────────┘ \n\+ \ \n\+ \ \n\+ \Your first ❰List❱ elements has this type: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... but the following element at index " <> txt1 <> ": \n\+ \ \n\+ \↳ " <> txt2 <> " \n\+ \ \n\+ \... has this type instead: \n\+ \ \n\+ \↳ " <> txt3 <> " \n"+ where+ txt0 = build expr0+ txt1 = build i+ txt2 = build expr1+ txt3 = build expr2++prettyTypeMessage (InvalidListElement i expr0 expr1 expr2) =+ ErrorMessages {..}+ where+ short = "List element has the wrong type"++ long =+ "Explanation: Every element in the list must have a type matching the type \n\+ \annotation at the end of the list \n\+ \ \n\+ \For example, this is a valid ❰List❱: \n\+ \ \n\+ \ \n\+ \ ┌──────────────────────────┐ \n\+ \ │ [1, 2, 3] : List Integer │ Every element in this ❰List❱ is an ❰Integer❱ \n\+ \ └──────────────────────────┘ \n\+ \ \n\+ \ \n\+ \.. but this is " <> _NOT <> " a valid ❰List❱: \n\+ \ \n\+ \ \n\+ \ ┌──────────────────────────────┐ \n\+ \ │ [1, \"ABC\", 3] : List Integer │ The second element is not an ❰Integer❱ \n\+ \ └──────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \Your ❰List❱ elements should have this type: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... but the following element at index " <> txt1 <> ": \n\+ \ \n\+ \↳ " <> txt2 <> " \n\+ \ \n\+ \... has this type instead: \n\+ \ \n\+ \↳ " <> txt3 <> " \n"+ where+ txt0 = build expr0+ txt1 = build i+ txt2 = build expr1+ txt3 = build expr2++prettyTypeMessage (InvalidOptionalType expr0) = ErrorMessages {..}+ where+ short = "Invalid type for ❰Optional❱ element"++ long =+ "Explanation: Every optional element ends with a type annotation for the element \n\+ \that might be present, like this: \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────┐ \n\+ \ │ [1] : Optional Integer │ An optional element that's present \n\+ \ └────────────────────────┘ \n\+ \ ⇧ \n\+ \ The type of the ❰Optional❱ element, which is an ❰Integer❱ \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────┐ \n\+ \ │ [] : Optional Integer │ An optional element that's absent \n\+ \ └────────────────────────┘ \n\+ \ ⇧ \n\+ \ You still specify the type even when the element is absent \n\+ \ \n\+ \ \n\+ \The element type must be a type and not something else. For example, the \n\+ \following element types are " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ ┌──────────────────┐ \n\+ \ │ ... : Optional 1 │ \n\+ \ └──────────────────┘ \n\+ \ ⇧ \n\+ \ This is an ❰Integer❱ and not a ❰Type❱ \n\+ \ \n\+ \ \n\+ \ ┌─────────────────────┐ \n\+ \ │ ... : Optional Type │ \n\+ \ └─────────────────────┘ \n\+ \ ⇧ \n\+ \ This is a ❰Kind❱ and not a ❰Type❱ \n\+ \ \n\+ \ \n\+ \Even if the element is absent you still must specify a valid type \n\+ \ \n\+ \You declared that the ❰Optional❱ element should have type: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... which is not a ❰Type❱ \n"+ where+ txt0 = build expr0++prettyTypeMessage (InvalidOptionalElement expr0 expr1 expr2) = ErrorMessages {..}+ where+ short = "❰Optional❱ element has the wrong type"++ long =+ "Explanation: An ❰Optional❱ element must have a type matching the type annotation\n\+ \ \n\+ \For example, this is a valid ❰Optional❱ value: \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────┐ \n\+ \ │ [1] : Optional Integer │ ❰1❱ is an ❰Integer❱, which matches the type \n\+ \ └────────────────────────┘ \n\+ \ \n\+ \ \n\+ \... but this is " <> _NOT <> " a valid ❰Optional❱ value: \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────────┐ \n\+ \ │ [\"ABC\"] : Optional Integer │ ❰\"ABC\"❱ is not an ❰Integer❱ \n\+ \ └────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \Your ❰Optional❱ element should have this type: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... but the element you provided: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... has this type instead: \n\+ \ \n\+ \↳ " <> txt2 <> " \n"+ where+ txt0 = build expr0+ txt1 = build expr1+ txt2 = build expr2++prettyTypeMessage (InvalidOptionalLiteral n) = ErrorMessages {..}+ where+ short = "Multiple ❰Optional❱ elements not allowed"++ long =+ "Explanation: The syntax for ❰Optional❱ values resembles the syntax for ❰List❱s: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────┐ \n\+ \ │ [] : Optional Integer │ An ❰Optional❱ value which is absent \n\+ \ └───────────────────────┘ \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────┐ \n\+ \ │ [] : List Integer │ An empty (0-element) ❰List❱ \n\+ \ └───────────────────────┘ \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────┐ \n\+ \ │ [1] : Optional Integer │ An ❰Optional❱ value which is present \n\+ \ └────────────────────────┘ \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────┐ \n\+ \ │ [1] : List Integer │ A singleton (1-element) ❰List❱ \n\+ \ └────────────────────────┘ \n\+ \ \n\+ \ \n\+ \However, an ❰Optional❱ value can " <> _NOT <> " have more than one element, whereas a\n\+ \❰List❱ can have multiple elements: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────┐ \n\+ \ │ [1, 2] : Optional Integer │ Invalid: multiple elements " <> _NOT <> " allowed\n\+ \ └───────────────────────────┘ \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────┐ \n\+ \ │ [1, 2] : List Integer │ Valid: multiple elements allowed \n\+ \ └───────────────────────────┘ \n\+ \ \n\+ \ \n\+ \Some common reasons why you might get this error: \n\+ \ \n\+ \● You accidentally typed ❰Optional❱ when you meant ❰List❱, like this: \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────────────────────────────────┐ \n\+ \ │ List/length Integer ([1, 2, 3] : Optional Integer) │ \n\+ \ └────────────────────────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ This should be ❰List❱ instead \n\+ \ \n\+ \ \n\+ \────────────────────────────────────────────────────────────────────────────────\n\+ \ \n\+ \Your ❰Optional❱ value had this many elements: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... when an ❰Optional❱ value can only have at most one element \n"+ where+ txt0 = build n++prettyTypeMessage (InvalidFieldType k expr0) = ErrorMessages {..}+ where+ short = "Invalid field type"++ long =+ "Explanation: Every record type documents the type of each field, like this: \n\+ \ \n\+ \ ┌──────────────────────────────────────────────┐ \n\+ \ │ { foo : Integer, bar : Integer, baz : Text } │ \n\+ \ └──────────────────────────────────────────────┘ \n\+ \ \n\+ \However, fields cannot be annotated with expressions other than types \n\+ \ \n\+ \For example, these record types are " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────────┐ \n\+ \ │ { foo : Integer, bar : 1 } │ \n\+ \ └────────────────────────────┘ \n\+ \ ⇧ \n\+ \ ❰1❱ is an ❰Integer❱ and not a ❰Type❱ \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────┐ \n\+ \ │ { foo : Integer, bar : Type } │ \n\+ \ └───────────────────────────────┘ \n\+ \ ⇧ \n\+ \ ❰Type❱ is a ❰Kind❱ and not a ❰Type❱ \n\+ \ \n\+ \ \n\+ \You provided a record type with a key named: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... annotated with the following expression: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... which is not a type \n"+ where+ txt0 = build k+ txt1 = build expr0++prettyTypeMessage (InvalidField k expr0) = ErrorMessages {..}+ where+ short = "Invalid field"++ long =+ "Explanation: Every record literal is a set of fields assigned to values, like \n\+ \this: \n\+ \ \n\+ \ ┌────────────────────────────────────────┐ \n\+ \ │ { foo = 100, bar = True, baz = \"ABC\" } │ \n\+ \ └────────────────────────────────────────┘ \n\+ \ \n\+ \However, fields can only be terms and cannot be types or kinds \n\+ \ \n\+ \For example, these record literals are " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────┐ \n\+ \ │ { foo = 100, bar = Text } │ \n\+ \ └───────────────────────────┘ \n\+ \ ⇧ \n\+ \ ❰Text❱ is a type and not a term \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────┐ \n\+ \ │ { foo = 100, bar = Type } │ \n\+ \ └───────────────────────────┘ \n\+ \ ⇧ \n\+ \ ❰Type❱ is a kind and not a term \n\+ \ \n\+ \ \n\+ \You provided a record literal with a key named: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... whose value is: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... which is not a term \n"+ where+ txt0 = build k+ txt1 = build expr0++prettyTypeMessage (InvalidAlternativeType k expr0) = ErrorMessages {..}+ where+ short = "Invalid alternative"++ long =+ "Explanation: Every union literal begins by selecting one alternative and \n\+ \specifying the value for that alternative, like this: \n\+ \ \n\+ \ \n\+ \ Select the ❰Left❱ alternative, whose value is ❰True❱ \n\+ \ ⇩ \n\+ \ ┌──────────────────────────────────┐ \n\+ \ │ < Left = True, Right : Natural > │ A union literal with two alternatives \n\+ \ └──────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \However, this value must be a term and not a type. For example, the following \n\+ \values are " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ ┌──────────────────────────────────┐ \n\+ \ │ < Left = Text, Right : Natural > │ Invalid union literal \n\+ \ └──────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ This is a type and not a term \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────┐ \n\+ \ │ < Left = Type, Right : Type > │ Invalid union type \n\+ \ └───────────────────────────────┘ \n\+ \ ⇧ \n\+ \ This is a kind and not a term \n\+ \ \n\+ \ \n\+ \Some common reasons why you might get this error: \n\+ \ \n\+ \● You accidentally typed ❰=❱ instead of ❰:❱ for a union literal with one \n\+ \ alternative: \n\+ \ \n\+ \ \n\+ \ ┌────────────────────┐ \n\+ \ │ < Example = Text > │ \n\+ \ └────────────────────┘ \n\+ \ ⇧ \n\+ \ This could be ❰:❱ instead \n\+ \ \n\+ \ \n\+ \────────────────────────────────────────────────────────────────────────────────\n\+ \ \n\+ \You provided a union literal with an alternative named: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... whose value is: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... which is not a term \n"+ where+ txt0 = build k+ txt1 = build expr0++prettyTypeMessage (InvalidAlternative k expr0) = ErrorMessages {..}+ where+ short = "Invalid alternative"++ long =+ "Explanation: Every union type specifies the type of each alternative, like this:\n\+ \ \n\+ \ \n\+ \ The type of the first alternative is ❰Bool❱ \n\+ \ ⇩ \n\+ \ ┌──────────────────────────────────┐ \n\+ \ │ < Left : Bool, Right : Natural > │ A union type with two alternatives \n\+ \ └──────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ The type of the second alternative is ❰Natural❱ \n\+ \ \n\+ \ \n\+ \However, these alternatives can only be annotated with types. For example, the \n\+ \following union types are " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────────┐ \n\+ \ │ < Left : Bool, Right : 1 > │ Invalid union type \n\+ \ └────────────────────────────┘ \n\+ \ ⇧ \n\+ \ This is a term and not a type \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────┐ \n\+ \ │ < Left : Bool, Right : Type > │ Invalid union type \n\+ \ └───────────────────────────────┘ \n\+ \ ⇧ \n\+ \ This is a kind and not a type \n\+ \ \n\+ \ \n\+ \Some common reasons why you might get this error: \n\+ \ \n\+ \● You accidentally typed ❰:❱ instead of ❰=❱ for a union literal with one \n\+ \ alternative: \n\+ \ \n\+ \ ┌─────────────────┐ \n\+ \ │ < Example : 1 > │ \n\+ \ └─────────────────┘ \n\+ \ ⇧ \n\+ \ This could be ❰=❱ instead \n\+ \ \n\+ \ \n\+ \────────────────────────────────────────────────────────────────────────────────\n\+ \ \n\+ \You provided a union type with an alternative named: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... annotated with the following expression which is not a type: \n\+ \ \n\+ \↳ " <> txt1 <> " \n"+ where+ txt0 = build k+ txt1 = build expr0++prettyTypeMessage (ListAppendMismatch expr0 expr1) = ErrorMessages {..}+ where+ short = "You can only append ❰List❱s with matching element types"++ long =+ "Explanation: You can append two ❰List❱s using the ❰#❱ operator, like this: \n\+ \ \n\+ \ \n\+ \ ┌────────────────────┐ \n\+ \ │ [1, 2, 3] # [4, 5] │ \n\+ \ └────────────────────┘ \n\+ \ \n\+ \ \n\+ \... but you cannot append two ❰List❱s if they have different element types. \n\+ \For example, the following expression is " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ These elements have type ❰Integer❱ \n\+ \ ⇩ \n\+ \ ┌───────────────────────────┐ \n\+ \ │ [1, 2, 3] # [True, False] │ Invalid: the element types don't match \n\+ \ └───────────────────────────┘ \n\+ \ ⇧ \n\+ \ These elements have type ❰Bool❱ \n\+ \ \n\+ \ \n\+ \────────────────────────────────────────────────────────────────────────────────\n\+ \ \n\+ \You tried to append a ❰List❱ thas has elements of type: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... with another ❰List❱ that has elements of type: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... and those two types do not match \n"+ where+ txt0 = build expr0+ txt1 = build expr1++prettyTypeMessage (DuplicateAlternative k) = ErrorMessages {..}+ where+ short = "Duplicate union alternative"++ long =+ "Explanation: Unions may not have two alternatives that share the same name \n\+ \ \n\+ \For example, the following expressions are " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ ┌─────────────────────────────┐ \n\+ \ │ < foo = True | foo : Text > │ Invalid: ❰foo❱ appears twice \n\+ \ └─────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────────────┐ \n\+ \ │ < foo = 1 | bar : Bool | bar : Text > │ Invalid: ❰bar❱ appears twice \n\+ \ └───────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \You have more than one alternative named: \n\+ \ \n\+ \↳ " <> txt0 <> " \n"+ where+ txt0 = build k++prettyTypeMessage (MustCombineARecord c expr0 expr1) = ErrorMessages {..}+ where+ short = "You can only combine records"++ long =+ "Explanation: You can combine records using the ❰" <> op <> "❱ operator, like this:\n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────────────────┐ \n\+ \ │ { foo = 1, bar = \"ABC\" } " <> op <> " { baz = True } │ \n\+ \ └───────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \ ┌─────────────────────────────────────────────┐ \n\+ \ │ λ(r : { foo : Bool }) → r " <> op <> " { bar = \"ABC\" } │ \n\+ \ └─────────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \... but you cannot combine values that are not records. \n\+ \ \n\+ \For example, the following expressions are " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ ┌──────────────────────────────┐ \n\+ \ │ { foo = 1, bar = \"ABC\" } " <> op <> " 1 │ \n\+ \ └──────────────────────────────┘ \n\+ \ ⇧ \n\+ \ Invalid: Not a record \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────────────────┐ \n\+ \ │ { foo = 1, bar = \"ABC\" } " <> op <> " { baz : Bool } │ \n\+ \ └───────────────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ Invalid: This is a record type and not a record\n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────────────────┐ \n\+ \ │ { foo = 1, bar = \"ABC\" } " <> op <> " < baz = True > │ \n\+ \ └───────────────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ Invalid: This is a union and not a record \n\+ \ \n\+ \ \n\+ \You tried to combine the following value: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... which is not a record, but is actually a: \n\+ \ \n\+ \↳ " <> txt1 <> " \n"+ where+ op = build c+ txt0 = build expr0+ txt1 = build expr1++prettyTypeMessage (FieldCollision k) = ErrorMessages {..}+ where+ short = "Field collision"++ long =+ "Explanation: You can combine records if they don't share any fields in common, \n\+ \like this: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────────────────┐ \n\+ \ │ { foo = 1, bar = \"ABC\" } ∧ { baz = True } │ \n\+ \ └───────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────────────────────┐ \n\+ \ │ λ(r : { baz : Bool}) → { foo = 1 } ∧ r │ \n\+ \ └────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \... but you cannot merge two records that share the same field \n\+ \ \n\+ \For example, the following expression is " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────────────────┐ \n\+ \ │ { foo = 1, bar = \"ABC\" } ∧ { foo = True } │ Invalid: Colliding ❰foo❱ fields\n\+ \ └───────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \Some common reasons why you might get this error: \n\+ \ \n\+ \● You tried to use ❰∧❱ to update a field's value, like this: \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────────────────────┐ \n\+ \ │ { foo = 1, bar = \"ABC\" } ∧ { foo = 2 } │ \n\+ \ └────────────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ Invalid attempt to update ❰foo❱'s value to ❰2❱\n\+ \ \n\+ \ Field updates are intentionally not allowed as the Dhall language discourages \n\+ \ patch-oriented programming \n\+ \ \n\+ \────────────────────────────────────────────────────────────────────────────────\n\+ \ \n\+ \You combined two records that share the following field: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... which is not allowed \n"+ where+ txt0 = build k++prettyTypeMessage (MustMergeARecord expr0 expr1) = ErrorMessages {..}+ where+ short = "❰merge❱ expects a record of handlers"++ 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 = +2 | Right : Bool > │ \n\+ \ │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │ \n\+ \ │ in merge handlers union : Bool │ \n\+ \ └─────────────────────────────────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \... but the first argument to ❰merge❱ must be a record and not some other type. \n\+ \ \n\+ \For example, the following expression is " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ ┌─────────────────────────────────────────┐ \n\+ \ │ let handler = λ(x : Bool) → x │ \n\+ \ │ in merge handler < Foo = True > : True │ \n\+ \ └─────────────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ Invalid: ❰handler❱ isn't a record \n\+ \ \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\+ \ you ❰merge❱ an empty union: \n\+ \ \n\+ \ \n\+ \ ┌──────────────────────────────────────────┐ \n\+ \ │ λ(x : <>) → λ(a : Type) → merge {} x : a │ \n\+ \ └──────────────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ This should be ❰{=}❱ instead \n\+ \ \n\+ \ \n\+ \────────────────────────────────────────────────────────────────────────────────\n\+ \ \n\+ \You provided the following handler: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... which is not a record, but is actually a value of type: \n\+ \ \n\+ \↳ " <> txt1 <> " \n"+ where+ txt0 = build expr0+ txt1 = build expr1++prettyTypeMessage (MustMergeUnion expr0 expr1) = ErrorMessages {..}+ where+ short = "❰merge❱ expects a union"++ 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 = +2 | Right : Bool > │ \n\+ \ │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │ \n\+ \ │ in merge handlers union : Bool │ \n\+ \ └─────────────────────────────────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \... but the second argument to ❰merge❱ must be a union and not some other type. \n\+ \ \n\+ \For example, the following expression is " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ ┌──────────────────────────────────────────┐ \n\+ \ │ let handlers = { Foo = λ(x : Bool) → x } │ \n\+ \ │ in merge handlers True : True │ \n\+ \ └──────────────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ Invalid: ❰True❱ isn't a union \n\+ \ \n\+ \ \n\+ \You tried to ❰merge❱ this expression: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... which is not a union, but is actually a value of type: \n\+ \ \n\+ \↳ " <> txt1 <> " \n"+ where+ txt0 = build expr0+ txt1 = build expr1++prettyTypeMessage (UnusedHandler ks) = ErrorMessages {..}+ where+ short = "Unused handler"++ 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 = +2 | Right : Bool > │ \n\+ \ │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │ \n\+ \ │ in merge handlers union : Bool │ \n\+ \ └─────────────────────────────────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \... but you must provide exactly one handler per alternative in the union. You \n\+ \cannot supply extra handlers \n\+ \ \n\+ \For example, the following expression is " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────────────┐ \n\+ \ │ let union = < Left = +2 > │ The ❰Right❱ alternative is missing\n\+ \ │ in let handlers = │ \n\+ \ │ { Left = Natural/even │ \n\+ \ │ , Right = λ(x : Bool) → x │ Invalid: ❰Right❱ handler isn't used\n\+ \ │ } │ \n\+ \ │ in merge handlers union : Bool │ \n\+ \ └───────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \You provided the following handlers: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... which had no matching alternatives in the union you tried to ❰merge❱ \n"+ where+ txt0 = build (Text.intercalate ", " (Data.Set.toList ks))++prettyTypeMessage (MissingHandler ks) = ErrorMessages {..}+ where+ short = "Missing handler"++ 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 = +2 | Right : Bool > │ \n\+ \ │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │ \n\+ \ │ in merge handlers union : Bool │ \n\+ \ └─────────────────────────────────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \... but you must provide exactly one handler per alternative in the union. You \n\+ \cannot omit any handlers \n\+ \ \n\+ \For example, the following expression is " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ Invalid: Missing ❰Right❱ handler \n\+ \ ⇩ \n\+ \ ┌─────────────────────────────────────────────────┐ \n\+ \ │ let handlers = { Left = Natural/even } │ \n\+ \ │ in let union = < Left = +2 | Right : Bool > │ \n\+ \ │ in merge handlers union : Bool │ \n\+ \ └─────────────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \Note that you need to provide handlers for other alternatives even if those \n\+ \alternatives are never used \n\+ \ \n\+ \You need to supply the following handlers: \n\+ \ \n\+ \↳ " <> txt0 <> " \n"+ where+ txt0 = build (Text.intercalate ", " (Data.Set.toList ks))++prettyTypeMessage MissingMergeType =+ ErrorMessages {..}+ where+ short = "An empty ❰merge❱ requires a type annotation"++ long =+ "Explanation: A ❰merge❱ does not require a type annotation if the union has at \n\+ \least one alternative, like this \n\+ \ \n\+ \ \n\+ \ ┌─────────────────────────────────────────────────────────────────────┐ \n\+ \ │ let union = < Left = +2 | Right : Bool > │ \n\+ \ │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │ \n\+ \ │ in merge handlers union │ \n\+ \ └─────────────────────────────────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \However, you must provide a type annotation when merging an empty union: \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────────────┐ \n\+ \ │ λ(a : <>) → merge {=} a : Bool │ \n\+ \ └────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ This can be any type \n\+ \ \n\+ \ \n\+ \You can provide any type at all as the annotation, since merging an empty \n\+ \union can produce any type of output \n"++prettyTypeMessage (HandlerInputTypeMismatch expr0 expr1 expr2) =+ ErrorMessages {..}+ where+ short = "Wrong handler input 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 = +2 | Right : Bool > │ \n\+ \ │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │ \n\+ \ │ in merge handlers union : Bool │ \n\+ \ └─────────────────────────────────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \... as long as the input type of each handler function matches the type of the \n\+ \corresponding alternative: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────────────────────────────────┐ \n\+ \ │ union : < Left : Natural | Right : Bool > │ \n\+ \ └───────────────────────────────────────────────────────────┘ \n\+ \ ⇧ ⇧ \n\+ \ These must match These must match \n\+ \ ⇩ ⇩ \n\+ \ ┌───────────────────────────────────────────────────────────┐ \n\+ \ │ handlers : { Left : Natural → Bool, Right : Bool → Bool } │ \n\+ \ └───────────────────────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \For example, the following expression is " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ Invalid: Doesn't match the type of the ❰Right❱ alternative \n\+ \ ⇩ \n\+ \ ┌──────────────────────────────────────────────────────────────────────┐ \n\+ \ │ let handlers = { Left = Natural/even | Right = λ(x : Text) → x } │ \n\+ \ │ in let union = < Left = +2 | Right : Bool > │ \n\+ \ │ in merge handlers union : Bool │ \n\+ \ └──────────────────────────────────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \Your handler for the following alternative: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... needs to accept an input value of type: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... but actually accepts an input value of a different type: \n\+ \ \n\+ \↳ " <> txt2 <> " \n"+ where+ txt0 = build expr0+ txt1 = build expr1+ txt2 = build expr2++prettyTypeMessage (InvalidHandlerOutputType expr0 expr1 expr2) =+ ErrorMessages {..}+ where+ short = "Wrong handler output 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 = +2 | Right : Bool > │ \n\+ \ │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │ \n\+ \ │ in merge handlers union : Bool │ \n\+ \ └─────────────────────────────────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \... as long as the output type of each handler function matches the declared type\n\+ \of the result: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────────────────────────────────┐ \n\+ \ │ handlers : { Left : Natural → Bool, Right : Bool → Bool } │ \n\+ \ └───────────────────────────────────────────────────────────┘ \n\+ \ ⇧ ⇧ \n\+ \ These output types ... \n\+ \ \n\+ \ ... must match the declared type of the ❰merge❱ \n\+ \ ⇩ \n\+ \ ┌─────────────────────────────┐ \n\+ \ │ merge handlers union : Bool │ \n\+ \ └─────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \For example, the following expression is " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ ┌──────────────────────────────────────────────────────────────────────┐ \n\+ \ │ let union = < Left = +2 | Right : Bool > │ \n\+ \ │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │ \n\+ \ │ in merge handlers union : Text │ \n\+ \ └──────────────────────────────────────────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ Invalid: Doesn't match output of either handler\n\+ \ \n\+ \ \n\+ \Your handler for the following alternative: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... needs to return an output value of type: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... but actually returns an output value of a different type: \n\+ \ \n\+ \↳ " <> txt2 <> " \n"+ where+ txt0 = build expr0+ txt1 = build expr1+ txt2 = build expr2++prettyTypeMessage (HandlerOutputTypeMismatch key0 expr0 key1 expr1) =+ ErrorMessages {..}+ where+ short = "Handlers should have the same output 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 = +2 | Right : Bool > │ \n\+ \ │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │ \n\+ \ │ in merge handlers union │ \n\+ \ └─────────────────────────────────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \... as long as the output type of each handler function is the same: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────────────────────────────────┐ \n\+ \ │ handlers : { Left : Natural → Bool, Right : Bool → Bool } │ \n\+ \ └───────────────────────────────────────────────────────────┘ \n\+ \ ⇧ ⇧ \n\+ \ These output types both match \n\+ \ \n\+ \ \n\+ \For example, the following expression is " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ ┌─────────────────────────────────────────────────┐ \n\+ \ │ let union = < Left = +2 | Right : Bool > │ \n\+ \ │ in let handlers = │ \n\+ \ │ { Left = λ(x : Natural) → x │ This outputs ❰Natural❱ \n\+ \ │ , Right = λ(x : Bool ) → x │ This outputs ❰Bool❱ \n\+ \ │ } │ \n\+ \ │ in merge handlers union │ \n\+ \ └─────────────────────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ Invalid: The handlers in this record don't have matching outputs\n\+ \ \n\+ \ \n\+ \The handler for the ❰" <> txt0 <> "❱ alternative has this output type: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... but the handler for the ❰" <> txt2 <> "❱ alternative has this output type instead:\n\+ \ \n\+ \↳ " <> txt3 <> " \n"+ where+ txt0 = build key0+ txt1 = build expr0+ txt2 = build key1+ txt3 = build expr1++prettyTypeMessage (HandlerNotAFunction k expr0) = ErrorMessages {..}+ where+ short = "Handler is not a function"++ 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 = +2 | Right : Bool > │ \n\+ \ │ in let handlers = { Left = Natural/even, Right = λ(x : Bool) → x } │ \n\+ \ │ in merge handlers union : Bool │ \n\+ \ └─────────────────────────────────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \... as long as each handler is a function \n\+ \ \n\+ \For example, the following expression is " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ ┌─────────────────────────────────────────┐ \n\+ \ │ merge { Foo = True } < Foo = 1 > : Bool │ \n\+ \ └─────────────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ Invalid: Not a function \n\+ \ \n\+ \ \n\+ \Your handler for this alternative: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... has the following type: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... which is not the type of a function \n"+ where+ txt0 = build k+ txt1 = build expr0++prettyTypeMessage (NotARecord k expr0 expr1) = ErrorMessages {..}+ where+ short = "Not a record"++ long =+ "Explanation: You can only access fields on records, like this: \n\+ \ \n\+ \ \n\+ \ ┌─────────────────────────────────┐ \n\+ \ │ { foo = True, bar = \"ABC\" }.foo │ This is valid ... \n\+ \ └─────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────────────────┐ \n\+ \ │ λ(r : { foo : Bool, bar : Text }) → r.foo │ ... and so is this \n\+ \ └───────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \... but you cannot access fields on non-record expressions \n\+ \ \n\+ \For example, the following expression is " <> _NOT <> " valid: \n\+ \ \n\+ \ \n\+ \ ┌───────┐ \n\+ \ │ 1.foo │ \n\+ \ └───────┘ \n\+ \ ⇧ \n\+ \ Invalid: Not a record \n\+ \ \n\+ \ \n\+ \Some common reasons why you might get this error: \n\+ \ \n\+ \● You accidentally try to access a field of a union instead of a record, like \n\+ \ this: \n\+ \ \n\+ \ \n\+ \ ┌─────────────────┐ \n\+ \ │ < foo : a >.foo │ \n\+ \ └─────────────────┘ \n\+ \ ⇧ \n\+ \ This is a union, not a record \n\+ \ \n\+ \ \n\+ \────────────────────────────────────────────────────────────────────────────────\n\+ \ \n\+ \You tried to access a field named: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... on the following expression which is not a record: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... but is actually an expression of type: \n\+ \ \n\+ \↳ " <> txt2 <> " \n"+ where+ txt0 = build k+ txt1 = build expr0+ txt2 = build expr1++prettyTypeMessage (MissingField k expr0) = ErrorMessages {..}+ where+ short = "Missing record field"++ long =+ "Explanation: You can only access fields on records, like this: \n\+ \ \n\+ \ \n\+ \ ┌─────────────────────────────────┐ \n\+ \ │ { foo = True, bar = \"ABC\" }.foo │ This is valid ... \n\+ \ └─────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────────────────────┐ \n\+ \ │ λ(r : { foo : Bool, bar : Text }) → r.foo │ ... and so is this \n\+ \ └───────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \... but you can only access fields if they are present \n\+ \ \n\+ \For example, the following expression is " <> _NOT <> " valid: \n\+ \ \n\+ \ ┌─────────────────────────────────┐ \n\+ \ │ { foo = True, bar = \"ABC\" }.qux │ \n\+ \ └─────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ Invalid: the record has no ❰qux❱ field \n\+ \ \n\+ \You tried to access a field named: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... but the field is missing because the record only defines the following fields:\n\+ \ \n\+ \↳ " <> txt1 <> " \n"+ where+ txt0 = build k+ txt1 = build expr0++prettyTypeMessage (CantAnd expr0 expr1) =+ buildBooleanOperator "&&" expr0 expr1++prettyTypeMessage (CantOr expr0 expr1) =+ buildBooleanOperator "||" expr0 expr1++prettyTypeMessage (CantEQ expr0 expr1) =+ buildBooleanOperator "==" expr0 expr1++prettyTypeMessage (CantNE expr0 expr1) =+ buildBooleanOperator "/=" expr0 expr1++prettyTypeMessage (CantTextAppend expr0 expr1) = ErrorMessages {..}+ where+ short = "❰++❱ only works on ❰Text❱"++ long =+ "Explanation: The ❰++❱ operator expects two arguments that have type ❰Text❱ \n\+ \ \n\+ \For example, this is a valid use of ❰++❱: \n\+ \ \n\+ \ \n\+ \ ┌────────────────┐ \n\+ \ │ \"ABC\" ++ \"DEF\" │ \n\+ \ └────────────────┘ \n\+ \ \n\+ \ \n\+ \Some common reasons why you might get this error: \n\+ \ \n\+ \● You might have thought that ❰++❱ was the operator to combine two lists: \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────┐ \n\+ \ │ [1, 2, 3] ++ [4, 5, 6] │ Not valid \n\+ \ └────────────────────────┘ \n\+ \ \n\+ \ \n\+ \ ... but the list concatenation operator is actually ❰#❱: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────┐ \n\+ \ │ [1, 2, 3] # [4, 5, 6] │ Valid \n\+ \ └───────────────────────┘ \n\+ \ \n\+ \ \n\+ \────────────────────────────────────────────────────────────────────────────────\n\+ \ \n\+ \You provided this argument: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... which does not have type ❰Text❱ but instead has type: \n\+ \ \n\+ \↳ " <> txt1 <> " \n"+ where+ txt0 = build expr0+ txt1 = build expr1++prettyTypeMessage (CantListAppend expr0 expr1) = ErrorMessages {..}+ where+ short = "❰#❱ only works on ❰List❱s"++ long =+ "Explanation: The ❰#❱ operator expects two arguments that are both ❰List❱s \n\+ \ \n\+ \For example, this is a valid use of ❰#❱: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────┐ \n\+ \ │ [1, 2, 3] # [4, 5, 6] │ \n\+ \ └───────────────────────┘ \n\+ \ \n\+ \ \n\+ \────────────────────────────────────────────────────────────────────────────────\n\+ \ \n\+ \You provided this argument: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... which is not a ❰List❱ but instead has type: \n\+ \ \n\+ \↳ " <> txt1 <> " \n"+ where+ txt0 = build expr0+ txt1 = build expr1++prettyTypeMessage (CantAdd expr0 expr1) =+ buildNaturalOperator "+" expr0 expr1++prettyTypeMessage (CantMultiply expr0 expr1) =+ buildNaturalOperator "*" expr0 expr1++prettyTypeMessage (NoDependentTypes expr0 expr1) = ErrorMessages {..}+ where+ short = "No dependent types"++ long =+ "Explanation: The Dhall programming language does not allow functions from terms \n\+ \to types. These function types are also known as \"dependent function types\" \n\+ \because you have a type whose value \"depends\" on the value of a term. \n\+ \ \n\+ \For example, this is " <> _NOT <> " a legal function type: \n\+ \ \n\+ \ \n\+ \ ┌─────────────┐ \n\+ \ │ Bool → Type │ \n\+ \ └─────────────┘ \n\+ \ \n\+ \ \n\+ \Similarly, this is " <> _NOT <> " legal code: \n\+ \ \n\+ \ \n\+ \ ┌────────────────────────────────────────────────────┐ \n\+ \ │ λ(Vector : Natural → Type → Type) → Vector +0 Text │ \n\+ \ └────────────────────────────────────────────────────┘ \n\+ \ ⇧ \n\+ \ Invalid dependent type \n\+ \ \n\+ \ \n\+ \Your function type is invalid because the input has type: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... and the output has kind: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... which makes this a forbidden dependent function type \n"+ where+ txt0 = build expr0+ txt1 = build expr1++prettyTypeMessage (NoDependentLet expr0 expr1) = ErrorMessages {..}+ where+ short = "No dependent ❰let❱"++ long =+ "Explanation: The Dhall programming language does not allow ❰let❱ expressions \n\+ \from terms to types. These ❰let❱ expressions are also known as \"dependent ❰let❱\n\+ \expressions\" because you have a type whose value depends on the value of a term.\n\+ \ \n\+ \The Dhall language forbids these dependent ❰let❱ expressions in order to \n\+ \guarantee that ❰let❱ expressions of the form: \n\+ \ \n\+ \ \n\+ \ ┌────────────────────┐ \n\+ \ │ let x : t = r in e │ \n\+ \ └────────────────────┘ \n\+ \ \n\+ \ \n\+ \... are always equivalent to: \n\+ \ \n\+ \ \n\+ \ ┌──────────────────┐ \n\+ \ │ (λ(x : t) → e) r │ \n\+ \ └──────────────────┘ \n\+ \ \n\+ \ \n\+ \This means that both expressions should normalize to the same result and if one \n\+ \of the two fails to type check then the other should fail to type check, too. \n\+ \ \n\+ \For this reason, the following is " <> _NOT <> " legal code: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────┐ \n\+ \ │ let x = 2 in Text │ \n\+ \ └───────────────────┘ \n\+ \ \n\+ \ \n\+ \... because the above ❰let❱ expression is equivalent to: \n\+ \ \n\+ \ \n\+ \ ┌─────────────────────────────┐ \n\+ \ │ let x : Integer = 2 in Text │ \n\+ \ └─────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \... which in turn must be equivalent to: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────────┐ \n\+ \ │ (λ(x : Integer) → Text) 2 │ \n\+ \ └───────────────────────────┘ \n\+ \ \n\+ \ \n\+ \... which in turn fails to type check because this sub-expression: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────┐ \n\+ \ │ λ(x : Integer) → Text │ \n\+ \ └───────────────────────┘ \n\+ \ \n\+ \ \n\+ \... has type: \n\+ \ \n\+ \ \n\+ \ ┌───────────────────────┐ \n\+ \ │ ∀(x : Integer) → Text │ \n\+ \ └───────────────────────┘ \n\+ \ \n\+ \ \n\+ \... which is a forbidden dependent function type (i.e. a function from a term to\n\+ \a type). Therefore the equivalent ❰let❱ expression is also forbidden. \n\+ \ \n\+ \Your ❰let❱ expression is invalid because the input has type: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... and the output has kind: \n\+ \ \n\+ \↳ " <> txt1 <> " \n\+ \ \n\+ \... which makes this a forbidden dependent ❰let❱ expression \n"+ where+ txt0 = build expr0+ txt1 = build expr1++buildBooleanOperator :: Text -> Expr s X -> Expr s X -> ErrorMessages+buildBooleanOperator operator expr0 expr1 = ErrorMessages {..}+ where+ short = "❰" <> txt2 <> "❱ only works on ❰Bool❱s"++ long =+ "Explanation: The ❰" <> txt2 <> "❱ operator expects two arguments that have type ❰Bool❱\n\+ \ \n\+ \For example, this is a valid use of ❰" <> txt2 <> "❱: \n\+ \ \n\+ \ \n\+ \ ┌───────────────┐ \n\+ \ │ True " <> txt2 <> " False │ \n\+ \ └───────────────┘ \n\+ \ \n\+ \ \n\+ \You provided this argument: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... which does not have type ❰Bool❱ but instead has type: \n\+ \ \n\+ \↳ " <> txt1 <> " \n"+ where+ txt0 = build expr0+ txt1 = build expr1++ txt2 = build operator++buildNaturalOperator :: Text -> Expr s X -> Expr s X -> ErrorMessages+buildNaturalOperator operator expr0 expr1 = ErrorMessages {..}+ where+ short = "❰" <> txt2 <> "❱ only works on ❰Natural❱s"++ long =+ "Explanation: The ❰" <> txt2 <> "❱ operator expects two arguments that have type ❰Natural❱\n\+ \ \n\+ \For example, this is a valid use of ❰" <> txt2 <> "❱: \n\+ \ \n\+ \ \n\+ \ ┌─────────┐ \n\+ \ │ +3 " <> txt2 <> " +5 │ \n\+ \ └─────────┘ \n\+ \ \n\+ \ \n\+ \Some common reasons why you might get this error: \n\+ \ \n\+ \● You might have tried to use an ❰Integer❱, which is " <> _NOT <> " allowed: \n\+ \ \n\+ \ \n\+ \ ┌─────────────────────────────────────────┐ \n\+ \ │ λ(x : Integer) → λ(y : Integer) → x " <> txt2 <> " y │ Not valid \n\+ \ └─────────────────────────────────────────┘ \n\+ \ \n\+ \ \n\+ \ You can only use ❰Natural❱ numbers \n\+ \ \n\+ \ \n\+ \● You might have mistakenly used an ❰Integer❱ literal, which is " <> _NOT <> " allowed:\n\+ \ \n\+ \ \n\+ \ ┌───────┐ \n\+ \ │ 2 " <> txt2 <> " 2 │ Not valid \n\+ \ └───────┘ \n\+ \ \n\+ \ \n\+ \ You need to prefix each literal with a ❰+❱ to transform them into ❰Natural❱ \n\+ \ literals, like this: \n\+ \ \n\+ \ \n\+ \ ┌─────────┐ \n\+ \ │ +2 " <> txt2 <> " +2 │ Valid \n\+ \ └─────────┘ \n\+ \ \n\+ \ \n\+ \────────────────────────────────────────────────────────────────────────────────\n\+ \ \n\+ \You provided this argument: \n\+ \ \n\+ \↳ " <> txt0 <> " \n\+ \ \n\+ \... which does not have type ❰Natural❱ but instead has type: \n\+ \ \n\+ \↳ " <> txt1 <> " \n"+ where+ txt0 = build expr0+ txt1 = build expr1++ txt2 = build operator -- | A structured type error that includes context data TypeError s = TypeError
tests/Examples.hs view
@@ -1,9 +1,7 @@ {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-} module Examples where -import qualified NeatInterpolation import qualified Test.Tasty import qualified Test.Tasty.HUnit import qualified Util@@ -273,1008 +271,778 @@ _Bool_and_0 :: TestTree _Bool_and_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/and ([True, False, True] : List Bool)-|]+ e <- Util.code "./Prelude/Bool/and ([True, False, True] : List Bool)" Util.assertNormalizesTo e "False" ) _Bool_and_1 :: TestTree _Bool_and_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/and ([] : List Bool)-|]+ e <- Util.code "./Prelude/Bool/and ([] : List Bool)" Util.assertNormalizesTo e "True" ) _Bool_build_0 :: TestTree _Bool_build_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/build (λ(bool : Type) → λ(true : bool) → λ(false : bool) → true)-|]+ e <- Util.code+ "./Prelude/Bool/build (λ(bool : Type) → λ(true : bool) → λ(false : bool) → true)" Util.assertNormalizesTo e "True" ) _Bool_build_1 :: TestTree _Bool_build_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/build (λ(bool : Type) → λ(true : bool) → λ(false : bool) → false)-|]+ e <- Util.code+ "./Prelude/Bool/build (λ(bool : Type) → λ(true : bool) → λ(false : bool) → false)" Util.assertNormalizesTo e "False" ) _Bool_even_0 :: TestTree _Bool_even_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/even ([False, True, False] : List Bool)-|]+ e <- Util.code "./Prelude/Bool/even ([False, True, False] : List Bool)" Util.assertNormalizesTo e "True" ) _Bool_even_1 :: TestTree _Bool_even_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/even ([False, True] : List Bool)-|]+ e <- Util.code "./Prelude/Bool/even ([False, True] : List Bool)" Util.assertNormalizesTo e "False" ) _Bool_even_2 :: TestTree _Bool_even_2 = Test.Tasty.HUnit.testCase "Example #2" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/even ([False] : List Bool)-|]+ e <- Util.code "./Prelude/Bool/even ([False] : List Bool)" Util.assertNormalizesTo e "False" ) _Bool_even_3 :: TestTree _Bool_even_3 = Test.Tasty.HUnit.testCase "Example #3" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/even ([] : List Bool)-|]+ e <- Util.code "./Prelude/Bool/even ([] : List Bool)" Util.assertNormalizesTo e "True" ) _Bool_fold_0 :: TestTree _Bool_fold_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/fold True Integer 0 1-|]+ e <- Util.code "./Prelude/Bool/fold True Integer 0 1" Util.assertNormalizesTo e "0" ) _Bool_fold_1 :: TestTree _Bool_fold_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/fold False Integer 0 1-|]+ e <- Util.code "./Prelude/Bool/fold False Integer 0 1" Util.assertNormalizesTo e "1" ) _Bool_not_0 :: TestTree _Bool_not_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/not True-|]+ e <- Util.code "./Prelude/Bool/not True" Util.assertNormalizesTo e "False" ) _Bool_not_1 :: TestTree _Bool_not_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/not False-|]+ e <- Util.code "./Prelude/Bool/not False" Util.assertNormalizesTo e "True" ) _Bool_odd_0 :: TestTree _Bool_odd_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/odd ([True, False, True] : List Bool)-|]+ e <- Util.code "./Prelude/Bool/odd ([True, False, True] : List Bool)" Util.assertNormalizesTo e "False" ) _Bool_odd_1 :: TestTree _Bool_odd_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/odd ([True, False] : List Bool)-|]+ e <- Util.code "./Prelude/Bool/odd ([True, False] : List Bool)" Util.assertNormalizesTo e "True" ) _Bool_odd_2 :: TestTree _Bool_odd_2 = Test.Tasty.HUnit.testCase "Example #2" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/odd ([True] : List Bool)-|]+ e <- Util.code "./Prelude/Bool/odd ([True] : List Bool)" Util.assertNormalizesTo e "True" ) _Bool_odd_3 :: TestTree _Bool_odd_3 = Test.Tasty.HUnit.testCase "Example #3" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/odd ([] : List Bool)-|]+ e <- Util.code "./Prelude/Bool/odd ([] : List Bool)" Util.assertNormalizesTo e "False" ) _Bool_or_0 :: TestTree _Bool_or_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/or ([True, False, True] : List Bool)-|]+ e <- Util.code "./Prelude/Bool/or ([True, False, True] : List Bool)" Util.assertNormalizesTo e "True" ) _Bool_or_1 :: TestTree _Bool_or_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/or ([] : List Bool)-|]+ e <- Util.code "./Prelude/Bool/or ([] : List Bool)" Util.assertNormalizesTo e "False" ) _Bool_show_0 :: TestTree _Bool_show_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/show True-|]+ e <- Util.code "./Prelude/Bool/show True" Util.assertNormalizesTo e "\"True\"" ) _Bool_show_1 :: TestTree _Bool_show_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Bool/show False-|]+ e <- Util.code "./Prelude/Bool/show False" Util.assertNormalizesTo e "\"False\"" ) _Double_show_0 :: TestTree _Double_show_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Double/show -3.1-|]+ e <- Util.code "./Prelude/Double/show -3.1" Util.assertNormalizesTo e "\"-3.1\"" ) _Double_show_1 :: TestTree _Double_show_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Double/show 0.4-|]+ e <- Util.code "./Prelude/Double/show 0.4" Util.assertNormalizesTo e "\"0.4\"" ) _Integer_show_0 :: TestTree _Integer_show_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Integer/show -3-|]+ e <- Util.code "./Prelude/Integer/show -3" Util.assertNormalizesTo e "\"-3\"" ) _Integer_show_1 :: TestTree _Integer_show_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Integer/show 0-|]+ e <- Util.code "./Prelude/Integer/show 0" Util.assertNormalizesTo e "\"0\"" ) _List_all_0 :: TestTree _List_all_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/all Natural Natural/even ([+2, +3, +5] : List Natural)-|]+ e <- Util.code "./Prelude/List/all Natural Natural/even ([+2, +3, +5] : List Natural)" Util.assertNormalizesTo e "False" ) _List_all_1 :: TestTree _List_all_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/all Natural Natural/even ([] : List Natural)-|]+ e <- Util.code "./Prelude/List/all Natural Natural/even ([] : List Natural)" Util.assertNormalizesTo e "True" ) _List_any_0 :: TestTree _List_any_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/any Natural Natural/even ([+2, +3, +5] : List Natural)-|]+ e <- Util.code "./Prelude/List/any Natural Natural/even ([+2, +3, +5] : List Natural)" Util.assertNormalizesTo e "True" ) _List_any_1 :: TestTree _List_any_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/any Natural Natural/even ([] : List Natural)-|]+ e <- Util.code "./Prelude/List/any Natural Natural/even ([] : List Natural)" Util.assertNormalizesTo e "False" ) _List_build_0 :: TestTree _List_build_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/build-Text-( λ(list : Type)-→ λ(cons : Text → list → list)-→ λ(nil : list)-→ cons "ABC" (cons "DEF" nil)-)-|]+ e <- Util.code+ "./Prelude/List/build \n\+ \Text \n\+ \( λ(list : Type) \n\+ \→ λ(cons : Text → list → list) \n\+ \→ λ(nil : list) \n\+ \→ cons \"ABC\" (cons \"DEF\" nil)\n\+ \) \n" Util.assertNormalizesTo e "[\"ABC\", \"DEF\"] : List Text" ) _List_build_1 :: TestTree _List_build_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/build-Text-( λ(list : Type)-→ λ(cons : Text → list → list)-→ λ(nil : list)-→ nil-)-|]+ e <- Util.code+ "./Prelude/List/build \n\+ \Text \n\+ \( λ(list : Type) \n\+ \→ λ(cons : Text → list → list)\n\+ \→ λ(nil : list) \n\+ \→ nil \n\+ \) \n" Util.assertNormalizesTo e "[] : List Text" ) _List_concat_0 :: TestTree _List_concat_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/concat Integer-( [ [0, 1, 2] : List Integer- , [3, 4] : List Integer- , [5, 6, 7, 8] : List Integer- ] : List (List Integer)-)-|]+ e <- Util.code+ "./Prelude/List/concat Integer \n\+ \( [ [0, 1, 2] : List Integer\n\+ \ , [3, 4] : List Integer\n\+ \ , [5, 6, 7, 8] : List Integer\n\+ \ ] : List (List Integer) \n\+ \) \n" Util.assertNormalizesTo e "[0, 1, 2, 3, 4, 5, 6, 7, 8] : List Integer" ) _List_concat_1 :: TestTree _List_concat_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/concat Integer-( [ [] : List Integer- , [] : List Integer- , [] : List Integer- ] : List (List Integer)-)-|]+ e <- Util.code+ "./Prelude/List/concat Integer\n\+ \( [ [] : List Integer \n\+ \ , [] : List Integer \n\+ \ , [] : List Integer \n\+ \ ] : List (List Integer)\n\+ \) \n" Util.assertNormalizesTo e "[] : List Integer" ) _List_filter_0 :: TestTree _List_filter_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/filter Natural Natural/even ([+2, +3, +5] : List Natural)-|]+ e <- Util.code+ "./Prelude/List/filter Natural Natural/even ([+2, +3, +5] : List Natural)" Util.assertNormalizesTo e "[+2] : List Natural" ) _List_filter_1 :: TestTree _List_filter_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/filter Natural Natural/odd ([+2, +3, +5] : List Natural)-|]+ e <- Util.code "./Prelude/List/filter Natural Natural/odd ([+2, +3, +5] : List Natural)" Util.assertNormalizesTo e "[+3, +5] : List Natural" ) _List_fold_0 :: TestTree _List_fold_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|- ./Prelude/List/fold- Natural- ([+2, +3, +5] : List Natural)- Natural- (λ(x : Natural) → λ(y : Natural) → x + y)- +0-|]+ e <- Util.code+ "./Prelude/List/fold \n\+ \Natural \n\+ \([+2, +3, +5] : List Natural) \n\+ \Natural \n\+ \(λ(x : Natural) → λ(y : Natural) → x + y)\n\+ \+0 \n" Util.assertNormalizesTo e "+10" ) _List_fold_1 :: TestTree _List_fold_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|- λ(nil : Natural)-→ ./Prelude/List/fold- Natural- ([+2, +3, +5] : List Natural)- Natural- (λ(x : Natural) → λ(y : Natural) → x + y)- nil-|]+ e <- Util.code+ " λ(nil : Natural) \n\+ \→ ./Prelude/List/fold \n\+ \ Natural \n\+ \ ([+2, +3, +5] : List Natural) \n\+ \ Natural \n\+ \ (λ(x : Natural) → λ(y : Natural) → x + y)\n\+ \ nil \n" Util.assertNormalizesTo e "λ(nil : Natural) → +2 + +3 + +5 + nil" ) _List_fold_2 :: TestTree _List_fold_2 = Test.Tasty.HUnit.testCase "Example #2" (do- e <- Util.code [NeatInterpolation.text|- λ(list : Type)-→ λ(cons : Natural → list → list)-→ λ(nil : list)-→ ./Prelude/List/fold Natural ([+2, +3, +5] : List Natural) list cons nil-|]+ e <- Util.code+ " λ(list : Type) \n\+ \→ λ(cons : Natural → list → list) \n\+ \→ λ(nil : list) \n\+ \→ ./Prelude/List/fold Natural ([+2, +3, +5] : List Natural) list cons nil\n" Util.assertNormalizesTo e "λ(list : Type) → λ(cons : Natural → list → list) → λ(nil : list) → cons +2 (cons +3 (cons +5 nil))" ) _List_generate_0 :: TestTree _List_generate_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/generate +5 Bool Natural/even-|]+ e <- Util.code "./Prelude/List/generate +5 Bool Natural/even" Util.assertNormalizesTo e "[True, False, True, False, True] : List Bool" ) _List_generate_1 :: TestTree _List_generate_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/generate +0 Bool Natural/even-|]+ e <- Util.code "./Prelude/List/generate +0 Bool Natural/even" Util.assertNormalizesTo e "[] : List Bool" ) _List_head_0 :: TestTree _List_head_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/head Integer ([0, 1, 2] : List Integer)-|]+ e <- Util.code "./Prelude/List/head Integer ([0, 1, 2] : List Integer)" Util.assertNormalizesTo e "[0] : Optional Integer" ) _List_head_1 :: TestTree _List_head_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/head Integer ([] : List Integer)-|]+ e <- Util.code "./Prelude/List/head Integer ([] : List Integer)" Util.assertNormalizesTo e "[] : Optional Integer" ) _List_indexed_0 :: TestTree _List_indexed_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/indexed Bool ([True, False, True] : List Bool)-|]+ e <- Util.code "./Prelude/List/indexed Bool ([True, False, True] : List Bool)" Util.assertNormalizesTo e "[{ index = +0, value = True }, { index = +1, value = False }, { index = +2, value = True }] : List { index : Natural, value : Bool }" ) _List_indexed_1 :: TestTree _List_indexed_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/indexed Bool ([] : List Bool)-|]+ e <- Util.code "./Prelude/List/indexed Bool ([] : List Bool)" Util.assertNormalizesTo e "[] : List { index : Natural, value : Bool }" ) _List_iterate_0 :: TestTree _List_iterate_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/iterate +10 Natural (λ(x : Natural) → x * +2) +1-|]+ e <- Util.code "./Prelude/List/iterate +10 Natural (λ(x : Natural) → x * +2) +1" Util.assertNormalizesTo e "[+1, +2, +4, +8, +16, +32, +64, +128, +256, +512] : List Natural" ) _List_iterate_1 :: TestTree _List_iterate_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/iterate +0 Natural (λ(x : Natural) → x * +2) +1-|]+ e <- Util.code "./Prelude/List/iterate +0 Natural (λ(x : Natural) → x * +2) +1" Util.assertNormalizesTo e "[] : List Natural" ) _List_last_0 :: TestTree _List_last_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/last Integer ([0, 1, 2] : List Integer)-|]+ e <- Util.code "./Prelude/List/last Integer ([0, 1, 2] : List Integer)" Util.assertNormalizesTo e "[2] : Optional Integer" ) _List_last_1 :: TestTree _List_last_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/last Integer ([] : List Integer)-|]+ e <- Util.code "./Prelude/List/last Integer ([] : List Integer)" Util.assertNormalizesTo e "[] : Optional Integer" ) _List_length_0 :: TestTree _List_length_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/length Integer ([0, 1, 2] : List Integer)-|]+ e <- Util.code "./Prelude/List/length Integer ([0, 1, 2] : List Integer)" Util.assertNormalizesTo e "+3" ) _List_length_1 :: TestTree _List_length_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/length Integer ([] : List Integer)-|]+ e <- Util.code "./Prelude/List/length Integer ([] : List Integer)" Util.assertNormalizesTo e "+0" ) _List_map_0 :: TestTree _List_map_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/map Natural Bool Natural/even ([+2, +3, +5] : List Natural)-|]+ e <- Util.code+ "./Prelude/List/map Natural Bool Natural/even ([+2, +3, +5] : List Natural)" Util.assertNormalizesTo e "[True, False, False] : List Bool" ) _List_map_1 :: TestTree _List_map_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/map Natural Bool Natural/even ([] : List Natural)-|]+ e <- Util.code "./Prelude/List/map Natural Bool Natural/even ([] : List Natural)" Util.assertNormalizesTo e "[] : List Bool" ) _List_null_0 :: TestTree _List_null_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/null Integer ([0, 1, 2] : List Integer)-|]+ e <- Util.code "./Prelude/List/null Integer ([0, 1, 2] : List Integer)" Util.assertNormalizesTo e "False" ) _List_null_1 :: TestTree _List_null_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/null Integer ([] : List Integer)-|]+ e <- Util.code "./Prelude/List/null Integer ([] : List Integer)" Util.assertNormalizesTo e "True" ) _List_replicate_0 :: TestTree _List_replicate_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/replicate +9 Integer 1-|]+ e <- Util.code "./Prelude/List/replicate +9 Integer 1" Util.assertNormalizesTo e "[1, 1, 1, 1, 1, 1, 1, 1, 1] : List Integer" ) _List_replicate_1 :: TestTree _List_replicate_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/replicate +0 Integer 1-|]+ e <- Util.code "./Prelude/List/replicate +0 Integer 1" Util.assertNormalizesTo e "[] : List Integer" ) _List_reverse_0 :: TestTree _List_reverse_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/reverse Integer ([0, 1, 2] : List Integer)-|]+ e <- Util.code "./Prelude/List/reverse Integer ([0, 1, 2] : List Integer)" Util.assertNormalizesTo e "[2, 1, 0] : List Integer" ) _List_reverse_1 :: TestTree _List_reverse_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/reverse Integer ([] : List Integer)-|]+ e <- Util.code "./Prelude/List/reverse Integer ([] : List Integer)" Util.assertNormalizesTo e "[] : List Integer" ) _List_shifted_0 :: TestTree _List_shifted_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/shifted-Bool-( [ [ { index = +0, value = True }- , { index = +1, value = True }- , { index = +2, value = True }- ] : List { index : Natural, value : Bool }- , [ { index = +0, value = False }- , { index = +1, value = False }- ] : List { index : Natural, value : Bool }- , [ { index = +0, value = True }- , { index = +1, value = True }- , { index = +2, value = True }- , { index = +3, value = True }- ] : List { index : Natural, value : Bool }- ] : List (List { index : Natural, value : Bool })-)-|]+ e <- Util.code+ "./Prelude/List/shifted \n\+ \Bool \n\+ \( [ [ { index = +0, value = True } \n\+ \ , { index = +1, value = True } \n\+ \ , { index = +2, value = True } \n\+ \ ] : List { index : Natural, value : Bool } \n\+ \ , [ { index = +0, value = False } \n\+ \ , { index = +1, value = False } \n\+ \ ] : List { index : Natural, value : Bool } \n\+ \ , [ { index = +0, value = True } \n\+ \ , { index = +1, value = True } \n\+ \ , { index = +2, value = True } \n\+ \ , { index = +3, value = True } \n\+ \ ] : List { index : Natural, value : Bool } \n\+ \ ] : List (List { index : Natural, value : Bool })\n\+ \) \n" Util.assertNormalizesTo e "[{ index = +0, value = True }, { index = +1, value = True }, { index = +2, value = True }, { index = +3, value = False }, { index = +4, value = False }, { index = +5, value = True }, { index = +6, value = True }, { index = +7, value = True }, { index = +8, value = True }] : List { index : Natural, value : Bool }" ) _List_shifted_1 :: TestTree _List_shifted_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/shifted Bool ([] : List (List { index : Natural, value : Bool }))-|]+ e <- Util.code+ "./Prelude/List/shifted Bool ([] : List (List { index : Natural, value : Bool }))" Util.assertNormalizesTo e "[] : List { index : Natural, value : Bool }" ) _List_unzip_0 :: TestTree _List_unzip_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/unzip-Text-Bool-( [ { _1 = "ABC", _2 = True }- , { _1 = "DEF", _2 = False }- , { _1 = "GHI", _2 = True }- ] : List { _1 : Text, _2 : Bool }-)-|]+ e <- Util.code+ "./Prelude/List/unzip \n\+ \Text \n\+ \Bool \n\+ \( [ { _1 = \"ABC\", _2 = True } \n\+ \ , { _1 = \"DEF\", _2 = False } \n\+ \ , { _1 = \"GHI\", _2 = True } \n\+ \ ] : List { _1 : Text, _2 : Bool }\n\+ \) \n" Util.assertNormalizesTo e "{ _1 = [\"ABC\", \"DEF\", \"GHI\"] : List Text, _2 = [True, False, True] : List Bool }" ) _List_unzip_1 :: TestTree _List_unzip_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/List/unzip Text Bool ([] : List { _1 : Text, _2 : Bool })-|]+ e <- Util.code "./Prelude/List/unzip Text Bool ([] : List { _1 : Text, _2 : Bool })" Util.assertNormalizesTo e "{ _1 = [] : List Text, _2 = [] : List Bool }" ) _Monoid_00 :: TestTree _Monoid_00 = Test.Tasty.HUnit.testCase "Example #0"- (Util.assertTypeChecks [NeatInterpolation.text|-./Prelude/Bool/and- : ./Prelude/Monoid Bool-|] )+ (Util.assertTypeChecks "./Prelude/Bool/and : ./Prelude/Monoid Bool") _Monoid_01 :: TestTree _Monoid_01 = Test.Tasty.HUnit.testCase "Example #1"- (Util.assertTypeChecks [NeatInterpolation.text|-./Prelude/Bool/or- : ./Prelude/Monoid Bool-|] )+ (Util.assertTypeChecks "./Prelude/Bool/or : ./Prelude/Monoid Bool") _Monoid_02 :: TestTree _Monoid_02 = Test.Tasty.HUnit.testCase "Example #2"- (Util.assertTypeChecks [NeatInterpolation.text|-./Prelude/Bool/even- : ./Prelude/Monoid Bool-|] )+ (Util.assertTypeChecks "./Prelude/Bool/even : ./Prelude/Monoid Bool") _Monoid_03 :: TestTree _Monoid_03 = Test.Tasty.HUnit.testCase "Example #3"- (Util.assertTypeChecks[NeatInterpolation.text|-./Prelude/Bool/odd- : ./Prelude/Monoid Bool-|] )+ (Util.assertTypeChecks "./Prelude/Bool/odd : ./Prelude/Monoid Bool") _Monoid_04 :: TestTree _Monoid_04 = Test.Tasty.HUnit.testCase "Example #4"- (Util.assertTypeChecks [NeatInterpolation.text|-./Prelude/List/concat- : ∀(a : Type) → ./Prelude/Monoid (List a)-|] )+ (Util.assertTypeChecks+ "./Prelude/List/concat : ∀(a : Type) → ./Prelude/Monoid (List a)" ) _Monoid_05 :: TestTree _Monoid_05 = Test.Tasty.HUnit.testCase "Example #5"- (Util.assertTypeChecks [NeatInterpolation.text|-./Prelude/List/shifted- : ∀(a : Type) → ./Prelude/Monoid (List { index : Natural, value : a })-|] )+ (Util.assertTypeChecks+ "./Prelude/List/shifted \n\+ \ : ∀(a : Type) → ./Prelude/Monoid (List { index : Natural, value : a })\n" ) _Monoid_06 :: TestTree _Monoid_06 = Test.Tasty.HUnit.testCase "Example #6"- (Util.assertTypeChecks [NeatInterpolation.text|-./Prelude/Natural/sum- : ./Prelude/Monoid Natural-|] )+ (Util.assertTypeChecks "./Prelude/Natural/sum : ./Prelude/Monoid Natural") _Monoid_07 :: TestTree _Monoid_07 = Test.Tasty.HUnit.testCase "Example #7"- (Util.assertTypeChecks [NeatInterpolation.text|-./Prelude/Natural/product- : ./Prelude/Monoid Natural-|] )+ (Util.assertTypeChecks "./Prelude/Natural/product : ./Prelude/Monoid Natural") _Monoid_08 :: TestTree _Monoid_08 = Test.Tasty.HUnit.testCase "Example #8"- (Util.assertTypeChecks [NeatInterpolation.text|-./Prelude/Optional/head- : ∀(a : Type) → ./Prelude/Monoid (Optional a)-|] )+ (Util.assertTypeChecks+ "./Prelude/Optional/head : ∀(a : Type) → ./Prelude/Monoid (Optional a)" ) _Monoid_09 :: TestTree _Monoid_09 = Test.Tasty.HUnit.testCase "Example #9"- (Util.assertTypeChecks [NeatInterpolation.text|-./Prelude/Optional/last- : ∀(a : Type) → ./Prelude/Monoid (Optional a)-|] )+ (Util.assertTypeChecks+ "./Prelude/Optional/last : ∀(a : Type) → ./Prelude/Monoid (Optional a)" ) _Monoid_10 :: TestTree _Monoid_10 = Test.Tasty.HUnit.testCase "Example #10"- (Util.assertTypeChecks [NeatInterpolation.text|-./Prelude/Text/concat- : ./Prelude/Monoid Text-|] )+ (Util.assertTypeChecks "./Prelude/Text/concat : ./Prelude/Monoid Text") _Natural_build_0 :: TestTree _Natural_build_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/build-( λ(natural : Type)-→ λ(succ : natural → natural)-→ λ(zero : natural)-→ succ (succ (succ zero))-)-|]+ e <- Util.code+ "./Prelude/Natural/build \n\+ \( λ(natural : Type) \n\+ \→ λ(succ : natural → natural)\n\+ \→ λ(zero : natural) \n\+ \→ succ (succ (succ zero)) \n\+ \) \n" Util.assertNormalizesTo e "+3" ) _Natural_build_1 :: TestTree _Natural_build_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/build-( λ(natural : Type)-→ λ(succ : natural → natural)-→ λ(zero : natural)-→ zero-)-|]+ e <- Util.code+ "./Prelude/Natural/build \n\+ \( λ(natural : Type) \n\+ \→ λ(succ : natural → natural)\n\+ \→ λ(zero : natural) \n\+ \→ zero \n\+ \) \n" Util.assertNormalizesTo e "+0" ) _Natural_enumerate_0 :: TestTree _Natural_enumerate_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/enumerate +10-|]+ e <- Util.code "./Prelude/Natural/enumerate +10" Util.assertNormalizesTo e "[+0, +1, +2, +3, +4, +5, +6, +7, +8, +9] : List Natural" ) _Natural_enumerate_1 :: TestTree _Natural_enumerate_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/enumerate +0-|]+ e <- Util.code "./Prelude/Natural/enumerate +0" Util.assertNormalizesTo e "[] : List Natural" ) _Natural_even_0 :: TestTree _Natural_even_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/even +3-|]+ e <- Util.code "./Prelude/Natural/even +3" Util.assertNormalizesTo e "False" ) _Natural_even_1 :: TestTree _Natural_even_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/even +0-|]+ e <- Util.code "./Prelude/Natural/even +0" Util.assertNormalizesTo e "True" ) _Natural_fold_0 :: TestTree _Natural_fold_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/fold +3 Natural (λ(x : Natural) → +5 * x) +1-|]+ e <- Util.code "./Prelude/Natural/fold +3 Natural (λ(x : Natural) → +5 * x) +1" Util.assertNormalizesTo e "+125" ) _Natural_fold_1 :: TestTree _Natural_fold_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-λ(zero : Natural) → ./Prelude/Natural/fold +3 Natural (λ(x : Natural) → +5 * x) zero-|]+ e <- Util.code+ "λ(zero : Natural) → ./Prelude/Natural/fold +3 Natural (λ(x : Natural) → +5 * x) zero" Util.assertNormalizesTo e "λ(zero : Natural) → +5 * +5 * +5 * zero" ) _Natural_fold_2 :: TestTree _Natural_fold_2 = Test.Tasty.HUnit.testCase "Example #2" (do- e <- Util.code [NeatInterpolation.text|- λ(natural : Type)-→ λ(succ : natural → natural)-→ λ(zero : natural)-→ ./Prelude/Natural/fold +3 natural succ zero-|]+ e <- Util.code+ " λ(natural : Type) \n\+ \→ λ(succ : natural → natural) \n\+ \→ λ(zero : natural) \n\+ \→ ./Prelude/Natural/fold +3 natural succ zero\n" Util.assertNormalizesTo e "λ(natural : Type) → λ(succ : natural → natural) → λ(zero : natural) → succ (succ (succ zero))" ) _Natural_isZero_0 :: TestTree _Natural_isZero_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/isZero +2-|]+ e <- Util.code "./Prelude/Natural/isZero +2" Util.assertNormalizesTo e "False" ) _Natural_isZero_1 :: TestTree _Natural_isZero_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/isZero +0-|]+ e <- Util.code "./Prelude/Natural/isZero +0" Util.assertNormalizesTo e "True" ) _Natural_odd_0 :: TestTree _Natural_odd_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/odd +3-|]+ e <- Util.code "./Prelude/Natural/odd +3" Util.assertNormalizesTo e "True" ) _Natural_odd_1 :: TestTree _Natural_odd_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/odd +0-|]+ e <- Util.code "./Prelude/Natural/odd +0" Util.assertNormalizesTo e "False" ) _Natural_product_0 :: TestTree _Natural_product_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/product ([+2, +3, +5] : List Natural)-|]+ e <- Util.code "./Prelude/Natural/product ([+2, +3, +5] : List Natural)" Util.assertNormalizesTo e "+30" ) _Natural_product_1 :: TestTree _Natural_product_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/product ([] : List Natural)-|]+ e <- Util.code "./Prelude/Natural/product ([] : List Natural)" Util.assertNormalizesTo e "+1" ) _Natural_show_0 :: TestTree _Natural_show_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/show +3-|]+ e <- Util.code "./Prelude/Natural/show +3" Util.assertNormalizesTo e "\"+3\"" ) _Natural_show_1 :: TestTree _Natural_show_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/show +0-|]+ e <- Util.code "./Prelude/Natural/show +0" Util.assertNormalizesTo e "\"+0\"" ) _Natural_sum_0 :: TestTree _Natural_sum_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/sum ([+2, +3, +5] : List Natural)-|]+ e <- Util.code "./Prelude/Natural/sum ([+2, +3, +5] : List Natural)" Util.assertNormalizesTo e "+10" ) _Natural_sum_1 :: TestTree _Natural_sum_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/sum ([] : List Natural)-|]+ e <- Util.code "./Prelude/Natural/sum ([] : List Natural)" Util.assertNormalizesTo e "+0" ) _Natural_toInteger_0 :: TestTree _Natural_toInteger_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/toInteger +3-|]+ e <- Util.code "./Prelude/Natural/toInteger +3" Util.assertNormalizesTo e "3" ) _Natural_toInteger_1 :: TestTree _Natural_toInteger_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Natural/toInteger +0-|]+ e <- Util.code "./Prelude/Natural/toInteger +0" Util.assertNormalizesTo e "0" ) _Optional_all_0 :: TestTree _Optional_all_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/all Natural Natural/even ([+3] : Optional Natural)-|]+ e <- Util.code "./Prelude/Optional/all Natural Natural/even ([+3] : Optional Natural)" Util.assertNormalizesTo e "False" ) _Optional_all_1 :: TestTree _Optional_all_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/all Natural Natural/even ([] : Optional Natural)-|]+ e <- Util.code "./Prelude/Optional/all Natural Natural/even ([] : Optional Natural)" Util.assertNormalizesTo e "True" ) _Optional_any_0 :: TestTree _Optional_any_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/any Natural Natural/even ([+2] : Optional Natural)-|]+ e <- Util.code "./Prelude/Optional/any Natural Natural/even ([+2] : Optional Natural)" Util.assertNormalizesTo e "True" ) _Optional_any_1 :: TestTree _Optional_any_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/any Natural Natural/even ([] : Optional Natural)-|]+ e <- Util.code "./Prelude/Optional/any Natural Natural/even ([] : Optional Natural)" Util.assertNormalizesTo e "False" ) _Optional_build_0 :: TestTree _Optional_build_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/build-Integer-( λ(optional : Type)-→ λ(just : Integer → optional)-→ λ(nothing : optional)-→ just 1-)-|]+ e <- Util.code+ "./Prelude/Optional/build \n\+ \Integer \n\+ \( λ(optional : Type) \n\+ \→ λ(just : Integer → optional)\n\+ \→ λ(nothing : optional) \n\+ \→ just 1 \n\+ \) \n" Util.assertNormalizesTo e "[1] : Optional Integer" ) _Optional_build_1 :: TestTree _Optional_build_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/build-Integer-( λ(optional : Type)-→ λ(just : Integer → optional)-→ λ(nothing : optional)-→ nothing-)-|]+ e <- Util.code+ "./Prelude/Optional/build \n\+ \Integer \n\+ \( λ(optional : Type) \n\+ \→ λ(just : Integer → optional)\n\+ \→ λ(nothing : optional) \n\+ \→ nothing \n\+ \) \n" Util.assertNormalizesTo e "[] : Optional Integer" ) _Optional_concat_0 :: TestTree _Optional_concat_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/concat Integer ([[1] : Optional Integer] : Optional (Optional Integer))-|]+ e <- Util.code+ "./Prelude/Optional/concat Integer ([[1] : Optional Integer] : Optional (Optional Integer))" Util.assertNormalizesTo e "[1] : Optional Integer" ) _Optional_concat_1 :: TestTree _Optional_concat_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/concat Integer ([[] : Optional Integer] : Optional (Optional Integer))-|]+ e <- Util.code+ "./Prelude/Optional/concat Integer ([[] : Optional Integer] : Optional (Optional Integer))" Util.assertNormalizesTo e "[] : Optional Integer" ) _Optional_concat_2 :: TestTree _Optional_concat_2 = Test.Tasty.HUnit.testCase "Example #2" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/concat Integer ([] : Optional (Optional Integer))-|]+ e <- Util.code "./Prelude/Optional/concat Integer ([] : Optional (Optional Integer))" Util.assertNormalizesTo e "[] : Optional Integer" ) _Optional_filter_0 :: TestTree _Optional_filter_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/filter Natural Natural/even ([+2] : Optional Natural)-|]+ e <- Util.code+ "./Prelude/Optional/filter Natural Natural/even ([+2] : Optional Natural)" Util.assertNormalizesTo e "[+2] : Optional Natural" ) _Optional_filter_1 :: TestTree _Optional_filter_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/filter Natural Natural/odd ([+2] : Optional Natural)-|]+ e <- Util.code "./Prelude/Optional/filter Natural Natural/odd ([+2] : Optional Natural)" Util.assertNormalizesTo e "[] : Optional Natural" ) _Optional_fold_0 :: TestTree _Optional_fold_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/fold Integer ([2] : Optional Integer) Integer (λ(x : Integer) → x) 0-|]+ e <- Util.code+ "./Prelude/Optional/fold Integer ([2] : Optional Integer) Integer (λ(x : Integer) → x) 0" Util.assertNormalizesTo e "2" ) _Optional_fold_1 :: TestTree _Optional_fold_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/fold Integer ([] : Optional Integer) Integer (λ(x : Integer) → x) 0-|]+ e <- Util.code+ "./Prelude/Optional/fold Integer ([] : Optional Integer) Integer (λ(x : Integer) → x) 0" Util.assertNormalizesTo e "0" ) _Optional_head_0 :: TestTree _Optional_head_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/head-Integer-( [[] : Optional Integer, [1] : Optional Integer, [2] : Optional Integer]- : List (Optional Integer)-)-|]+ e <- Util.code+ "./Prelude/Optional/head \n\+ \Integer \n\+ \( [[] : Optional Integer, [1] : Optional Integer, [2] : Optional Integer]\n\+ \ : List (Optional Integer) \n\+ \) \n" Util.assertNormalizesTo e "[1] : Optional Integer" ) _Optional_head_1 :: TestTree _Optional_head_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/head-Integer-([[] : Optional Integer, [] : Optional Integer] : List (Optional Integer))-|]+ e <- Util.code+ "./Prelude/Optional/head \n\+ \Integer \n\+ \([[] : Optional Integer, [] : Optional Integer] : List (Optional Integer))\n" Util.assertNormalizesTo e "[] : Optional Integer" ) _Optional_head_2 :: TestTree _Optional_head_2 = Test.Tasty.HUnit.testCase "Example #2" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/head Integer ([] : List (Optional Integer))-|]+ e <- Util.code "./Prelude/Optional/head Integer ([] : List (Optional Integer))" Util.assertNormalizesTo e "[] : Optional Integer" ) _Optional_last_0 :: TestTree _Optional_last_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/last-Integer-( [[] : Optional Integer, [1] : Optional Integer, [2] : Optional Integer]- : List (Optional Integer)-)-|]+ e <- Util.code+ "./Prelude/Optional/last \n\+ \Integer \n\+ \( [[] : Optional Integer, [1] : Optional Integer, [2] : Optional Integer]\n\+ \ : List (Optional Integer) \n\+ \) \n" Util.assertNormalizesTo e "[2] : Optional Integer" ) _Optional_last_1 :: TestTree _Optional_last_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/last-Integer-([[] : Optional Integer, [] : Optional Integer] : List (Optional Integer))-|]+ e <- Util.code+ "./Prelude/Optional/last \n\+ \Integer \n\+ \([[] : Optional Integer, [] : Optional Integer] : List (Optional Integer))\n" Util.assertNormalizesTo e "[] : Optional Integer" ) _Optional_last_2 :: TestTree _Optional_last_2 = Test.Tasty.HUnit.testCase "Example #2" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/last Integer ([] : List (Optional Integer))-|]+ e <- Util.code "./Prelude/Optional/last Integer ([] : List (Optional Integer))" Util.assertNormalizesTo e "[] : Optional Integer" ) _Optional_map_0 :: TestTree _Optional_map_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/map Natural Bool Natural/even ([+3] : Optional Natural)-|]+ e <- Util.code+ "./Prelude/Optional/map Natural Bool Natural/even ([+3] : Optional Natural)" Util.assertNormalizesTo e "[False] : Optional Bool" ) _Optional_length_0 :: TestTree _Optional_length_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/length Integer ([2] : Optional Integer)-|]+ e <- Util.code "./Prelude/Optional/length Integer ([2] : Optional Integer)" Util.assertNormalizesTo e "+1" ) _Optional_length_1 :: TestTree _Optional_length_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/length Integer ([] : Optional Integer)-|]+ e <- Util.code "./Prelude/Optional/length Integer ([] : Optional Integer)" Util.assertNormalizesTo e "+0" ) _Optional_map_1 :: TestTree _Optional_map_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/map Natural Bool Natural/even ([] : Optional Natural)-|]+ e <- Util.code+ "./Prelude/Optional/map Natural Bool Natural/even ([] : Optional Natural)" Util.assertNormalizesTo e "[] : Optional Bool" ) _Optional_null_0 :: TestTree _Optional_null_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/null Integer ([2] : Optional Integer)-|]+ e <- Util.code "./Prelude/Optional/null Integer ([2] : Optional Integer)" Util.assertNormalizesTo e "False" ) _Optional_null_1 :: TestTree _Optional_null_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/null Integer ([] : Optional Integer)-|]+ e <- Util.code "./Prelude/Optional/null Integer ([] : Optional Integer)" Util.assertNormalizesTo e "True" ) _Optional_toList_0 :: TestTree _Optional_toList_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/toList Integer ([1] : Optional Integer)-|]+ e <- Util.code "./Prelude/Optional/toList Integer ([1] : Optional Integer)" Util.assertNormalizesTo e "[1] : List Integer" ) _Optional_toList_1 :: TestTree _Optional_toList_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/toList Integer ([] : Optional Integer)-|]+ e <- Util.code "./Prelude/Optional/toList Integer ([] : Optional Integer)" Util.assertNormalizesTo e "[] : List Integer" ) _Optional_unzip_0 :: TestTree _Optional_unzip_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/unzip-Text-Bool-([{ _1 = "ABC", _2 = True }] : Optional { _1 : Text, _2 : Bool })-|]+ e <- Util.code+ "./Prelude/Optional/unzip \n\+ \Text \n\+ \Bool \n\+ \([{ _1 = \"ABC\", _2 = True }] : Optional { _1 : Text, _2 : Bool })\n" Util.assertNormalizesTo e "{ _1 = [\"ABC\"] : Optional Text, _2 = [True] : Optional Bool }" ) _Optional_unzip_1 :: TestTree _Optional_unzip_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Optional/unzip Text Bool ([] : Optional { _1 : Text, _2 : Bool })-|]+ e <- Util.code+ "./Prelude/Optional/unzip Text Bool ([] : Optional { _1 : Text, _2 : Bool })" Util.assertNormalizesTo e "{ _1 = [] : Optional Text, _2 = [] : Optional Bool }" ) _Text_concat_0 :: TestTree _Text_concat_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Text/concat (["ABC", "DEF", "GHI"] : List Text)-|]+ e <- Util.code "./Prelude/Text/concat ([\"ABC\", \"DEF\", \"GHI\"] : List Text)" Util.assertNormalizesTo e "\"ABCDEFGHI\"" ) _Text_concat_1 :: TestTree _Text_concat_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Text/concat ([] : List Text)-|]+ e <- Util.code "./Prelude/Text/concat ([] : List Text)" Util.assertNormalizesTo e "\"\"" ) _Text_concatMap_0 :: TestTree _Text_concatMap_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Text//concatMap Integer (λ(n : Integer) → "${Integer/show n} ") [0, 1, 2]-|]+ e <- Util.code+ "./Prelude/Text/concatMap Integer (λ(n : Integer) → \"${Integer/show n} \") [0, 1, 2]" Util.assertNormalizesTo e "\"0 1 2 \"" ) _Text_concatMap_1 :: TestTree _Text_concatMap_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Text/concatMap Integer (λ(n : Integer) → "${Integer/show n} ") ([] : List Integer)-|]+ e <- Util.code+ "./Prelude/Text/concatMap Integer (λ(n : Integer) → \"${Integer/show n} \") ([] : List Integer)" Util.assertNormalizesTo e "\"\"" ) _Text_concatMapSep_0 :: TestTree _Text_concatMapSep_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Text/concatMapSep ", " Integer Integer/show [0, 1, 2]-|]+ e <- Util.code "./Prelude/Text/concatMapSep \", \" Integer Integer/show [0, 1, 2]" Util.assertNormalizesTo e "\"0, 1, 2\"" ) _Text_concatMapSep_1 :: TestTree _Text_concatMapSep_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Text/concatMapSep ", " Integer Integer/show ([] : List Integer)-|]+ e <- Util.code+ "./Prelude/Text/concatMapSep \", \" Integer Integer/show ([] : List Integer)" Util.assertNormalizesTo e "\"\"" ) _Text_concatSep_0 :: TestTree _Text_concatSep_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Text/concatSep ", " ["ABC", "DEF", "GHI"]-|]+ e <- Util.code "./Prelude/Text/concatSep \", \" [\"ABC\", \"DEF\", \"GHI\"]" Util.assertNormalizesTo e "\"ABC, DEF, GHI\"" ) _Text_concatSep_1 :: TestTree _Text_concatSep_1 = Test.Tasty.HUnit.testCase "Example #1" (do- e <- Util.code [NeatInterpolation.text|-./Prelude/Text/concatSep ", " ([] : List Text)-|]+ e <- Util.code "./Prelude/Text/concatSep \", \" ([] : List Text)" Util.assertNormalizesTo e "\"\"" )
tests/Normalization.hs view
@@ -1,19 +1,20 @@ {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-} module Normalization (normalizationTests) where -import Dhall.Core-import qualified NeatInterpolation-import Test.Tasty-import Test.Tasty.HUnit-import Util (code, normalize', assertNormalizesTo, assertNormalized)+import Data.Monoid ((<>))+import Dhall.Core+import Dhall.Context+import Test.Tasty+import Test.Tasty.HUnit+import Util normalizationTests :: TestTree normalizationTests = testGroup "normalization" [ constantFolding , conversions , fusion+ , customization ] constantFolding :: TestTree@@ -29,6 +30,36 @@ , naturalToInteger ] +customization :: TestTree+customization = testGroup "customization"+ [simpleCustomization+ ,nestedReduction]++simpleCustomization :: TestTree+simpleCustomization = testCase "simpleCustomization" $ do+ let tyCtx = insert "min" (Pi "_" Natural (Pi "_" Natural Natural)) empty + valCtx e = case e of+ (App (App (Var (V "min" 0)) (NaturalLit x)) (NaturalLit y)) -> Just (NaturalLit (min x y))+ _ -> Nothing+ e <- codeWith tyCtx "min (min +11 +12) +8 + +1" + assertNormalizesToWith valCtx e "+9"++nestedReduction :: TestTree+nestedReduction = testCase "doubleReduction" $ do+ minType <- insert "min" <$> code "Natural → Natural → Natural"+ fiveorlessType <- insert "fiveorless" <$> code "Natural → Natural"+ wurbleType <- insert "wurble" <$> code "Natural → Integer"+ let tyCtx = minType . fiveorlessType . wurbleType $ empty+ valCtx e = case e of+ (App (App (Var (V "min" 0)) (NaturalLit x)) (NaturalLit y)) -> Just (NaturalLit (min x y))+ (App (Var (V "wurble" 0)) (NaturalLit x)) -> Just+ (App (Var (V "fiveorless" 0)) (NaturalPlus (NaturalLit x) (NaturalLit 2))) + (App (Var (V "fiveorless" 0)) (NaturalLit x)) -> Just+ (App (App (Var (V "min" 0)) (NaturalLit x)) (NaturalPlus (NaturalLit 3) (NaturalLit 2)))+ _ -> Nothing+ e <- codeWith tyCtx "wurble +6"+ assertNormalizesToWith valCtx e "+5"+ naturalPlus :: TestTree naturalPlus = testCase "natural plus" $ do e <- code "+1 + +2"@@ -58,9 +89,7 @@ optionalFold :: TestTree optionalFold = testGroup "Optional/fold" [ just, nothing ] where test label inp out = testCase label $ do- e <- code [NeatInterpolation.text|- Optional/fold Text ([$inp] : Optional Text) Natural (λ(j : Text) → +1) +2- |]+ e <- code ("Optional/fold Text ([" <> inp <> "] : Optional Text) Natural (λ(j : Text) → +1) +2") e `assertNormalizesTo` out just = test "just" "\"foo\"" "+1" nothing = test "nothing" "" "+2"@@ -73,42 +102,39 @@ optionalBuild1 :: TestTree optionalBuild1 = testCase "reducible" $ do- e <- code [NeatInterpolation.text|-Optional/build-Natural-( λ(optional : Type)-→ λ(just : Natural → optional)-→ λ(nothing : optional)-→ just +1-)-|]+ e <- code+ "Optional/build \n\+ \Natural \n\+ \( λ(optional : Type) \n\+ \→ λ(just : Natural → optional)\n\+ \→ λ(nothing : optional) \n\+ \→ just +1 \n\+ \) \n" e `assertNormalizesTo` "[+1] : Optional Natural" optionalBuildShadowing :: TestTree optionalBuildShadowing = testCase "handles shadowing" $ do- e <- code [NeatInterpolation.text|-Optional/build-Integer-( λ(optional : Type)-→ λ(x : Integer → optional)-→ λ(x : optional)-→ x@1 1-)-|]+ e <- code+ "Optional/build \n\+ \Integer \n\+ \( λ(optional : Type) \n\+ \→ λ(x : Integer → optional)\n\+ \→ λ(x : optional) \n\+ \→ x@1 1 \n\+ \) \n" e `assertNormalizesTo` "[1] : Optional Integer" optionalBuildIrreducible :: TestTree optionalBuildIrreducible = testCase "irreducible" $ do- e <- code [NeatInterpolation.text|- λ(id : ∀(a : Type) → a → a)-→ Optional/build- Bool- ( λ(optional : Type)- → λ(just : Bool → optional)- → λ(nothing : optional)- → id optional (just True)- )-|]+ e <- code+ " λ(id : ∀(a : Type) → a → a) \n\+ \→ Optional/build \n\+ \ Bool \n\+ \ ( λ(optional : Type) \n\+ \ → λ(just : Bool → optional)\n\+ \ → λ(nothing : optional) \n\+ \ → id optional (just True) \n\+ \ ) \n" assertNormalized e fusion :: TestTree@@ -118,39 +144,36 @@ fuseOptionalBF :: TestTree fuseOptionalBF = testCase "fold . build" $ do- e0 <- code [NeatInterpolation.text|- λ( f- : ∀(optional : Type)- → ∀(just : Text → optional)- → ∀(nothing : optional)- → optional- )-→ Optional/fold- Text- ( Optional/build- Text- f- )-|]- e1 <- code [NeatInterpolation.text|- λ( f- : ∀(optional : Type)- → ∀(just : Text → optional)- → ∀(nothing : optional)- → optional- )-→ f-|]+ e0 <- code+ " λ( f \n\+ \ : ∀(optional : Type) \n\+ \ → ∀(just : Text → optional)\n\+ \ → ∀(nothing : optional) \n\+ \ → optional \n\+ \ ) \n\+ \→ Optional/fold \n\+ \ Text \n\+ \ ( Optional/build \n\+ \ Text \n\+ \ f \n\+ \ ) \n"+ e1 <- code+ " λ( f \n\+ \ : ∀(optional : Type) \n\+ \ → ∀(just : Text → optional)\n\+ \ → ∀(nothing : optional) \n\+ \ → optional \n\+ \ ) \n\+ \→ f \n" e0 `assertNormalizesTo` (Dhall.Core.pretty e1) fuseOptionalFB :: TestTree fuseOptionalFB = testCase "build . fold" $ do- test <- code [NeatInterpolation.text|-Optional/build-Text-( Optional/fold- Text- (["foo"] : Optional Text)-)-|]+ test <- code+ "Optional/build \n\+ \Text \n\+ \( Optional/fold \n\+ \ Text \n\+ \ ([\"foo\"] : Optional Text)\n\+ \) \n" test `assertNormalizesTo` "[\"foo\"] : Optional Text"
tests/Tutorial.hs view
@@ -1,14 +1,19 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-} module Tutorial where -import qualified NeatInterpolation+import qualified Data.Vector+import qualified Dhall import qualified Test.Tasty import qualified Test.Tasty.HUnit import qualified Util +import Dhall (Inject)+import GHC.Generics (Generic) import Test.Tasty (TestTree)+import Test.Tasty.HUnit ((@?=)) tutorialTests :: TestTree tutorialTests =@@ -17,24 +22,48 @@ [ _Interpolation_0 , _Interpolation_1 ]+ , Test.Tasty.testGroup "Functions"+ [ _Functions_0+ , _Functions_1+ , _Functions_2+ ] ] _Interpolation_0 :: TestTree _Interpolation_0 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|- let name = "John Doe"-in let age = 21-in "My name is $${name} and my age is $${Integer/show age}"-|]+ e <- Util.code+ " let name = \"John Doe\" \n\+ \in let age = 21 \n\+ \in \"My name is ${name} and my age is ${Integer/show age}\"\n" Util.assertNormalizesTo e "\"My name is John Doe and my age is 21\"" ) _Interpolation_1 :: TestTree-_Interpolation_1 = Test.Tasty.HUnit.testCase "Example #0" (do- e <- Util.code [NeatInterpolation.text|-''- for file in *; do- echo "Found ''$${file}"- done-''-|]+_Interpolation_1 = Test.Tasty.HUnit.testCase "Example #1" (do+ e <- Util.code+ "'' \n\+ \ for file in *; do \n\+ \ echo \"Found ''${file}\"\n\+ \ done \n\+ \'' \n" Util.assertNormalized e )++_Functions_0 :: TestTree+_Functions_0 = Test.Tasty.HUnit.testCase "Example #0" (do+ let text = "\\(n : Bool) -> [ n && True, n && False, n || True, n || False ]"+ makeBools <- Dhall.input Dhall.auto text+ makeBools True @?= Data.Vector.fromList [True,False,True,True] )++_Functions_1 :: TestTree+_Functions_1 = Test.Tasty.HUnit.testCase "Example #1" (do+ let text = "λ(x : Bool) → λ(y : Bool) → x && y"+ makeBools <- Dhall.input Dhall.auto text+ makeBools True False @?= False )++data Example0 = Example0 { foo :: Bool, bar :: Bool }+ deriving (Generic, Inject)++_Functions_2 :: TestTree+_Functions_2 = Test.Tasty.HUnit.testCase "Example #2" (do+ f <- Dhall.input Dhall.auto "λ(r : { foo : Bool, bar : Bool }) → r.foo && r.bar"+ f (Example0 { foo = True, bar = False }) @?= False+ f (Example0 { foo = True, bar = True }) @?= True )
tests/Util.hs view
@@ -1,19 +1,25 @@-{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-} module Util ( code+ , codeWith , normalize'+ , normalizeWith' , assertNormalizesTo+ , assertNormalizesToWith , assertNormalized , assertTypeChecks ) where import qualified Control.Exception import qualified Data.Functor+import Data.Bifunctor (first) import Data.Text (Text) import qualified Data.Text.Lazy import qualified Dhall.Core-import Dhall.Core (Expr)+import Dhall.Core (Expr, Normalizer)+import qualified Dhall.Context+import Dhall.Context (Context) import qualified Dhall.Import import qualified Dhall.Parser import Dhall.Parser (Src)@@ -24,22 +30,34 @@ normalize' :: Expr Src X -> Data.Text.Lazy.Text normalize' = Dhall.Core.pretty . Dhall.Core.normalize +normalizeWith' :: Normalizer X -> Expr Src X -> Data.Text.Lazy.Text+normalizeWith' ctx = Dhall.Core.pretty . Dhall.Core.normalizeWith ctx+ code :: Data.Text.Text -> IO (Expr Src X)-code strictText = do+code = codeWith Dhall.Context.empty++codeWith :: Context (Expr Src X) -> Data.Text.Text -> IO (Expr Src X)+codeWith ctx strictText = do let lazyText = Data.Text.Lazy.fromStrict strictText expr0 <- case Dhall.Parser.exprFromText mempty lazyText of Left parseError -> Control.Exception.throwIO parseError Right expr0 -> return expr0 expr1 <- Dhall.Import.load expr0- case Dhall.TypeCheck.typeOf expr1 of+ case Dhall.TypeCheck.typeWith ctx expr1 of Left typeError -> Control.Exception.throwIO typeError Right _ -> return () return expr1 assertNormalizesTo :: Expr Src X -> Data.Text.Lazy.Text -> IO ()-assertNormalizesTo e expected = do+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 -> Data.Text.Lazy.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 ()