Improve type-inference of annotated list literals (#1366)

* Remove a redundant check of the type annotation.

* Replace dead code for processing the list elements
  with an error.

* Document the ListLit invariant.

Closes https://github.com/dhall-lang/dhall-haskell/issues/1359.
This commit is contained in:
Simon Jakobi 2019-10-02 07:17:03 +02:00 committed by mergify[bot]
parent a44fb420a5
commit 5a2ee1ca57
3 changed files with 43 additions and 28 deletions

View File

@ -509,8 +509,20 @@ data Expr s a
| TextShow
-- | > List ~ List
| List
-- | > ListLit (Just t ) [x, y, z] ~ [x, y, z] : t
-- | > ListLit (Just t ) [] ~ [] : t
-- > ListLit Nothing [x, y, z] ~ [x, y, z]
--
-- Invariant: A non-empty list literal is always represented as
-- @ListLit Nothing xs@.
--
-- When an annotated, non-empty list literal is parsed, it is represented
-- as
--
-- > Annot (ListLit Nothing [x, y, z]) t ~ [x, y, z] : t
-- Eventually we should have separate constructors for empty and non-empty
-- list literals. For now it's easier to check the invariant in @infer@.
-- See https://github.com/dhall-lang/dhall-haskell/issues/1359#issuecomment-537087234.
| ListLit (Maybe (Expr s a)) (Seq (Expr s a))
-- | > ListAppend x y ~ x # y
| ListAppend (Expr s a) (Expr s a)
@ -1685,7 +1697,7 @@ normalizeWithM ctx e0 = loop (denote e0)
TextShow -> pure TextShow
List -> pure List
ListLit t es
| Data.Sequence.null es -> ListLit <$> t' <*> es'
| Data.Sequence.null es -> ListLit <$> t' <*> pure Data.Sequence.empty
| otherwise -> ListLit Nothing <$> es'
where
t' = traverse loop t

View File

@ -639,7 +639,7 @@ eval !env t0 =
VPrim $ \ ~a ->
VPrim $ \case
VListLit t as | null as ->
VListLit t (Sequence.reverse as)
VListLit t as
VListLit _ as ->
VListLit Nothing (Sequence.reverse as)
t ->

View File

@ -595,35 +595,20 @@ infer typer = loop
die MissingListType
ListLit (Just _T) ts -> do
_ <- loop ctx _T
if Data.Sequence.null ts
then do
_ <- loop ctx _T
let _T' = eval values _T
let _T' = eval values _T
let _T'' = quote names _T'
let _T'' = quote names _T'
case _T' of
VList _T' -> do
tT' <- loop ctx (quote names _T')
case _T' of
VList _ -> return _T'
_ -> die (InvalidListType _T'')
case tT' of
VConst Type -> return ()
_ -> die (InvalidListType _T'')
_ -> do
die (InvalidListType _T'')
let process i t = do
_T' <- loop ctx t
if Eval.conv values _T' _T₁'
then return ()
else do
let _T'' = quote names _T'
die (InvalidListElement i _T'' t _T'')
traverseWithIndex_ process ts
return _T'
-- See https://github.com/dhall-lang/dhall-haskell/issues/1359.
else die ListLitInvariant
ListAppend x y -> do
tx' <- loop ctx x
@ -1279,6 +1264,7 @@ data TypeMessage s a
| MismatchedListElements Int (Expr s a) (Expr s a) (Expr s a)
| InvalidListElement Int (Expr s a) (Expr s a) (Expr s a)
| InvalidListType (Expr s a)
| ListLitInvariant
| InvalidSome (Expr s a) (Expr s a) (Expr s a)
| InvalidPredicate (Expr s a) (Expr s a)
| IfBranchMismatch (Expr s a) (Expr s a) (Expr s a) (Expr s a)
@ -2251,6 +2237,21 @@ prettyTypeMessage (IfBranchMismatch expr0 expr1 expr2 expr3) =
txt2 = insert expr2
txt3 = insert expr3
prettyTypeMessage (ListLitInvariant) = ErrorMessages {..}
where
short = "Internal error: A non-empty list literal violated an internal invariant"
long =
"Explanation: Internal error: A non-empty list literal violated an internal \n\
\invariant. \n\
\ \n\
\A non-empty list literal must always be represented as \n\
\ \n\
\ ListLit Nothing [x, y, ...] \n\
\ \n\
\Please file a bug report at https://github.com/dhall-lang/dhall-haskell/issues, \n\
\ideally including the offending source code. \n"
prettyTypeMessage (InvalidListType expr0) = ErrorMessages {..}
where
short = "Invalid type for ❰List❱"
@ -4306,6 +4307,8 @@ messageExpressions f m = case m of
InvalidListElement <$> pure a <*> f b <*> f c <*> f d
InvalidListType a ->
InvalidListType <$> f a
ListLitInvariant ->
pure ListLitInvariant
InvalidSome a b c ->
InvalidSome <$> f a <*> f b <*> f c
InvalidPredicate a b ->