on master help sign in
Support deconstruction for view patterns
17950c9Bryan O'Sullivan
Data/Text.hs+52−1
@@ -140,6 +140,10 @@ module Data.Text
140 , isSuffixOf
140 , isSuffixOf
141 , isInfixOf
141 , isInfixOf
142
142
143+ -- ** View patterns
144+ , prefixed
145+ , suffixed
146+
143 -- * Searching
147 -- * Searching
144 , filter
148 , filter
145 , find
149 , find
@@ -165,7 +169,7 @@ module Data.Text
165import Prelude (Char, Bool(..), Functor(..), Int, Maybe(..), String,
169import Prelude (Char, Bool(..), Functor(..), Int, Maybe(..), String,
166 Eq(..), Ord(..), (++),
170 Eq(..), Ord(..), (++),
167 Read(..), Show(..),
171 Read(..), Show(..),
168− (&&), (||), (+), (-), (.), ($), (>>), (*),
172+ (&&), (||), (+), (-), (.), ($), ($!), (>>), (*),
169 div, error, not, return, otherwise)
173 div, error, not, return, otherwise)
170#if defined(HAVE_DEEPSEQ)
174#if defined(HAVE_DEEPSEQ)
171import Control.DeepSeq (NFData)
175import Control.DeepSeq (NFData)
@@ -1289,5 +1293,52 @@ isInfixOf needle haystack
1289 isInfixOf (singleton n) h = S.elem n (S.stream h)
1293 isInfixOf (singleton n) h = S.elem n (S.stream h)
1290 #-}
1294 #-}
1291
1295
1296+-------------------------------------------------------------------------------
1297+-- * View patterns
1298+
1299+-- | /O(n)/ Returns the suffix of the second string if its prefix
1300+-- matches the first.
1301+--
1302+-- Examples:
1303+--
1304+-- > prefixed "foo" "foobar" == Just "bar"
1305+-- > prefixed "foo" "quux" == Nothing
1306+--
1307+-- This is particularly useful with the @ViewPatterns@ extension to
1308+-- GHC, as follows:
1309+--
1310+-- > {-# LANGUAGE ViewPatterns #-}
1311+-- > import Data.Text as T
1312+-- >
1313+-- > fnordLength :: Text -> Int
1314+-- > fnordLength (prefixed "fnord" -> Just suf) = T.length suf
1315+-- > fnordLength _ = -1
1316+prefixed :: Text -> Text -> Maybe Text
1317+prefixed p@(Text _arr _off plen) t@(Text arr off len)
1318+ | p `isPrefixOf` t = Just $! textP arr (off+plen) (len-plen)
1319+ | otherwise = Nothing
1320+
1321+-- | /O(n)/ Returns the prefix of the second string if its suffix
1322+-- matches the first.
1323+--
1324+-- Examples:
1325+--
1326+-- > suffixed "bar" "foobar" == Just "foo"
1327+-- > suffixed "foo" "quux" == Nothing
1328+--
1329+-- This is particularly useful with the @ViewPatterns@ extension to
1330+-- GHC, as follows:
1331+--
1332+-- > {-# LANGUAGE ViewPatterns #-}
1333+-- > import Data.Text as T
1334+-- >
1335+-- > quuxLength :: Text -> Int
1336+-- > quuxLength (suffixed "quux" -> Just pre) = T.length pre
1337+-- > quuxLength _ = -1
1338+suffixed :: Text -> Text -> Maybe Text
1339+suffixed p@(Text _arr _off plen) t@(Text arr off len)
1340+ | p `isSuffixOf` t = Just $! textP arr off (len-plen)
1341+ | otherwise = Nothing
1342+
1292emptyError :: String -> a
1343emptyError :: String -> a
1293emptyError fun = P.error ("Data.Text." ++ fun ++ ": empty input")
1344emptyError fun = P.error ("Data.Text." ++ fun ++ ": empty input")
Data/Text/Lazy.hs+53
@@ -144,6 +144,10 @@ module Data.Text.Lazy
144 , isSuffixOf
144 , isSuffixOf
145 , isInfixOf
145 , isInfixOf
146
146
147+ -- ** View patterns
148+ , prefixed
149+ , suffixed
150+
147 -- * Searching
151 -- * Searching
148 , filter
152 , filter
149 , find
153 , find
@@ -1156,6 +1160,55 @@ isInfixOf needle haystack
1156 isInfixOf (singleton n) h = S.elem n (S.stream h)
1160 isInfixOf (singleton n) h = S.elem n (S.stream h)
1157 #-}
1161 #-}
1158
1162
1163+-------------------------------------------------------------------------------
1164+-- * View patterns
1165+
1166+-- | /O(n)/ Returns the suffix of the second string if its prefix
1167+-- matches the first.
1168+--
1169+-- Examples:
1170+--
1171+-- > prefixed "foo" "foobar" == Just "bar"
1172+-- > prefixed "foo" "quux" == Nothing
1173+--
1174+-- This is particularly useful with the @ViewPatterns@ extension to
1175+-- GHC, as follows:
1176+--
1177+-- > {-# LANGUAGE ViewPatterns #-}
1178+-- > import Data.Text as T
1179+-- >
1180+-- > fnordLength :: Text -> Int
1181+-- > fnordLength (prefixed "fnord" -> Just suf) = T.length suf
1182+-- > fnordLength _ = -1
1183+prefixed :: Text -> Text -> Maybe Text
1184+-- Yes, this could be much more efficient.
1185+prefixed p t
1186+ | p `isPrefixOf` t = Just (drop (length p) t)
1187+ | otherwise = Nothing
1188+
1189+-- | /O(n)/ Returns the prefix of the second string if its suffix
1190+-- matches the first.
1191+--
1192+-- Examples:
1193+--
1194+-- > suffixed "bar" "foobar" == Just "foo"
1195+-- > suffixed "foo" "quux" == Nothing
1196+--
1197+-- This is particularly useful with the @ViewPatterns@ extension to
1198+-- GHC, as follows:
1199+--
1200+-- > {-# LANGUAGE ViewPatterns #-}
1201+-- > import Data.Text as T
1202+-- >
1203+-- > quuxLength :: Text -> Int
1204+-- > quuxLength (suffixed "quux" -> Just pre) = T.length pre
1205+-- > quuxLength _ = -1
1206+suffixed :: Text -> Text -> Maybe Text
1207+-- Yes, this could be much more efficient.
1208+suffixed p t
1209+ | p `isSuffixOf` t = Just (take (length t - length p) t)
1210+ | otherwise = Nothing
1211+
1159-- | /O(n)/ 'filter', applied to a predicate and a 'Text',
1212-- | /O(n)/ 'filter', applied to a predicate and a 'Text',
1160-- returns a 'Text' containing those characters that satisfy the
1213-- returns a 'Text' containing those characters that satisfy the
1161-- predicate.
1214-- predicate.
tests/Properties.hs+21−1
@@ -496,6 +496,19 @@ tl_isSuffixOf s = L.isSuffixOf s`eqP` TL.isSuffixOf (packS s)
496t_isInfixOf s = L.isInfixOf s `eqP` T.isInfixOf (packS s)
496t_isInfixOf s = L.isInfixOf s `eqP` T.isInfixOf (packS s)
497tl_isInfixOf s = L.isInfixOf s `eqP` TL.isInfixOf (packS s)
497tl_isInfixOf s = L.isInfixOf s `eqP` TL.isInfixOf (packS s)
498
498
499+prefixed (p:ps) (t:ts)
500+ | p == t = prefixed ps ts
501+prefixed [] ts = Just ts
502+prefixed _ _ = Nothing
503+
504+t_prefixed s = (fmap packS . prefixed s) `eqP` T.prefixed (packS s)
505+tl_prefixed s = (fmap packS . prefixed s) `eqP` TL.prefixed (packS s)
506+
507+suffixed p t = reverse `fmap` prefixed (reverse p) (reverse t)
508+
509+t_suffixed s = (fmap packS . suffixed s) `eqP` T.suffixed (packS s)
510+tl_suffixed s = (fmap packS . suffixed s) `eqP` TL.suffixed (packS s)
511+
499sf_elem p c = (L.elem c . L.filter p) `eqP` (S.elem c . S.filter p)
512sf_elem p c = (L.elem c . L.filter p) `eqP` (S.elem c . S.filter p)
500sf_filter q p = (L.filter p . L.filter q) `eqP` (unpackS . S.filter p . S.filter q)
513sf_filter q p = (L.filter p . L.filter q) `eqP` (unpackS . S.filter p . S.filter q)
501t_filter p = L.filter p `eqP` (unpackS . T.filter p)
514t_filter p = L.filter p `eqP` (unpackS . T.filter p)
@@ -871,7 +884,14 @@ tests = [
871 testProperty "t_isSuffixOf" t_isSuffixOf,
884 testProperty "t_isSuffixOf" t_isSuffixOf,
872 testProperty "tl_isSuffixOf" tl_isSuffixOf,
885 testProperty "tl_isSuffixOf" tl_isSuffixOf,
873 testProperty "t_isInfixOf" t_isInfixOf,
886 testProperty "t_isInfixOf" t_isInfixOf,
874− testProperty "tl_isInfixOf" tl_isInfixOf
887+ testProperty "tl_isInfixOf" tl_isInfixOf,
888+
889+ testGroup "view" [
890+ testProperty "t_prefixed" t_prefixed,
891+ testProperty "tl_prefixed" tl_prefixed,
892+ testProperty "t_suffixed" t_suffixed,
893+ testProperty "tl_suffixed" tl_suffixed
894+ ]
875 ],
895 ],
876
896
877 testGroup "searching" [
897 testGroup "searching" [