Skip to content

Commit d287f02

Browse files
authored
Implement new simplifications for boolean logic (#316)
... as standardized in dhall-lang/dhall-lang#109
1 parent 5a9126b commit d287f02

File tree

14 files changed

+97
-29
lines changed

14 files changed

+97
-29
lines changed

dhall.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ Extra-Source-Files:
139139
tests/normalization/examples/Text/concatMap/*.dhall
140140
tests/normalization/examples/Text/concatMapSep/*.dhall
141141
tests/normalization/examples/Text/concatSep/*.dhall
142+
tests/normalization/simplifications/*.dhall
142143
tests/parser/*.dhall
143144
tests/regression/*.dhall
144145
tests/tutorial/*.dhall

src/Dhall/Core.hs

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ module Dhall.Core (
3232
, normalize
3333
, normalizeWith
3434
, Normalizer
35+
, judgmentallyEqual
3536
, subst
3637
, shift
3738
, isNormalized
@@ -1107,7 +1108,7 @@ alphaNormalize (Embed a) =
11071108
However, `normalize` will not fail if the expression is ill-typed and will
11081109
leave ill-typed sub-expressions unevaluated.
11091110
-}
1110-
normalize :: Expr s a -> Expr t a
1111+
normalize :: Eq a => Expr s a -> Expr t a
11111112
normalize = normalizeWith (const Nothing)
11121113

11131114
{-| This function is used to determine whether folds like @Natural/fold@ or
@@ -1209,7 +1210,7 @@ denote (Embed a ) = Embed a
12091210
with those functions is not total either.
12101211
12111212
-}
1212-
normalizeWith :: Normalizer a -> Expr s a -> Expr t a
1213+
normalizeWith :: Eq a => Normalizer a -> Expr s a -> Expr t a
12131214
normalizeWith ctx e0 = loop (denote e0)
12141215
where
12151216
loop e = case e of
@@ -1356,32 +1357,40 @@ normalizeWith ctx e0 = loop (denote e0)
13561357
decide (BoolLit False) _ = BoolLit False
13571358
decide l (BoolLit True ) = l
13581359
decide _ (BoolLit False) = BoolLit False
1359-
decide l r = BoolAnd l r
1360+
decide l r
1361+
| judgmentallyEqual l r = l
1362+
| otherwise = BoolAnd l r
13601363
BoolOr x y -> decide (loop x) (loop y)
13611364
where
13621365
decide (BoolLit False) r = r
13631366
decide (BoolLit True ) _ = BoolLit True
13641367
decide l (BoolLit False) = l
13651368
decide _ (BoolLit True ) = BoolLit True
1366-
decide l r = BoolOr l r
1369+
decide l r
1370+
| judgmentallyEqual l r = l
1371+
| otherwise = BoolOr l r
13671372
BoolEQ x y -> decide (loop x) (loop y)
13681373
where
13691374
decide (BoolLit True ) r = r
13701375
decide l (BoolLit True ) = l
1371-
decide (BoolLit False) (BoolLit False) = BoolLit True
1372-
decide l r = BoolEQ l r
1376+
decide l r
1377+
| judgmentallyEqual l r = BoolLit True
1378+
| otherwise = BoolEQ l r
13731379
BoolNE x y -> decide (loop x) (loop y)
13741380
where
13751381
decide (BoolLit False) r = r
13761382
decide l (BoolLit False) = l
1377-
decide (BoolLit True ) (BoolLit True ) = BoolLit False
1378-
decide l r = BoolNE l r
1383+
decide l r
1384+
| judgmentallyEqual l r = BoolLit False
1385+
| otherwise = BoolNE l r
13791386
BoolIf bool true false -> decide (loop bool) (loop true) (loop false)
13801387
where
13811388
decide (BoolLit True ) l _ = l
13821389
decide (BoolLit False) _ r = r
13831390
decide b (BoolLit True) (BoolLit False) = b
1384-
decide b l r = BoolIf b l r
1391+
decide b l r
1392+
| judgmentallyEqual l r = l
1393+
| otherwise = BoolIf b l r
13851394
Natural -> Natural
13861395
NaturalLit n -> NaturalLit n
13871396
NaturalFold -> NaturalFold
@@ -1525,6 +1534,15 @@ normalizeWith ctx e0 = loop (denote e0)
15251534
Note _ e' -> loop e'
15261535
Embed a -> Embed a
15271536

1537+
{-| Returns `True` if two expressions are α-equivalent and β-equivalent and
1538+
`False` otherwise
1539+
-}
1540+
judgmentallyEqual :: Eq a => Expr s a -> Expr t a -> Bool
1541+
judgmentallyEqual eL0 eR0 = alphaBetaNormalize eL0 == alphaBetaNormalize eR0
1542+
where
1543+
alphaBetaNormalize :: Eq a => Expr s a -> Expr () a
1544+
alphaBetaNormalize = alphaNormalize . normalize
1545+
15281546
-- | Use this to wrap you embedded functions (see `normalizeWith`) to make them
15291547
-- polymorphic enough to be used.
15301548
type Normalizer a = forall s. Expr s a -> Maybe (Expr s a)

src/Dhall/TypeCheck.hs

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,6 @@ rule Type Type = return Type
5858
rule Kind Kind = return Kind
5959
rule Kind Type = return Type
6060

61-
judgmentallyEqual :: Eq a => Expr s a -> Expr t a -> Bool
62-
judgmentallyEqual eL0 eR0 = alphaBetaNormalize eL0 == alphaBetaNormalize eR0
63-
where
64-
alphaBetaNormalize :: Expr s a -> Expr X a
65-
alphaBetaNormalize =
66-
Dhall.Core.alphaNormalize
67-
. Dhall.Core.normalize
68-
6961
{-| Type-check an expression and return the expression's type if type-checking
7062
succeeds or an error if type-checking fails
7163
@@ -131,7 +123,7 @@ typeWithA tpa = loop
131123
Pi x _A _B -> return (x, _A, _B)
132124
_ -> Left (TypeError ctx e (NotAFunction f tf))
133125
_A' <- loop ctx a
134-
if judgmentallyEqual _A _A'
126+
if Dhall.Core.judgmentallyEqual _A _A'
135127
then do
136128
let a' = Dhall.Core.shift 1 (V x 0) a
137129
let _B' = Dhall.Core.subst (V x 0) a' _B
@@ -148,7 +140,7 @@ typeWithA tpa = loop
148140
_ <- loop ctx _A0
149141
let nf_A0 = Dhall.Core.normalize _A0
150142
let nf_A1 = Dhall.Core.normalize _A1
151-
if judgmentallyEqual _A0 _A1
143+
if Dhall.Core.judgmentallyEqual _A0 _A1
152144
then return ()
153145
else Left (TypeError ctx e (AnnotMismatch a0 nf_A0 nf_A1))
154146
Nothing -> return ()
@@ -161,7 +153,7 @@ typeWithA tpa = loop
161153
_ <- loop ctx t
162154

163155
t' <- loop ctx x
164-
if judgmentallyEqual t t'
156+
if Dhall.Core.judgmentallyEqual t t'
165157
then do
166158
return t
167159
else do
@@ -237,7 +229,7 @@ typeWithA tpa = loop
237229
Const Type -> return ()
238230
_ -> Left (TypeError ctx e (IfBranchMustBeTerm False z tz ttz))
239231

240-
if judgmentallyEqual ty tz
232+
if Dhall.Core.judgmentallyEqual ty tz
241233
then return ()
242234
else Left (TypeError ctx e (IfBranchMismatch y z ty tz))
243235
return ty
@@ -335,7 +327,7 @@ typeWithA tpa = loop
335327
_ -> Left (TypeError ctx e (InvalidListType t))
336328
flip traverseWithIndex_ xs (\i x -> do
337329
t' <- loop ctx x
338-
if judgmentallyEqual t t'
330+
if Dhall.Core.judgmentallyEqual t t'
339331
then return ()
340332
else do
341333
let nf_t = Dhall.Core.normalize t
@@ -351,7 +343,7 @@ typeWithA tpa = loop
351343
_ -> Left (TypeError ctx e (InvalidListType t))
352344
flip traverseWithIndex_ xs (\i x -> do
353345
t' <- loop ctx x
354-
if judgmentallyEqual t t'
346+
if Dhall.Core.judgmentallyEqual t t'
355347
then return ()
356348
else do
357349
let nf_t = Dhall.Core.normalize t
@@ -369,7 +361,7 @@ typeWithA tpa = loop
369361
App List er -> return er
370362
_ -> Left (TypeError ctx e (CantListAppend r tr))
371363

372-
if judgmentallyEqual el er
364+
if Dhall.Core.judgmentallyEqual el er
373365
then return (App List el)
374366
else Left (TypeError ctx e (ListAppendMismatch el er))
375367
loop _ ListBuild = do
@@ -410,7 +402,7 @@ typeWithA tpa = loop
410402
_ -> Left (TypeError ctx e (InvalidOptionalType t))
411403
forM_ xs (\x -> do
412404
t' <- loop ctx x
413-
if judgmentallyEqual t t'
405+
if Dhall.Core.judgmentallyEqual t t'
414406
then return ()
415407
else do
416408
let nf_t = Dhall.Core.normalize t
@@ -538,10 +530,10 @@ typeWithA tpa = loop
538530
Just tX ->
539531
case tX of
540532
Pi _ tY' t' -> do
541-
if judgmentallyEqual tY tY'
533+
if Dhall.Core.judgmentallyEqual tY tY'
542534
then return ()
543535
else Left (TypeError ctx e (HandlerInputTypeMismatch kY tY tY'))
544-
if judgmentallyEqual t t'
536+
if Dhall.Core.judgmentallyEqual t t'
545537
then return ()
546538
else Left (TypeError ctx e (InvalidHandlerOutputType kY t t'))
547539
_ -> Left (TypeError ctx e (HandlerNotAFunction kY tX))
@@ -577,10 +569,10 @@ typeWithA tpa = loop
577569
Just tX ->
578570
case tX of
579571
Pi _ tY' t' -> do
580-
if judgmentallyEqual tY tY'
572+
if Dhall.Core.judgmentallyEqual tY tY'
581573
then return ()
582574
else Left (TypeError ctx e (HandlerInputTypeMismatch kY tY tY'))
583-
if judgmentallyEqual t t'
575+
if Dhall.Core.judgmentallyEqual t t'
584576
then return ()
585577
else Left (TypeError ctx e (HandlerOutputTypeMismatch kX t kY t'))
586578
_ -> Left (TypeError ctx e (HandlerNotAFunction kY tX))

tests/Normalization.hs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ normalizationTests :: TestTree
2525
normalizationTests =
2626
testGroup "normalization"
2727
[ examples
28+
, simplifications
2829
, constantFolding
2930
, conversions
3031
, shouldNormalize "Optional build/fold fusion" "optionalBuildFold"
@@ -154,6 +155,16 @@ examples =
154155
, shouldNormalize "Text/concatSep" "./examples/Text/concatSep/1"
155156
]
156157

158+
simplifications :: TestTree
159+
simplifications =
160+
testGroup "Simplifications"
161+
[ shouldNormalize "if/then/else" "./simplifications/ifThenElse"
162+
, shouldNormalize "||" "./simplifications/or"
163+
, shouldNormalize "&&" "./simplifications/and"
164+
, shouldNormalize "==" "./simplifications/eq"
165+
, shouldNormalize "!=" "./simplifications/ne"
166+
]
167+
157168
constantFolding :: TestTree
158169
constantFolding =
159170
testGroup "folding of constants"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{ example0 = λ(x : Bool) x && True
2+
, example1 = λ(x : Bool) True && x
3+
, example2 = λ(x : Bool) x && False
4+
, example3 = λ(x : Bool) False && x
5+
, example4 = λ(x : Bool) x && x
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{ example0 = λ(x : Bool) x
2+
, example1 = λ(x : Bool) x
3+
, example2 = λ(x : Bool) False
4+
, example3 = λ(x : Bool) False
5+
, example4 = λ(x : Bool) x
6+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{ example0 = λ(x : Bool) x == True
2+
, example1 = λ(x : Bool) True == x
3+
, example2 = λ(x : Bool) x == x
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{ example0 = λ(x : Bool) x
2+
, example1 = λ(x : Bool) x
3+
, example2 = λ(x : Bool) True
4+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{ example0 = λ(x : Bool) if x then True else False
2+
, example1 = λ(x : Bool) λ(y : Text) if x then y else y
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{ example0 = λ(x : Bool) x
2+
, example1 = λ(x : Bool) λ(y : Text) y
3+
}

0 commit comments

Comments
 (0)