any-pat 0.3.0.0 → 0.4.0.0
raw patch · 4 files changed
+103/−23 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGELOG.md +6/−1
- README.md +16/−5
- any-pat.cabal +1/−1
- src/Data/Pattern/Any.hs +80/−16
CHANGELOG.md view
@@ -6,7 +6,12 @@ and this project adheres to the [Haskell Package Versioning Policy](https://pvp.haskell.org/). -For a full list of changes, see the history on [*GitHub*](https://github.com/hapytex/any-pat).+## 0.1.0.0 - YYYY-MM-DD+# `any-pat` changelog++## 0.4.0.0 - 2024-01-18++Lookups with only a variable pattern for `hashpat`. ## 0.3.0.0 - 2023-12-25
README.md view
@@ -75,8 +75,8 @@ `hashpat` is a quasi quoter for patterns to make lookups on `HashMap`s more convenient. Indeed, we can for example create a function: ```-sumab :: HashMap String Int -> Int-sumab [hashpat|"a" -> a, "b" -> b|] = a + b+sumab ∷ HashMap String Int → Int+sumab [hashpat|"a" → a, "b" → b|] = a + b sumab _ = 0 ``` @@ -85,14 +85,25 @@ The keys can take arbitrary expressions, and we thus can for example use `"a" ++ "b"` as key. Furthermore we can use an arbitrary pattern at the right side of the arrow, such that it only "fires" if it matches a given pattern. For example: ```-bifanothing :: HashMap String (Maybe Int) -> Int-bifanothing [hashpat|"a" ++ "b" -> Nothing, "b" -> Just x|] = x+bifanothing ∷ HashMap String (Maybe Int) → Int+bifanothing [hashpat|"a" ++ "b" → Nothing, "b" → Just x|] = x bifanothing _ = 0 ``` this will thus fire the first clause if the `HashMap` has a key `"ab"` that maps to `Nothing`, and a key `"b"` that maps to a `Just x`, and in that case return the `x`. Essentially it thus compiles the pattern, which is a sequence of view patterns into a function that will perform `lookup`s and then pattern match on the result of these lookups. +Since `HashMap`s with strings as keys are common, and unpacking these into variables with the same name is a common usecase, one can also just list variable names. These will then be translated into lookups with strings, or if the `OverloadedStrings` extension is enabled, a `HashMap` where the key type is both a member of `Hashable` and `IsString`: +```+{-# LANGUAGE OverloadedStrings, QuasiQuotes, ViewPatterns #-}++sumab :: (Hashable s, IsString s, Num n) => HashMap s n -> n+sumab [hashpat|a, b|] = a + b+sumab _ = 0+```++this thus makes it possible to handle optional named paramters, although these all have to have the same type of corresponding values. The dictionary can thus to some extend ???+ ### `rangepat` `rangepat` defines patterns for range memberships. For example:@@ -114,7 +125,7 @@ ## Behind the curtains -The package transforms a sequence of patterns to a *view pattern*, or an *expression*, depending on where the quasi quoter is used. If we create a pattern <code>[anypat|p<sub>1</sub>, p<sub>2</sub>, …, p<sub>n</sub>]</code>, it will create a view pattern that looks like:+The package transforms a sequence of patterns to a *view pattern*, or an *expression*, depending on where the quasi quoter is used. If we create a pattern <code>[anypat|p<sub>1</sub>, p<sub>2</sub>, …, p<sub>n</sub>|]</code>, it will create a view pattern that looks like: <pre><code>\case p<sub>1</sub> → Just n⃗
any-pat.cabal view
@@ -1,5 +1,5 @@ name: any-pat-version: 0.3.0.0+version: 0.4.0.0 synopsis: Quasiquoters that act on a sequence of patterns and compiles these view into patterns and expressions. -- description: homepage: https://github.com/hapytex/any-pat#readme
src/Data/Pattern/Any.hs view
@@ -57,7 +57,7 @@ import Language.Haskell.Exts.Extension (Extension (EnableExtension), KnownExtension (ViewPatterns)) import Language.Haskell.Exts.Parser (ParseMode (extensions), ParseResult (ParseFailed, ParseOk), defaultParseMode, parseExp, parsePatWithMode) import Language.Haskell.Meta (toExp, toPat)-import Language.Haskell.TH (Body (NormalB), Exp (AppE, ArithSeqE, ConE, LamCaseE, LamE, TupE, VarE), Match (Match), Name, Pat (AsP, BangP, ConP, InfixP, ListP, LitP, ParensP, RecP, SigP, TildeP, TupP, UInfixP, UnboxedSumP, UnboxedTupP, VarP, ViewP, WildP), Q, Range (FromR, FromThenR, FromThenToR, FromToR), newName)+import Language.Haskell.TH (Body (NormalB), Exp (AppE, ArithSeqE, ConE, LamCaseE, LamE, LitE, TupE, VarE), Lit (StringL), Match (Match), Name, Pat (AsP, BangP, ConP, InfixP, ListP, LitP, ParensP, RecP, SigP, TildeP, TupP, UInfixP, UnboxedSumP, UnboxedTupP, VarP, ViewP, WildP), Q, Range (FromR, FromThenR, FromThenToR, FromToR), nameBase, newName) import Language.Haskell.TH.Quote (QuasiQuoter (QuasiQuoter)) data HowPass = Simple | AsJust | AsNothing deriving (Eq, Ord, Read, Show)@@ -301,6 +301,15 @@ -- | A quasquoter to specify multiple patterns that will succeed if any of the patterns match. All patterns should have the same set of variables and these should -- have the same type, otherwise a variable would have two different types, and if a variable is absent in one of the patterns, the question is what to pass as value.+--+-- __Examples__:+--+-- @+-- {-# LANGUAGE ViewPatterns, QuasiQuotes #-}+--+-- example :: (Bool, a, a) -> a+-- example [anypat|(False, a, _), (True, _, a)|] = a+-- @ anypat :: -- | The quasiquoter that can be used as expression and pattern. QuasiQuoter@@ -309,28 +318,37 @@ -- | A quasiquoter to specify multiple patterns that will succeed if any of these patterns match. Patterns don't have to have the same variable names but if a variable is shared over the -- different patterns, it should have the same type. In case a variable name does not appear in all patterns, it will be passed as a 'Maybe' to the clause with 'Nothing' if a pattern matched -- without that variable name, and a 'Just' if the (first) pattern that matched had such variable.+--+-- __Examples__:+--+-- @+-- {-# LANGUAGE ViewPatterns, QuasiQuotes #-}+--+-- example :: (Bool, a) -> Maybe a+-- example [maypat|(True, a), _|] = a+-- @ maypat :: -- | The quasiquoter that can be used as expression and pattern. QuasiQuoter maypat = QuasiQuoter ((liftFail >=> unionCaseExp False) . parsePatternSequence) ((liftFail >=> unionCaseFunc False) . parsePatternSequence) failQ failQ #if MIN_VERSION_template_haskell(2, 16, 0)-_makeTupleExpressions :: Name -> [Pat] -> Q ([Maybe Exp], [Pat])-_makeTupleExpressions hm = go [] [] . reverse- where- go es ps [] = pure (es, ps)- go es ps (ViewP e p : xs) = go (Just (VarE 'Data.HashMap.Strict.lookup `AppE` e `AppE` VarE hm) : es) (conP 'Just [p] : ps) xs- go _ _ _ = fail "all items in the hashpat should look like view patterns."+tupE :: [Exp] -> Exp+tupE = TupE . map Just #else-_makeTupleExpressions :: Name -> [Pat] -> Q ([Exp], [Pat])-_makeTupleExpressions hm = go [] [] . reverse+tupE :: [Exp] -> Exp+tupE = TupE+#endif++_makeTupleExpressions :: [Pat] -> Q ([Exp], [Pat])+_makeTupleExpressions = go [] [] . reverse where go es ps [] = pure (es, ps)- go es ps (ViewP e p : xs) = go (VarE 'Data.HashMap.Strict.lookup `AppE` e `AppE` VarE hm : es) (conP 'Just [p] : ps) xs- go _ _ _ = fail "all items in the hashpat should look like view patterns."-#endif+ go es ps (p@(VarP n) : xs) = go es ps (ViewP (LitE (StringL (nameBase n))) p : xs)+ go es ps (ViewP e p : xs) = go (VarE 'Data.HashMap.Strict.lookup `AppE` e : es) (conP 'Just [p] : ps) xs+ go _ _ _ = fail "all items in the hashpat should look like view patterns or simple variables." --- | Create a view pattern that maps a HashMap with a locally scoped @hm@ parameter to a the patterns. It thus basically implicitly adds `lookup`+-- | Create a view pattern that maps a HashMap with a locally scoped @hm@ parameter to a the patterns. It thus basically implicitly adds 'Data.HashMap.Strict.lookup' -- to all expressions and matches these with the given patterns. The compilation fails if not all elements are view patterns. combineHashViewPats :: -- | The non-empty list of view patterns that are compiled into a viw pattern.@@ -340,9 +358,34 @@ combineHashViewPats (ViewP e p :| []) = pure (ViewP (AppE (VarE 'Data.HashMap.Strict.lookup) e) (conP 'Just [p])) combineHashViewPats (x :| xs) = do hm <- newName "hm"- uncurry ((. TupP) . (ViewP . LamE [VarP hm] . TupE)) <$> _makeTupleExpressions hm (x : xs)+ uncurry ((. TupP) . (ViewP . LamE [VarP hm] . tupE . map (`AppE` VarE hm))) <$> _makeTupleExpressions (x : xs) --- | A quasiquoter to make `Data.HashMap.Strict.HashMap` lookups more convenient. This can only be used as a pattern.+-- | A quasiquoter to make 'Data.HashMap.Strict.HashMap' lookups more convenient. This can only be used as a pattern. It takes a sequence of+-- view patterns, where it will perform the lookup on the expression part of the view pattern, and match the /successful/ lookup with the pattern.+-- The `Just` part is thus not used in the pattern part to indicate a successful lookup. If a single variable is used, it will make a lookup with+-- a string literal with the same variable.+--+-- __Examples__:+--+-- @+-- {-# LANGUAGE ViewPatterns, QuasiQuotes #-}+--+-- sumab :: HashMap String Int -> Int+-- sumab [rangepat|"a" -> a, "b" -> b|] = a + b+-- sumab _ = 0+-- @+--+-- This will sum up the values for `"a"` and `"b"` in the 'Data.HashMap.Strict.HashMap', given these /both/ exist. Otherwise, it returns `0`.+--+-- @+-- {-# LANGUAGE ViewPatterns, QuasiQuotes #-}+--+-- sumab :: HashMap String Int -> Int+-- sumab [rangepat|a, b|] = a + b+-- sumab _ = 0+-- @+--+-- This will sum up the values for `"a"` and `"b"` in the 'Data.HashMap.Strict.HashMap', given these /both/ exist. Otherwise, it returns `0`. hashpat :: QuasiQuoter hashpat = QuasiQuoter failQ ((liftFail >=> combineHashViewPats) . parsePatternSequence) failQ failQ @@ -441,6 +484,16 @@ -- | A 'QuasiQuoter' to parse a range expression to a 'RangeObj'. In case the 'QuasiQuoter' is used for a pattern, -- it compiles into a /view pattern/ that will work if the element is a member of the 'RangeObj'.+--+-- __Examples__:+--+-- @+-- {-# LANGUAGE ViewPatterns, QuasiQuotes #-}+--+-- positiveEven :: Int -> Bool+-- positiveEven [rangepat|0, 2 ..|] = True+-- positiveEven _ = False+-- @ rangepat :: -- | The quasiquoter that can be used as expression and pattern. QuasiQuoter@@ -450,7 +503,18 @@ -- | An alias of the 'rangepat' 'QuasiQuoter', this is used since it looks quite similar to @∊ [a .. b]@, -- beware that the @ϵ@ in @[ϵ|a .. b|]@ is not an /element of/ character, but the /Greek lunate epsilon/ character--- which only looks similar.+-- which only /looks/ similar. The reason we use an epsiolon is because this can be used as an identifier, whereas+-- the element of is an operator.+--+-- __Examples__:+--+-- @+-- {-# LANGUAGE ViewPatterns, QuasiQuotes #-}+--+-- positiveEven :: Int -> Bool+-- positiveEven [ϵ|2, 4 ..|] = True+-- positiveEven _ = False+-- @ ϵ :: -- | The quasiquoter that can be used as expression and pattern. QuasiQuoter