From 649e3b2290548657569bd4083310b1e8f7ac8b72 Mon Sep 17 00:00:00 2001 From: Nathan van Doorn Date: Fri, 10 Sep 2021 11:02:09 +0200 Subject: [PATCH 1/5] Add prisms for working with prefixed and suffixed text --- CHANGELOG.markdown | 2 ++ src/Data/Text/Lazy/Lens.hs | 30 ++++++++++++++++++++++++++++++ src/Data/Text/Strict/Lens.hs | 30 ++++++++++++++++++++++++++++++ tests/properties.hs | 4 ++-- 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index c6466296d..0d8759dab 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -5,6 +5,8 @@ * Allow building with GHC 9.2. * Define `_CharTyLit` in `Language.Haskell.TH.Lens` when building with `template-haskell-2.18` or later. +* Add prisms for working with text with prefixes or suffices in + `Data.Text.Strict.Lens` and `Data.Text.Lazy.Lens` 5.0.1 [2021.02.24] ------------------ diff --git a/src/Data/Text/Lazy/Lens.hs b/src/Data/Text/Lazy/Lens.hs index aa212dfd7..3cdafd75b 100644 --- a/src/Data/Text/Lazy/Lens.hs +++ b/src/Data/Text/Lazy/Lens.hs @@ -1,6 +1,7 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE RankNTypes #-} #if __GLASGOW_HASKELL__ >= 710 {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE ViewPatterns #-} @@ -17,6 +18,7 @@ ---------------------------------------------------------------------------- module Data.Text.Lazy.Lens ( packed, unpacked + , prefixed, suffixed , _Text , text , builder @@ -82,6 +84,34 @@ unpacked :: Iso' Text String unpacked = iso Text.unpack Text.pack {-# INLINE unpacked #-} +-- | A 'Prism' stripping a prefix from a text when used as a 'Traversal', or +-- prepending that prefix when run backwards: +-- +-- >>> "preview" ^? prefixed "pre" +-- Just "view" +-- +-- >>> "review" ^? prefixed "pre" +-- Nothing +-- +-- >>> prefixed "pre" # "amble" +-- "preamble" +prefixed :: Text -> Prism' Text Text +prefixed p = prism' (p <>) (Text.stripPrefix p) + +-- | A 'Prism' stripping a suffix from a text when used as a 'Traversal', or +-- appending that suffix when run backwards: +-- +-- >>> "review" ^? suffixed "view" +-- Just "re" +-- +-- >>> "review" ^? suffixed "tire" +-- Nothing +-- +-- >>> suffixed ".o" # "hello" +-- "hello.o" +suffixed :: Text -> Prism' Text Text +suffixed q = prism' (<> q) (Text.stripSuffix q) + -- | This is an alias for 'unpacked' that makes it clearer how to use it with @('#')@. -- -- @ diff --git a/src/Data/Text/Strict/Lens.hs b/src/Data/Text/Strict/Lens.hs index 982a05084..0874ffaf4 100644 --- a/src/Data/Text/Strict/Lens.hs +++ b/src/Data/Text/Strict/Lens.hs @@ -1,5 +1,6 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE RankNTypes #-} #if __GLASGOW_HASKELL__ >= 710 {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE ViewPatterns #-} @@ -16,6 +17,7 @@ ---------------------------------------------------------------------------- module Data.Text.Strict.Lens ( packed, unpacked + , prefixed, suffixed , builder , text , utf8 @@ -84,6 +86,34 @@ unpacked :: Iso' Text String unpacked = iso Strict.unpack Strict.pack {-# INLINE unpacked #-} +-- | A 'Prism' stripping a prefix from a text when used as a 'Traversal', or +-- prepending that prefix when run backwards: +-- +-- >>> "preview" ^? prefixed "pre" +-- Just "view" +-- +-- >>> "review" ^? prefixed "pre" +-- Nothing +-- +-- >>> prefixed "pre" # "amble" +-- "preamble" +prefixed :: Text -> Prism' Text Text +prefixed p = prism' (p <>) (Strict.stripPrefix p) + +-- | A 'Prism' stripping a suffix from a text when used as a 'Traversal', or +-- appending that suffix when run backwards: +-- +-- >>> "review" ^? suffixed "view" +-- Just "re" +-- +-- >>> "review" ^? suffixed "tire" +-- Nothing +-- +-- >>> suffixed ".o" # "hello" +-- "hello.o" +suffixed :: Text -> Prism' Text Text +suffixed q = prism' (<> q) (Strict.stripSuffix q) + -- | This is an alias for 'unpacked' that makes it more obvious how to use it with '#' -- -- >> _Text # "hello" -- :: Text diff --git a/tests/properties.hs b/tests/properties.hs index f9f30f842..acf6b0e1c 100644 --- a/tests/properties.hs +++ b/tests/properties.hs @@ -32,7 +32,7 @@ import Test.QuickCheck import Test.Framework import Test.Framework.Providers.QuickCheck2 import Data.Char (isAlphaNum, isAscii, toUpper) -import Data.Text.Strict.Lens +import qualified Data.Text.Strict.Lens as Text import Data.List.Lens import GHC.Exts (Constraint) import Numeric (showHex, showOct, showSigned) @@ -86,7 +86,7 @@ prop__Just = isPrism (_Just :: Prism' (Maybe Int) Int) prop_prefixed s = isPrism (prefixed s :: Prism' String String) -- Data.Text.Lens -prop_text s = s^.packed.from packed == s +prop_text s = s^.Text.packed.from Text.packed == s --prop_text = isIso packed -- Numeric.Lens From 2d09fe56402b4785bc0d68b838d55d3853e58f9d Mon Sep 17 00:00:00 2001 From: Nathan van Doorn Date: Mon, 13 Sep 2021 11:12:55 +0100 Subject: [PATCH 2/5] Move all prefixed and suffixed to a typeclass in Prism --- src/Control/Lens/Prism.hs | 76 ++++++++++++++++++++++++++++++++++++ src/Data/List/Lens.hs | 56 +++++--------------------- src/Data/Text/Lazy/Lens.hs | 29 -------------- src/Data/Text/Strict/Lens.hs | 29 -------------- tests/properties.hs | 1 - 5 files changed, 86 insertions(+), 105 deletions(-) diff --git a/src/Control/Lens/Prism.hs b/src/Control/Lens/Prism.hs index 526ae1f1b..04f1ef59c 100644 --- a/src/Control/Lens/Prism.hs +++ b/src/Control/Lens/Prism.hs @@ -43,6 +43,8 @@ module Control.Lens.Prism , _Show , only , nearly + , Prefixed(..) + , Suffixed(..) -- * Prismatic profunctors , Choice(..) ) where @@ -53,10 +55,16 @@ import Control.Lens.Lens import Control.Lens.Review import Control.Lens.Type import Control.Monad +import qualified Data.ByteString as BS +import qualified Data.ByteString.Lazy as BL import Data.Functor.Identity +import qualified Data.List as List +import qualified Data.List.Lens as List import Data.Profunctor import Data.Profunctor.Rep import Data.Profunctor.Sieve +import qualified Data.Text as TS +import qualified Data.Text.Lazy as TL import Data.Traversable import Data.Void import Data.Coerce @@ -361,3 +369,71 @@ _Show = prism show $ \s -> case reads s of [(a,"")] -> Right a _ -> Left s {-# INLINE _Show #-} + +class Prefixed t where + -- | A 'Prism' stripping a prefix from a sequence when used as a 'Traversal', + -- or prepending that prefix when run backwards: + -- + -- >>> "preview" ^? prefixed "pre" + -- Just "view" + -- + -- >>> "review" ^? prefixed "pre" + -- Nothing + -- + -- >>> prefixed "pre" # "amble" + -- "preamble" + prefixed :: t -> Prism' t t + +instance Eq a => Prefixed [a] where + prefixed ps = prism' (ps ++) (List.stripPrefix ps) + {-# INLINE prefixed #-} + +instance Prefixed TS.Text where + prefixed p = prism' (p <>) (TS.stripPrefix p) + {-# INLINE prefixed #-} + +instance Prefixed TL.Text where + prefixed p = prism' (p <>) (TL.stripPrefix p) + {-# INLINE prefixed #-} + +instance Prefixed BS.ByteString where + prefixed p = prism' (p <>) (BS.stripPrefix p) + {-# INLINE prefixed #-} + +instance Prefixed BL.ByteString where + prefixed p = prism' (p <>) (BL.stripPrefix p) + {-# INLINE prefixed #-} + +class Suffixed t where + -- | A 'Prism' stripping a suffix from a sequence when used as a 'Traversal', + -- or appending that suffix when run backwards: + -- + -- >>> "review" ^? suffixed "view" + -- Just "re" + -- + -- >>> "review" ^? suffixed "tire" + -- Nothing + -- + -- >>> suffixed ".o" # "hello" + -- "hello.o" + suffixed :: t -> Prism' t t + +instance Eq a => Suffixed [a] where + suffixed qs = prism' (++ qs) (List.stripSuffix qs) + {-# INLINE suffixed #-} + +instance Suffixed TS.Text where + suffixed qs = prism' (<> qs) (TS.stripSuffix qs) + {-# INLINE suffixed #-} + +instance Suffixed TL.Text where + suffixed qs = prism' (<> qs) (TL.stripSuffix qs) + {-# INLINE suffixed #-} + +instance Suffixed BS.ByteString where + suffixed qs = prism' (<> qs) (BS.stripSuffix qs) + {-# INLINE suffixed #-} + +instance Suffixed BL.ByteString where + suffixed qs = prism' (<> qs) (BL.stripSuffix qs) + {-# INLINE suffixed #-} diff --git a/src/Data/List/Lens.hs b/src/Data/List/Lens.hs index 0b8486279..c077b2c47 100644 --- a/src/Data/List/Lens.hs +++ b/src/Data/List/Lens.hs @@ -1,6 +1,3 @@ -{-# LANGUAGE Rank2Types #-} -{-# LANGUAGE FlexibleContexts #-} - ----------------------------------------------------------------------------- -- | -- Module : Data.List.Lens @@ -68,6 +65,15 @@ -- >>> "live" & reversed %~ ('d':) -- "lived" -- +-- It's possible to work under a prefix or suffix of a list using +-- 'Control.Lens.Prism.Prefixed' and 'Control.Lens.Prism.Suffixed'. +-- +-- >>> "preview" ^? prefixed "pre" +-- Just "view" +-- +-- >>> suffixed ".o" # "hello" +-- "hello.o" +-- -- Finally, it's possible to traverse, fold over, and map over -- index-value pairs thanks to instances of -- 'Control.Lens.Indexed.TraversableWithIndex', @@ -89,55 +95,13 @@ -- ---------------------------------------------------------------------------- module Data.List.Lens - ( prefixed - , suffixed - , stripSuffix + ( stripSuffix ) where import Prelude () import Control.Monad (guard) import Control.Lens.Internal.Prelude -import Control.Lens -import qualified Data.List as List - --- $setup --- >>> :set -XNoOverloadedStrings --- >>> import Control.Lens --- >>> import Debug.SimpleReflect.Expr --- >>> import Debug.SimpleReflect.Vars as Vars hiding (f,g) --- >>> let f :: Expr -> Expr; f = Debug.SimpleReflect.Vars.f --- >>> let g :: Expr -> Expr; g = Debug.SimpleReflect.Vars.g - --- | A 'Prism' stripping a prefix from a list when used as a 'Traversal', or --- prepending that prefix when run backwards: --- --- >>> "preview" ^? prefixed "pre" --- Just "view" --- --- >>> "review" ^? prefixed "pre" --- Nothing --- --- >>> prefixed "pre" # "amble" --- "preamble" -prefixed :: Eq a => [a] -> Prism' [a] [a] -prefixed ps = prism' (ps ++) (List.stripPrefix ps) -{-# INLINE prefixed #-} - --- | A 'Prism' stripping a suffix from a list when used as a 'Traversal', or --- appending that suffix when run backwards: --- --- >>> "review" ^? suffixed "view" --- Just "re" --- --- >>> "review" ^? suffixed "tire" --- Nothing --- --- >>> suffixed ".o" # "hello" --- "hello.o" -suffixed :: Eq a => [a] -> Prism' [a] [a] -suffixed qs = prism' (++ qs) (stripSuffix qs) -{-# INLINE suffixed #-} ------------------------------------------------------------------------------ -- Util diff --git a/src/Data/Text/Lazy/Lens.hs b/src/Data/Text/Lazy/Lens.hs index 3cdafd75b..8c5eec444 100644 --- a/src/Data/Text/Lazy/Lens.hs +++ b/src/Data/Text/Lazy/Lens.hs @@ -18,7 +18,6 @@ ---------------------------------------------------------------------------- module Data.Text.Lazy.Lens ( packed, unpacked - , prefixed, suffixed , _Text , text , builder @@ -84,34 +83,6 @@ unpacked :: Iso' Text String unpacked = iso Text.unpack Text.pack {-# INLINE unpacked #-} --- | A 'Prism' stripping a prefix from a text when used as a 'Traversal', or --- prepending that prefix when run backwards: --- --- >>> "preview" ^? prefixed "pre" --- Just "view" --- --- >>> "review" ^? prefixed "pre" --- Nothing --- --- >>> prefixed "pre" # "amble" --- "preamble" -prefixed :: Text -> Prism' Text Text -prefixed p = prism' (p <>) (Text.stripPrefix p) - --- | A 'Prism' stripping a suffix from a text when used as a 'Traversal', or --- appending that suffix when run backwards: --- --- >>> "review" ^? suffixed "view" --- Just "re" --- --- >>> "review" ^? suffixed "tire" --- Nothing --- --- >>> suffixed ".o" # "hello" --- "hello.o" -suffixed :: Text -> Prism' Text Text -suffixed q = prism' (<> q) (Text.stripSuffix q) - -- | This is an alias for 'unpacked' that makes it clearer how to use it with @('#')@. -- -- @ diff --git a/src/Data/Text/Strict/Lens.hs b/src/Data/Text/Strict/Lens.hs index 0874ffaf4..2b6b8935b 100644 --- a/src/Data/Text/Strict/Lens.hs +++ b/src/Data/Text/Strict/Lens.hs @@ -17,7 +17,6 @@ ---------------------------------------------------------------------------- module Data.Text.Strict.Lens ( packed, unpacked - , prefixed, suffixed , builder , text , utf8 @@ -86,34 +85,6 @@ unpacked :: Iso' Text String unpacked = iso Strict.unpack Strict.pack {-# INLINE unpacked #-} --- | A 'Prism' stripping a prefix from a text when used as a 'Traversal', or --- prepending that prefix when run backwards: --- --- >>> "preview" ^? prefixed "pre" --- Just "view" --- --- >>> "review" ^? prefixed "pre" --- Nothing --- --- >>> prefixed "pre" # "amble" --- "preamble" -prefixed :: Text -> Prism' Text Text -prefixed p = prism' (p <>) (Strict.stripPrefix p) - --- | A 'Prism' stripping a suffix from a text when used as a 'Traversal', or --- appending that suffix when run backwards: --- --- >>> "review" ^? suffixed "view" --- Just "re" --- --- >>> "review" ^? suffixed "tire" --- Nothing --- --- >>> suffixed ".o" # "hello" --- "hello.o" -suffixed :: Text -> Prism' Text Text -suffixed q = prism' (<> q) (Strict.stripSuffix q) - -- | This is an alias for 'unpacked' that makes it more obvious how to use it with '#' -- -- >> _Text # "hello" -- :: Text diff --git a/tests/properties.hs b/tests/properties.hs index acf6b0e1c..5a2e73a74 100644 --- a/tests/properties.hs +++ b/tests/properties.hs @@ -33,7 +33,6 @@ import Test.Framework import Test.Framework.Providers.QuickCheck2 import Data.Char (isAlphaNum, isAscii, toUpper) import qualified Data.Text.Strict.Lens as Text -import Data.List.Lens import GHC.Exts (Constraint) import Numeric (showHex, showOct, showSigned) import Numeric.Lens From 1c8cbbaf992e1201826ce1f867c90ed7df30c8fa Mon Sep 17 00:00:00 2001 From: Nathan van Doorn Date: Fri, 17 Sep 2021 16:02:59 +0100 Subject: [PATCH 3/5] Readd doctest setup partially to attempt to fix doctest --- src/Data/List/Lens.hs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Data/List/Lens.hs b/src/Data/List/Lens.hs index c077b2c47..60d0b768e 100644 --- a/src/Data/List/Lens.hs +++ b/src/Data/List/Lens.hs @@ -103,6 +103,10 @@ import Prelude () import Control.Monad (guard) import Control.Lens.Internal.Prelude +-- $setup +-- >>> :set -XNoOverloadedStrings +-- >>> import Control.Lens + ------------------------------------------------------------------------------ -- Util ------------------------------------------------------------------------------ From 4a083150f6988ff8520f2f8411f9de4e7429656c Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Mon, 1 Nov 2021 19:28:04 -0400 Subject: [PATCH 4/5] Fix the build on old GHCs --- src/Control/Lens/Prism.hs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Control/Lens/Prism.hs b/src/Control/Lens/Prism.hs index 04f1ef59c..6d4ff2e2d 100644 --- a/src/Control/Lens/Prism.hs +++ b/src/Control/Lens/Prism.hs @@ -49,26 +49,23 @@ module Control.Lens.Prism , Choice(..) ) where +import Prelude () + import Control.Applicative import Control.Lens.Internal.Prism +import Control.Lens.Internal.Prelude import Control.Lens.Lens import Control.Lens.Review import Control.Lens.Type import Control.Monad import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BL -import Data.Functor.Identity +import Data.Coerce import qualified Data.List as List import qualified Data.List.Lens as List -import Data.Profunctor import Data.Profunctor.Rep -import Data.Profunctor.Sieve import qualified Data.Text as TS import qualified Data.Text.Lazy as TL -import Data.Traversable -import Data.Void -import Data.Coerce -import Prelude -- $setup -- >>> :set -XNoOverloadedStrings From 26c70f9aa4614bdcf7d3f6e90ec6b817d489d12b Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Sat, 6 Nov 2021 07:17:54 -0400 Subject: [PATCH 5/5] Re-export Prefixed/Suffixed from Data.List.Lens, CHANGELOG --- CHANGELOG.markdown | 12 ++++++++-- src/Control/Lens/Internal/List.hs | 33 +++++++++++++++++++++++++++ src/Control/Lens/Prism.hs | 2 +- src/Data/List/Lens.hs | 37 +++++++++++-------------------- 4 files changed, 57 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 262c4ef16..f502d5a39 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -7,8 +7,16 @@ when building with `template-haskell-2.18` or later. * Define `_CharTyLit` in `Language.Haskell.TH.Lens` when building with `template-haskell-2.18` or later. -* Add prisms for working with text with prefixes or suffices in - `Data.Text.Strict.Lens` and `Data.Text.Lazy.Lens` +* Add `Prefixed` and `Suffixed` classes to `Control.Lens.Prism`, which provide + `prefixed` and `suffixed` prisms for prefixes and suffixes of sequence types. + These classes generalize the `prefixed` and `suffixed` functions in + `Data.List.Lens`, which were previously top-level functions. In addition to + providing `Prefixed` and `Suffixed` instances for lists, instances for `Text` + and `ByteString` types are also provided. + + At present, `Prefixed` and `Suffixed` are re-exported from `Data.List.Lens` + for backwards compatibility. This may change in a future version of `lens`, + however. 5.0.1 [2021.02.24] ------------------ diff --git a/src/Control/Lens/Internal/List.hs b/src/Control/Lens/Internal/List.hs index fb90be77f..ccc345c65 100644 --- a/src/Control/Lens/Internal/List.hs +++ b/src/Control/Lens/Internal/List.hs @@ -13,11 +13,17 @@ ------------------------------------------------------------------------------- module Control.Lens.Internal.List ( ordinalNub + , stripSuffix ) where +import Control.Monad (guard) import Data.IntSet (IntSet) import qualified Data.IntSet as IntSet +--- $setup +--- >>> :set -XNoOverloadedStrings +--- >>> import Control.Lens.Internal.List + -- | Return the the subset of given ordinals within a given bound -- and in order of the first occurrence seen. -- @@ -38,3 +44,30 @@ ordinalNubHelper l x next seen where outOfBounds = x < 0 || l <= x notUnique = x `IntSet.member` seen + +-- | \(\mathcal{O}(\min(m,n))\). The 'stripSuffix' function drops the given +-- suffix from a list. It returns 'Nothing' if the list did not end with the +-- suffix given, or 'Just' the list after the suffix, if it does. +-- +-- >>> stripSuffix "bar" "foobar" +-- Just "foo" +-- +-- >>> stripSuffix "foo" "foo" +-- Just "" +-- +-- >>> stripSuffix "bar" "barfoo" +-- Nothing +-- +-- >>> stripSuffix "foo" "barfoobaz" +-- Nothing +stripSuffix :: Eq a => [a] -> [a] -> Maybe [a] +stripSuffix qs xs0 = go xs0 zs + where + zs = drp qs xs0 + drp (_:ps) (_:xs) = drp ps xs + drp [] xs = xs + drp _ [] = [] + go (_:xs) (_:ys) = go xs ys + go xs [] = zipWith const xs0 zs <$ guard (xs == qs) + go [] _ = Nothing -- impossible +{-# INLINE stripSuffix #-} diff --git a/src/Control/Lens/Prism.hs b/src/Control/Lens/Prism.hs index 35aacfcae..dc29a805c 100644 --- a/src/Control/Lens/Prism.hs +++ b/src/Control/Lens/Prism.hs @@ -52,6 +52,7 @@ module Control.Lens.Prism import Prelude () import Control.Applicative +import qualified Control.Lens.Internal.List as List import Control.Lens.Internal.Prism import Control.Lens.Internal.Prelude import Control.Lens.Lens @@ -61,7 +62,6 @@ import Control.Monad import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BL import qualified Data.List as List -import qualified Data.List.Lens as List import Data.Profunctor.Rep import qualified Data.Text as TS import qualified Data.Text.Lazy as TL diff --git a/src/Data/List/Lens.hs b/src/Data/List/Lens.hs index 60d0b768e..306178247 100644 --- a/src/Data/List/Lens.hs +++ b/src/Data/List/Lens.hs @@ -74,6 +74,11 @@ -- >>> suffixed ".o" # "hello" -- "hello.o" -- +-- At present, "Data.List.Lens" re-exports 'Prefixed' and 'Suffixed' for +-- backwards compatibility, as 'prefixed' and 'suffixed' used to be top-level +-- functions defined in this module. This may change in a future major release +-- of @lens@. +-- -- Finally, it's possible to traverse, fold over, and map over -- index-value pairs thanks to instances of -- 'Control.Lens.Indexed.TraversableWithIndex', @@ -95,30 +100,14 @@ -- ---------------------------------------------------------------------------- module Data.List.Lens - ( stripSuffix + ( Prefixed(..) + , Suffixed(..) + , stripSuffix ) where -import Prelude () - -import Control.Monad (guard) -import Control.Lens.Internal.Prelude - --- $setup --- >>> :set -XNoOverloadedStrings --- >>> import Control.Lens - ------------------------------------------------------------------------------- --- Util ------------------------------------------------------------------------------- +import Control.Lens.Prism (Prefixed(..), Suffixed(..)) +import Control.Lens.Internal.List (stripSuffix) -stripSuffix :: Eq a => [a] -> [a] -> Maybe [a] -stripSuffix qs xs0 = go xs0 zs - where - zs = drp qs xs0 - drp (_:ps) (_:xs) = drp ps xs - drp [] xs = xs - drp _ [] = [] - go (_:xs) (_:ys) = go xs ys - go xs [] = zipWith const xs0 zs <$ guard (xs == qs) - go [] _ = Nothing -- impossible -{-# INLINE stripSuffix #-} +--- $setup +--- >>> :set -XNoOverloadedStrings +--- >>> import Control.Lens