Skip to content

Commit c224466

Browse files
committed
Add predicate combinators
1 parent a8bf83b commit c224466

File tree

7 files changed

+103
-2
lines changed

7 files changed

+103
-2
lines changed

.hlint.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,6 +1558,12 @@
15581558
name: "Use 'bool' from Relude"
15591559
note: "'bool' is already exported from Relude"
15601560
rhs: bool
1561+
- warn:
1562+
lhs: "(\\a -> f a && g a)"
1563+
rhs: "f .&& g"
1564+
- warn:
1565+
lhs: "(\\a -> f a || g a)"
1566+
rhs: "f .|| g"
15611567
- warn:
15621568
lhs: Data.Hashable.Hashable
15631569
name: "Use 'Hashable' from Relude"

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ The changelog is available [on GitHub][2].
66
## Unreleased
77

88
- Allow containers-0.8.
9+
- Add predicate combinators
910

1011
## 1.2.2.0 – Oct 13, 2024
1112

hlint/hlint.dhall

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,8 @@ in [ Rule.Arguments { arguments =
541541
, warnReexport "unless" "Control.Monad"
542542
, warnReexport "when" "Control.Monad"
543543
, warnReexport "bool" "Data.Bool"
544+
, warnSimple "(\\a -> f a && g a)" "f .&& g"
545+
, warnSimple "(\\a -> f a || g a)" "f .|| g"
544546

545547
-- Container
546548
, warnReexport "Hashable" "Data.Hashable"

relude.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ library
152152
Relude.Applicative
153153
Relude.Base
154154
Relude.Bool
155+
Relude.Bool.Extra
155156
Relude.Bool.Guard
156157
Relude.Bool.Reexport
157158
Relude.Container

src/Relude/Bool.hs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,16 @@ with monads.
1515
-}
1616

1717
module Relude.Bool
18-
( module Relude.Bool.Reexport
18+
-- $reexport
19+
( module Relude.Bool.Extra
20+
-- $reexport
21+
, module Relude.Bool.Reexport
1922
-- $reexport
2023
, module Relude.Bool.Guard
2124
-- $guard
2225
) where
2326

27+
import Relude.Bool.Extra
2428
import Relude.Bool.Guard
2529
import Relude.Bool.Reexport
2630

src/Relude/Bool/Extra.hs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{-# LANGUAGE Safe #-}
2+
3+
{- |
4+
Module : Relude.Bool.Extra
5+
Copyright : (c) 2025 drlkf
6+
(c) 2025 Kowainik
7+
SPDX-License-Identifier : MIT
8+
Maintainer : Kowainik <[email protected]>
9+
Stability : Stable
10+
Portability : Portable
11+
12+
Convenient functions to work with predicates.
13+
-}
14+
15+
module Relude.Bool.Extra
16+
( (.&&)
17+
, (.||)
18+
) where
19+
20+
import Relude.Bool.Reexport (Bool, (&&), (||))
21+
22+
{- | Predicate @and@ combinator.
23+
24+
Combine two predicate functions into one with @and@ boolean logic.
25+
26+
>>> even .&& (> 0) $ 2
27+
True
28+
29+
>>> odd .&& (> 0) $ 2
30+
False
31+
32+
>>> even .&& (< 0) $ 2
33+
False
34+
35+
-}
36+
infixr 3 .&&
37+
(.&&)
38+
:: (a -> Bool)
39+
-> (a -> Bool)
40+
-> (a -> Bool)
41+
(.&&) p1 p2 a = p1 a && p2 a
42+
{-# INLINE (.&&) #-}
43+
44+
{- | Predicate @or@ combinator.
45+
46+
Combine two predicate functions into one with @or@ boolean logic.
47+
Lazy in the second argument.
48+
49+
>>> even .|| (> 0) $ 2
50+
True
51+
52+
>>> even .|| (< 0) $ 2
53+
True
54+
55+
>>> even .|| error "impossible" $ 2
56+
False
57+
58+
-}
59+
infixr 2 .||
60+
(.||)
61+
:: (a -> Bool)
62+
-> (a -> Bool)
63+
-> (a -> Bool)
64+
(.||) p1 p2 a = p1 a || p2 a
65+
{-# INLINE (.||) #-}

test/Test/Relude/Property.hs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@ import Data.List (nub)
88
import Hedgehog (Group (..), Property, assert, forAll, property, (===))
99

1010
import Test.Relude.Container.One (oneProps)
11-
import Test.Relude.Gen (genBoolList, genIntList, genUtf8ByteString, genUtf8String, genUtf8Text)
11+
import Test.Relude.Gen (genBoolList, genInt, genIntList, genUtf8ByteString, genUtf8String,
12+
genUtf8Text)
1213

1314

1415
hedgehogTestList :: [Group]
1516
hedgehogTestList =
1617
[ utfProps
1718
, listProps
1819
, logicProps
20+
, predicateOperatorProps
1921
, oneProps
2022
]
2123

@@ -106,3 +108,23 @@ prop_orM :: Property
106108
prop_orM = property $ do
107109
bs <- forAll genBoolList
108110
orM (pure <$> bs) === pure @Maybe (or bs)
111+
112+
----------------------------------------------------------------------------
113+
-- predicate operators
114+
----------------------------------------------------------------------------
115+
116+
predicateOperatorProps :: Group
117+
predicateOperatorProps = Group "predicate logic operators property tests"
118+
[ (".&&", prop_andP)
119+
, (".||", prop_orP)
120+
]
121+
122+
prop_andP :: Property
123+
prop_andP = property $ do
124+
x <- forAll genInt
125+
(even x && x > 0) === (even .&& (> 0)) x
126+
127+
prop_orP :: Property
128+
prop_orP = property $ do
129+
x <- forAll genInt
130+
(even x || x > 0) === (even .|| (> 0)) x

0 commit comments

Comments
 (0)