From 309ed1bfce224eadda9e63494e973fdc076dcaad Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Fri, 6 Feb 2026 16:00:30 -0800 Subject: [PATCH 01/32] doc fixes --- src/Control/Lens/Grammar.hs | 3 ++- src/Control/Lens/Grammar/Kleene.hs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Control/Lens/Grammar.hs b/src/Control/Lens/Grammar.hs index 27ce10b..64ba174 100644 --- a/src/Control/Lens/Grammar.hs +++ b/src/Control/Lens/Grammar.hs @@ -78,7 +78,8 @@ Unfortunately, we can't use TemplateHaskell to generate it in [GHCi] which is used to test this documenation. Normally we would write `makeNestedPrisms` @''SemVer@, but here is equivalent explicit Haskell code instead. -Since @SemVer@ is a newtype, @_SemVer@ can be an `Control.Lens.Iso.Iso`. +Since @SemVer@ has only one constructor, +@_SemVer@ can be an `Control.Lens.Iso.Iso`. >>> :set -XRecordWildCards >>> import Control.Lens (Iso', iso) diff --git a/src/Control/Lens/Grammar/Kleene.hs b/src/Control/Lens/Grammar/Kleene.hs index f13a1f5..2884553 100644 --- a/src/Control/Lens/Grammar/Kleene.hs +++ b/src/Control/Lens/Grammar/Kleene.hs @@ -63,7 +63,7 @@ class Monoid k => KleeneStarAlgebra k where orK :: (Foldable f, KleeneStarAlgebra k) => f k -> k orK = foldl' (>|<) zeroK --- | universal +-- | existential anyK :: (Foldable f, KleeneStarAlgebra k) => (a -> k) -> f a -> k anyK f = foldl' (\b a -> b >|< f a) zeroK From 55b8d817d8a79a779a33ad86f8d40262e66a48aa Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Sat, 7 Feb 2026 18:05:19 -0800 Subject: [PATCH 02/32] Update Grammar.hs --- src/Control/Lens/Grammar.hs | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/Control/Lens/Grammar.hs b/src/Control/Lens/Grammar.hs index 64ba174..b3a94de 100644 --- a/src/Control/Lens/Grammar.hs +++ b/src/Control/Lens/Grammar.hs @@ -239,6 +239,9 @@ Just "1+2*3" >>> do pr <- printG arithGrammar (Num 69); return (pr "") :: Maybe String Just "69" +If all `rule`s are non-recursive, then a `Grammar` +can be rewritten as a `RegGrammar`. + -} type Grammar token a = forall p. ( Lexical token p @@ -246,19 +249,7 @@ type Grammar token a = forall p. , Alternator p ) => p a a -{- | -In addition to context-sensitivity via `Monadic` combinators, -`CtxGrammar`s adds general filtration via `Filtrator` to `Grammar`s. - ->>> :{ -palindromeG :: CtxGrammar Char String -palindromeG = rule "palindrome" $ - satisfied (\wrd -> reverse wrd == wrd) >?< manyP (anyToken @Char) -:} - -The `satisfied` pattern is used together with the `Choice` & -`Data.Profunctor.Cochoice` applicator `>?<` for general filtration. -For context-sensitivity, +{- | For context-sensitivity, the `Monadic` interface is used by importing "Data.Profunctor.Monadic" qualified and using a "bonding" notation which mixes "idiom" style with qualified do-notation. @@ -291,15 +282,15 @@ The qualified do-notation changes the signature of @P.@`Data.Profunctor.Monadic.>>=`, so that we must apply the constructor pattern @_LenVec@ to the do-block with the `>?` applicator. -Any bound named variable, @var <- action@, +Any scoped bound action, @var <- action@, gets "bonded" to the constructor pattern. Any unbound actions, except for the last action in the do-block, does not get bonded to the pattern. The last action does get bonded to the pattern. -Any unnamed bound action, @_ <- action@, +Any unscoped bound action, @_ <- action@, also gets bonded to the pattern, -but being unnamed means it isn't added to the context. -If all bound actions are unnamed, then a `CtxGrammar` can +but being unscoped means it isn't added to the context. +If all bound actions are unscoped, then a `CtxGrammar` can be rewritten as a `Grammar` since it is context-free. We can't generate a `RegBnf` since the `rule`s of a `CtxGrammar` aren't static, but dynamic and contextual. @@ -313,6 +304,18 @@ We can generate parsers and printers as expected. ["2;6,7"] >>> [pr "" | pr <- printG lenvecGrammar (LenVec 200 [100])] :: [String] [] + +In addition to context-sensitivity via `Monadic` combinators, +`CtxGrammar`s adds general filtration via `Filtrator` to `Grammar`s. +The `satisfy` function can be used as a general predicate character class. +And the `satisfied` pattern is used together with the `Choice` & +`Data.Profunctor.Cochoice` applicator `>?<` for general filtration. + +>>> :{ +palindromeG :: CtxGrammar Char String +palindromeG = rule "palindrome" $ + satisfied (\wrd -> reverse wrd == wrd) >?< manyP (anyToken @Char) +:} >>> [pal | word <- ["racecar", "word"], (pal, "") <- parseG palindromeG word] ["racecar"] -} From cdc8f7d40f2fb27cca21f9b736c310d16f1ed2b3 Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Sat, 7 Feb 2026 18:28:08 -0800 Subject: [PATCH 03/32] Update Distributor.hs --- src/Data/Profunctor/Distributor.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Data/Profunctor/Distributor.hs b/src/Data/Profunctor/Distributor.hs index 1d2fd37..231d5ad 100644 --- a/src/Data/Profunctor/Distributor.hs +++ b/src/Data/Profunctor/Distributor.hs @@ -15,8 +15,6 @@ module Data.Profunctor.Distributor , Alternator (..) , choice , option - -- * Homogeneous - , Homogeneous (..) -- * SepBy , SepBy (..) , sepBy @@ -26,6 +24,8 @@ module Data.Profunctor.Distributor , chain , chain1 , intercalateP + -- * Homogeneous + , Homogeneous (..) ) where import Control.Applicative hiding (WrappedArrow) From 6cbf0947bc2862e8bc3817632139a2be0a9a3fb1 Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Sat, 7 Feb 2026 19:01:58 -0800 Subject: [PATCH 04/32] Update Grammar.hs --- src/Control/Lens/Grammar.hs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Control/Lens/Grammar.hs b/src/Control/Lens/Grammar.hs index b3a94de..0e1358e 100644 --- a/src/Control/Lens/Grammar.hs +++ b/src/Control/Lens/Grammar.hs @@ -316,6 +316,7 @@ palindromeG :: CtxGrammar Char String palindromeG = rule "palindrome" $ satisfied (\wrd -> reverse wrd == wrd) >?< manyP (anyToken @Char) :} + >>> [pal | word <- ["racecar", "word"], (pal, "") <- parseG palindromeG word] ["racecar"] -} @@ -446,6 +447,8 @@ newtype RegString = RegString {runRegString :: RegEx Char} {- | `RegBnf`s are an embedded domain specific language of Backus-Naur forms extended by regular expression strings. +A `RegBnf` consists of a distinguished `RegString` "start" rule, +and a set of named `RegString` `rule`s. Like `RegString`s they have a string-like interface. >>> let bnf = fromString "{start} = foo|bar" :: RegBnf @@ -453,6 +456,11 @@ Like `RegString`s they have a string-like interface. {start} = foo|bar >>> bnf "{start} = foo|bar" +>>> rule "baz" bnf +"{start} = \\q{baz}\n{baz} = foo|bar" +>>> putStringLn (ruleRec "infloop" (\x -> x) :: RegBnf) +{start} = \q{infloop} +{infloop} = \q{infloop} `RegBnf`s can be generated from context-free `Grammar`s with `regbnfG`. From 394c2c8783c5342d28ef5b38365b5a1e71ab9cde Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Sat, 7 Feb 2026 19:46:30 -0800 Subject: [PATCH 05/32] Update Grammar.hs --- src/Control/Lens/Grammar.hs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Control/Lens/Grammar.hs b/src/Control/Lens/Grammar.hs index 0e1358e..4f01907 100644 --- a/src/Control/Lens/Grammar.hs +++ b/src/Control/Lens/Grammar.hs @@ -456,11 +456,8 @@ Like `RegString`s they have a string-like interface. {start} = foo|bar >>> bnf "{start} = foo|bar" ->>> rule "baz" bnf -"{start} = \\q{baz}\n{baz} = foo|bar" ->>> putStringLn (ruleRec "infloop" (\x -> x) :: RegBnf) -{start} = \q{infloop} -{infloop} = \q{infloop} +>>> :type toList bnf +toList bnf :: [Char] `RegBnf`s can be generated from context-free `Grammar`s with `regbnfG`. @@ -470,6 +467,13 @@ regbnfG regbnfGrammar :: RegBnf Like `RegString`s, `RegBnf`s can be constructed using `Lexical`, `Monoid` and `KleeneStarAlgebra` combinators. But they also support `BackusNaurForm` `rule`s and `ruleRec`s. + +>>> putStringLn (rule "baz" (bnf >|< terminal "baz")) +{start} = \q{baz} +{baz} = foo|bar|baz +>>> putStringLn (ruleRec "∞" (\x -> x) :: RegBnf) +{start} = \q{∞} +{∞} = \q{∞} -} newtype RegBnf = RegBnf {runRegBnf :: Bnf RegString} deriving newtype From 8b74db88d63a21ca0f8e7c38affdb17c8381d8e3 Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Sat, 7 Feb 2026 20:13:05 -0800 Subject: [PATCH 06/32] Update Grammar.hs --- src/Control/Lens/Grammar.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Control/Lens/Grammar.hs b/src/Control/Lens/Grammar.hs index 4f01907..242ff48 100644 --- a/src/Control/Lens/Grammar.hs +++ b/src/Control/Lens/Grammar.hs @@ -307,7 +307,7 @@ We can generate parsers and printers as expected. In addition to context-sensitivity via `Monadic` combinators, `CtxGrammar`s adds general filtration via `Filtrator` to `Grammar`s. -The `satisfy` function can be used as a general predicate character class. +The `satisfy` function is a general character class. And the `satisfied` pattern is used together with the `Choice` & `Data.Profunctor.Cochoice` applicator `>?<` for general filtration. From c5fe85b0523b7be1a16d850d50cc10f5fe653ef6 Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Sat, 7 Feb 2026 20:48:11 -0800 Subject: [PATCH 07/32] Update Boole.hs --- src/Control/Lens/Grammar/Boole.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Control/Lens/Grammar/Boole.hs b/src/Control/Lens/Grammar/Boole.hs index f5a5cef..3a5b2ec 100644 --- a/src/Control/Lens/Grammar/Boole.hs +++ b/src/Control/Lens/Grammar/Boole.hs @@ -74,8 +74,8 @@ allB f = foldl' (\b a -> b >&&< f a) (fromBool True) anyB :: (Foldable f, BooleanAlgebra b) => (a -> b) -> f a -> b anyB f = foldl' (\b a -> b >||< f a) (fromBool False) --- | `TokenTest` forms a closed `Tokenized` `BooleanAlgebra` --- of `Categorized` `tokenClass`es. +-- | `TokenTest` forms a closed `Tokenized` `BooleanAlgebra`, +-- for use an an argument to `tokenClass`. newtype TokenTest token = TokenTest (RegExam token (TokenTest token)) -- | `TokenAlgebra` extends `Tokenized` methods to support From 26cd2b0a65145b95dd704aebb324eaf6c9fa8b75 Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Thu, 12 Feb 2026 12:00:08 -0800 Subject: [PATCH 08/32] Caveat notes Notes that some definitions are more powerful than name implies (bad for RegGrammar and good for CtxGrammar) Thanks to @mniip for bringing the latter to my awareness. --- src/Control/Lens/Grammar.hs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Control/Lens/Grammar.hs b/src/Control/Lens/Grammar.hs index 242ff48..cf6d204 100644 --- a/src/Control/Lens/Grammar.hs +++ b/src/Control/Lens/Grammar.hs @@ -23,7 +23,7 @@ module Control.Lens.Grammar , RegBnf (..) , regbnfG , regbnfGrammar - -- * Context-sensitive grammar + -- * Unrestricted, context-sensitive grammar , CtxGrammar , printG , parseG @@ -242,6 +242,11 @@ Just "69" If all `rule`s are non-recursive, then a `Grammar` can be rewritten as a `RegGrammar`. +Since Haskell permits general recursion, and `RegGrammar`s are +embedded in Haskell, one can define context-free grammars with them, +but its recommended to use `Grammar`s for `rule` abstraction +and generator support. + -} type Grammar token a = forall p. ( Lexical token p @@ -319,6 +324,14 @@ palindromeG = rule "palindrome" $ >>> [pal | word <- ["racecar", "word"], (pal, "") <- parseG palindromeG word] ["racecar"] + +Since Haskell permits computable predicates, +`CtxGrammar`s are embedded in Haskell, +and `Filtrator` is implied by the `Monadic` `Altenator` combinator `mfiltrate`, +the context-sensitivity of `CtxGrammar` also yields +general filtration, or _unrestricted_ grammars, +which can parse recursively enumerable languages. + -} type CtxGrammar token a = forall p. ( Lexical token p From 585702e9bd0b2817ef8c43b54eef917084b749ff Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Thu, 12 Feb 2026 12:30:49 -0800 Subject: [PATCH 09/32] Update Grammar.hs --- src/Control/Lens/Grammar.hs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Control/Lens/Grammar.hs b/src/Control/Lens/Grammar.hs index cf6d204..a7a95ac 100644 --- a/src/Control/Lens/Grammar.hs +++ b/src/Control/Lens/Grammar.hs @@ -56,8 +56,9 @@ import Witherable {- | A regular grammar may be constructed using `Lexical` and `Alternator` combinators. + Let's see an example using -[semantic versioning](https://semver.org/). +[semantic versioning](https://semver.org/) syntax. >>> import Numeric.Natural (Natural) >>> :{ @@ -222,7 +223,7 @@ arithGrammar = ruleRec "arith" sumG _Num . iso show read >? someP (asIn @Char DecimalNumber) :} -We can generate a `RegBnf`, printers and parsers from @arithGrammar@. +We can generate grammar strings, printers and parsers from @arithGrammar@. >>> putStringLn (regbnfG arithGrammar) {start} = \q{arith} @@ -231,7 +232,6 @@ We can generate a `RegBnf`, printers and parsers from @arithGrammar@. {number} = \p{Nd}+ {product} = \q{factor}(\*\q{factor})* {sum} = \q{product}(\+\q{product})* - >>> [x | (x,"") <- parseG arithGrammar "1+2*3+4"] [Add (Add (Num 1) (Mul (Num 2) (Num 3))) (Num 4)] >>> unparseG arithGrammar (Add (Num 1) (Mul (Num 2) (Num 3))) "" :: Maybe String @@ -241,11 +241,10 @@ Just "69" If all `rule`s are non-recursive, then a `Grammar` can be rewritten as a `RegGrammar`. - Since Haskell permits general recursion, and `RegGrammar`s are -embedded in Haskell, one can define context-free grammars with them, -but its recommended to use `Grammar`s for `rule` abstraction -and generator support. +embedded in Haskell, you can define context-free grammars with them. +But it's recommended to use `Grammar`s for `rule` abstraction +and generator support for `ruleRec`. -} type Grammar token a = forall p. @@ -354,8 +353,9 @@ type Lexical token p = ) :: Constraint {- | `RegString`s are an embedded domain specific language -of regular expression strings. Since they are strings, -they have a string-like interface. +of regular expression strings. + +Since they are strings, they have a string-like interface. >>> let rex = fromString "ab|c" :: RegString >>> putStringLn rex @@ -460,8 +460,14 @@ newtype RegString = RegString {runRegString :: RegEx Char} {- | `RegBnf`s are an embedded domain specific language of Backus-Naur forms extended by regular expression strings. + A `RegBnf` consists of a distinguished `RegString` "start" rule, and a set of named `RegString` `rule`s. + +>>> putStringLn (rule "baz" (terminal "foo" >|< terminal "bar") :: RegBnf) +{start} = \q{baz} +{baz} = foo|bar + Like `RegString`s they have a string-like interface. >>> let bnf = fromString "{start} = foo|bar" :: RegBnf From a640e69f4c5b65e60d2e720a597a799beddb924f Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Thu, 12 Feb 2026 12:34:46 -0800 Subject: [PATCH 10/32] Update Grammar.hs --- src/Control/Lens/Grammar.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Control/Lens/Grammar.hs b/src/Control/Lens/Grammar.hs index a7a95ac..0f82960 100644 --- a/src/Control/Lens/Grammar.hs +++ b/src/Control/Lens/Grammar.hs @@ -56,7 +56,6 @@ import Witherable {- | A regular grammar may be constructed using `Lexical` and `Alternator` combinators. - Let's see an example using [semantic versioning](https://semver.org/) syntax. From bf9881705b385c1d3c48895cd5ff0ed056579aa9 Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Thu, 12 Feb 2026 12:45:46 -0800 Subject: [PATCH 11/32] Update Grammar.hs --- src/Control/Lens/Grammar.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Control/Lens/Grammar.hs b/src/Control/Lens/Grammar.hs index 0f82960..ce60a59 100644 --- a/src/Control/Lens/Grammar.hs +++ b/src/Control/Lens/Grammar.hs @@ -293,8 +293,8 @@ The last action does get bonded to the pattern. Any unscoped bound action, @_ <- action@, also gets bonded to the pattern, but being unscoped means it isn't added to the context. -If all bound actions are unscoped, then a `CtxGrammar` can -be rewritten as a `Grammar` since it is context-free. +If all bound actions are unscoped, and filtration isn't used, +then a `CtxGrammar` can be rewritten as a `Grammar` since it is context-free. We can't generate a `RegBnf` since the `rule`s of a `CtxGrammar` aren't static, but dynamic and contextual. We can generate parsers and printers as expected. From 5affb7ed311a5178343e99559e0cef3a912ba949 Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Mon, 16 Feb 2026 13:26:58 -0800 Subject: [PATCH 12/32] imprism --- src/Control/Lens/Monocle.hs | 11 +++++++++++ src/Data/Profunctor/Monoidal.hs | 12 ++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/Control/Lens/Monocle.hs b/src/Control/Lens/Monocle.hs index c2d055e..40afc8f 100644 --- a/src/Control/Lens/Monocle.hs +++ b/src/Control/Lens/Monocle.hs @@ -19,6 +19,7 @@ module Control.Lens.Monocle , monocle , withMonocle , cloneMonocle + , imprism , mapMonocle , ditraversed , forevered @@ -61,6 +62,16 @@ monomorphically typed `Monocle` for different purposes. cloneMonocle :: AMonocle s t a b -> Monocle s t a b cloneMonocle mon = unwrapPafb . mapMonocle mon . WrapPafb +{- | Convert a `Monocle` to an improper `Prism`. + +>>> review (imprism (ditraversed @Complex)) (1 :: Double) +1.0 :+ 1.0 +>>> preview (imprism (ditraversed)) (1 :+ 2 :: Complex Double) +Just 1.0 +-} +imprism :: Monocle s t a b -> Prism s t a b +imprism mon = clonePrism mon + {- | Build a `Monocle` from a `Traversable` & `Distributive`, homogeneous, countable product. diff --git a/src/Data/Profunctor/Monoidal.hs b/src/Data/Profunctor/Monoidal.hs index b1aefa7..91d1d60 100644 --- a/src/Data/Profunctor/Monoidal.hs +++ b/src/Data/Profunctor/Monoidal.hs @@ -25,6 +25,7 @@ import Control.Applicative qualified as Ap (WrappedArrow) import Control.Arrow import Control.Lens hiding (chosen) import Control.Lens.Internal.Context +import Control.Lens.Internal.Prism import Control.Lens.Internal.Profunctor import Control.Lens.PartialIso import Data.Bifunctor.Clown @@ -247,3 +248,14 @@ instance (Profunctor p, Alternative (p a)) empty = proreturn empty ab <|> cd = proreturn (proextract ab <|> proextract cd) many = proreturn . many . proextract +instance Applicative (Market a b s) where + pure t = Market (pure t) (pure (Left t)) + Market f0 g0 <*> Market f1 g1 = Market + (\b -> f0 b (f1 b)) + (\s -> + case g0 s of + Left bt -> case g1 s of + Left b -> Left (bt b) + Right a -> Right a + Right a -> Right a + ) From ad5bddd3528a918b3b4977aa44548c7047f71cad Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Mon, 16 Feb 2026 13:39:09 -0800 Subject: [PATCH 13/32] filtration & partiality clarity --- src/Control/Lens/Grammar.hs | 9 ++++----- src/Control/Lens/PartialIso.hs | 2 +- src/Data/Profunctor/Distributor.hs | 2 +- src/Data/Profunctor/Filtrator.hs | 6 +++--- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Control/Lens/Grammar.hs b/src/Control/Lens/Grammar.hs index ce60a59..f6c6c87 100644 --- a/src/Control/Lens/Grammar.hs +++ b/src/Control/Lens/Grammar.hs @@ -323,11 +323,10 @@ palindromeG = rule "palindrome" $ >>> [pal | word <- ["racecar", "word"], (pal, "") <- parseG palindromeG word] ["racecar"] -Since Haskell permits computable predicates, -`CtxGrammar`s are embedded in Haskell, -and `Filtrator` is implied by the `Monadic` `Altenator` combinator `mfiltrate`, -the context-sensitivity of `CtxGrammar` also yields -general filtration, or _unrestricted_ grammars, +Since `CtxGrammar`s are embedded in Haskell which permits computable predicates, +and `Filtrator` has a default definition for `Monadic` `Alternator`s, +the context-sensitivity of `CtxGrammar` implies +general filtration of unrestricted grammars, which can parse recursively enumerable languages. -} diff --git a/src/Control/Lens/PartialIso.hs b/src/Control/Lens/PartialIso.hs index 64c927a..be822a2 100644 --- a/src/Control/Lens/PartialIso.hs +++ b/src/Control/Lens/PartialIso.hs @@ -222,7 +222,7 @@ infixl 4 >? (?<) pat = withPrism pat $ \f g -> unright . dimap (either id f) g infixl 4 ?< -{- | Action of `APartialIso` on `Choice` and `Cochoice` `Profunctor`s. -} +{- | Action of `APartialIso` on `Choice` & `Cochoice` partial profunctors. -} (>?<) :: (Choice p, Cochoice p) => APartialIso s t a b diff --git a/src/Data/Profunctor/Distributor.hs b/src/Data/Profunctor/Distributor.hs index 231d5ad..f74b342 100644 --- a/src/Data/Profunctor/Distributor.hs +++ b/src/Data/Profunctor/Distributor.hs @@ -330,7 +330,7 @@ class (Choice p, Distributor p, forall x. Alternative (p x)) prop> zeroP = empty prop> x >+< y = alternate (Left x) <|> alternate (Right y) - `alternate` has a default for `Cochoice`. + `alternate` has a default for `Choice` & `Cochoice` partial profunctors. -} alternate :: Either (p a b) (p c d) diff --git a/src/Data/Profunctor/Filtrator.hs b/src/Data/Profunctor/Filtrator.hs index 75a1e42..4538d09 100644 --- a/src/Data/Profunctor/Filtrator.hs +++ b/src/Data/Profunctor/Filtrator.hs @@ -42,7 +42,7 @@ class (Cochoice p, forall x. Filterable (p x)) `filtrate` is a distant relative to `Data.Either.partitionEithers`. - `filtrate` has a default for `Choice`. + `filtrate` has a default for `Choice` & `Cochcoice` partial profunctors. -} filtrate :: p (Either a c) (Either b d) @@ -56,9 +56,9 @@ class (Cochoice p, forall x. Filterable (p x)) &&& dimapMaybe (Just . Right) (either (const Nothing) Just) --- | `mfiltrate` can be used as `filtrate`, for `Monadic` `Alternator`s. +-- | `Filtrator` has a default definition for `Monadic` `Alternator`s. -- --- prop> mfiltrate = filtrate +-- prop> filtrate = mfiltrate mfiltrate :: (Monadic p, Alternator p) => p (Either a c) (Either b d) From 5042384f0bef03f27ede6f6a284c35f67c401397 Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Mon, 16 Feb 2026 14:01:03 -0800 Subject: [PATCH 14/32] testing for Brzozowski Matching unfortunately it's quite slow for most of the grammars with a lot of monoidal sequencing on large example strings I only enabled testing for regexGrammar and arithGrammar. --- test/Main.hs | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/test/Main.hs b/test/Main.hs index 06bb306..155ccb9 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -3,6 +3,7 @@ module Main (main) where import Data.Foldable hiding (toList) import Data.Maybe (listToMaybe) import Control.Lens.Grammar +import Control.Lens.Grammar.BackusNaur import Test.DocTest import Test.Hspec @@ -18,14 +19,14 @@ main :: IO () main = do doctests hspec $ do - testGrammar "regexGrammar" regexGrammar regexExamples - testGrammar "semverGrammar" semverGrammar semverExamples - testGrammar "semverCtxGrammar" semverCtxGrammar semverExamples - testGrammar "arithGrammar" arithGrammar arithExamples - testGrammar "jsonGrammar" jsonGrammar jsonExamples - testGrammar "sexprGrammar" sexprGrammar sexprExamples - testGrammar "lambdaGrammar" lambdaGrammar lambdaExamples - testGrammar "lenvecGrammar" lenvecGrammar lenvecExamples + describe "regexGrammar" $ for_ regexExamples $ testGrammarExample regexGrammar + describe "semverGrammar" $ for_ semverExamples $ testCtxGrammarExample semverGrammar + describe "semverCtxGrammar" $ for_ semverExamples $ testCtxGrammarExample semverCtxGrammar + describe "arithGrammar" $ for_ arithExamples $ testGrammarExample arithGrammar + describe "jsonGrammar" $ for_ jsonExamples $ testCtxGrammarExample jsonGrammar + describe "sexprGrammar" $ for_ sexprExamples $ testCtxGrammarExample sexprGrammar + describe "lambdaGrammar" $ for_ lambdaExamples $ testCtxGrammarExample lambdaGrammar + describe "lenvecGrammar" $ for_ lenvecExamples $ testCtxGrammarExample lenvecGrammar doctests :: IO () doctests = do @@ -77,16 +78,21 @@ doctests = do putStrLn modulePath doctest (modulePath : languageExtensions) -testGrammar :: (Show a, Eq a) => String -> CtxGrammar Char a -> [(a, String)] -> Spec -testGrammar name grammar examples = - describe name $ - for_ examples $ \(expectedSyntax, expectedString) -> do - it ("should parse from " <> expectedString <> " correctly") $ do - let actualSyntax = [parsed | (parsed, "") <- parseG grammar expectedString] - listToMaybe actualSyntax `shouldBe` Just expectedSyntax - it ("should unparse to " <> expectedString <> " correctly") $ do - let actualString = unparseG grammar expectedSyntax "" - actualString `shouldBe` Just expectedString - it ("should print to " <> expectedString <> " correctly") $ do - let actualString = ($ "") <$> printG grammar expectedSyntax - actualString `shouldBe` Just expectedString +testGrammarExample :: (Show a, Eq a) => Grammar Char a -> (a, String) -> Spec +testGrammarExample grammar (expectedSyntax, expectedString) = do + testCtxGrammarExample grammar (expectedSyntax, expectedString) + it ("should match from " <> expectedString <> " correctly") $ do + let actualMatch = expectedString =~ regbnfG grammar + actualMatch `shouldBe` True + +testCtxGrammarExample :: (Show a, Eq a) => CtxGrammar Char a -> (a, String) -> Spec +testCtxGrammarExample grammar (expectedSyntax, expectedString) = do + it ("should parse from " <> expectedString <> " correctly") $ do + let actualSyntax = [parsed | (parsed, "") <- parseG grammar expectedString] + listToMaybe actualSyntax `shouldBe` Just expectedSyntax + it ("should unparse to " <> expectedString <> " correctly") $ do + let actualString = unparseG grammar expectedSyntax "" + actualString `shouldBe` Just expectedString + it ("should print to " <> expectedString <> " correctly") $ do + let actualString = ($ "") <$> printG grammar expectedSyntax + actualString `shouldBe` Just expectedString From bd04c6891f8f45b324c4f3109e9dcbf671f1879c Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Mon, 16 Feb 2026 14:50:12 -0800 Subject: [PATCH 15/32] Update Monocle.hs --- src/Control/Lens/Monocle.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Control/Lens/Monocle.hs b/src/Control/Lens/Monocle.hs index 40afc8f..98c392e 100644 --- a/src/Control/Lens/Monocle.hs +++ b/src/Control/Lens/Monocle.hs @@ -62,7 +62,7 @@ monomorphically typed `Monocle` for different purposes. cloneMonocle :: AMonocle s t a b -> Monocle s t a b cloneMonocle mon = unwrapPafb . mapMonocle mon . WrapPafb -{- | Convert a `Monocle` to an improper `Prism`. +{- | Convert a `Monocle` to an improper `Control.Lens.Prism.Prism`. >>> review (imprism (ditraversed @Complex)) (1 :: Double) 1.0 :+ 1.0 From 1bcabc9ce128a90d6952e85a2ec5da11665ec5f0 Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Mon, 16 Feb 2026 14:50:17 -0800 Subject: [PATCH 16/32] Update Filtrator.hs --- src/Data/Profunctor/Filtrator.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Data/Profunctor/Filtrator.hs b/src/Data/Profunctor/Filtrator.hs index 4538d09..0431d5d 100644 --- a/src/Data/Profunctor/Filtrator.hs +++ b/src/Data/Profunctor/Filtrator.hs @@ -41,8 +41,11 @@ class (Cochoice p, forall x. Filterable (p x)) prop> unright = snd . filtrate `filtrate` is a distant relative to `Data.Either.partitionEithers`. + `filtrate` can be given a default value for `Monadic` `Alternator`s via `mfiltrate`. - `filtrate` has a default for `Choice` & `Cochcoice` partial profunctors. + prop> filtrate = mfiltrate + + `filtrate` has a default for `Choice` & `Cochoice` partial profunctors. -} filtrate :: p (Either a c) (Either b d) From 886859b2dd8d80766a1e3b457cf5744c6c2e956d Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Mon, 16 Feb 2026 15:22:00 -0800 Subject: [PATCH 17/32] Update Monocle.hs --- src/Control/Lens/Monocle.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Control/Lens/Monocle.hs b/src/Control/Lens/Monocle.hs index 98c392e..06c756d 100644 --- a/src/Control/Lens/Monocle.hs +++ b/src/Control/Lens/Monocle.hs @@ -64,10 +64,10 @@ cloneMonocle mon = unwrapPafb . mapMonocle mon . WrapPafb {- | Convert a `Monocle` to an improper `Control.Lens.Prism.Prism`. ->>> review (imprism (ditraversed @Complex)) (1 :: Double) -1.0 :+ 1.0 ->>> preview (imprism (ditraversed)) (1 :+ 2 :: Complex Double) -Just 1.0 +>>> review (imprism ditraversed) 1 :: Complex Int +1 :+ 1 +>>> preview (imprism ditraversed) (1 :+ 2 :: Complex Int) +Just 1 -} imprism :: Monocle s t a b -> Prism s t a b imprism mon = clonePrism mon From 114b1f827f9c56baf1595e1eaaf08ce3b1daa7a3 Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Mon, 16 Feb 2026 18:31:22 -0800 Subject: [PATCH 18/32] monomorphically --- src/Control/Lens/Diopter.hs | 3 +-- src/Control/Lens/Grate.hs | 3 +-- src/Control/Lens/Monocle.hs | 3 +-- src/Control/Lens/PartialIso.hs | 3 +-- src/Control/Lens/Wither.hs | 3 +-- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Control/Lens/Diopter.hs b/src/Control/Lens/Diopter.hs index c78b06c..e94584c 100644 --- a/src/Control/Lens/Diopter.hs +++ b/src/Control/Lens/Diopter.hs @@ -41,8 +41,7 @@ type Diopter s t a b = forall p f. (Distributor p, Applicative f) => p a (f b) -> p s (f t) -{- | If you see `ADiopter` in a signature for a function, -the function is expecting a `Diopter`. -} +{- | `ADiopter` is monomorphically a `Diopter`. -} type ADiopter s t a b = Dioptrice a b a (Identity b) -> Dioptrice a b s (Identity t) diff --git a/src/Control/Lens/Grate.hs b/src/Control/Lens/Grate.hs index 9db64ae..520065b 100644 --- a/src/Control/Lens/Grate.hs +++ b/src/Control/Lens/Grate.hs @@ -48,8 +48,7 @@ type Grate s t a b = forall p f. (Closed p, Monoidal p, Distributive f, Applicative f) => p a (f b) -> p s (f t) -{- | If you see `AGrate` in a signature for a function, -the function is expecting a `Grate`. -} +{- | `AGrate` is monomorphically a `Grate`. -} type AGrate s t a b = Grating a b a (Identity b) -> Grating a b s (Identity t) diff --git a/src/Control/Lens/Monocle.hs b/src/Control/Lens/Monocle.hs index 06c756d..da093f7 100644 --- a/src/Control/Lens/Monocle.hs +++ b/src/Control/Lens/Monocle.hs @@ -43,8 +43,7 @@ type Monocle s t a b = forall p f. (Monoidal p, Applicative f) => p a (f b) -> p s (f t) -{- | If you see `AMonocle` in a signature for a function, -the function is expecting a `Monocle`. -} +{- | `AMonocle` is monomorphically a `Monocle`. -} type AMonocle s t a b = Monocular a b a (Identity b) -> Monocular a b s (Identity t) diff --git a/src/Control/Lens/PartialIso.hs b/src/Control/Lens/PartialIso.hs index be822a2..6e581ee 100644 --- a/src/Control/Lens/PartialIso.hs +++ b/src/Control/Lens/PartialIso.hs @@ -117,8 +117,7 @@ some equivalence class of terms. -} type PartialIso' s a = PartialIso s s a a -{- | If you see `APartialIso` in a signature for a function, -the function is expecting a `PartialIso`. -} +{- | `APartialIso` is monomorphically a `PartialIso`. -} type APartialIso s t a b = PartialExchange a b a (Maybe b) -> PartialExchange a b s (Maybe t) diff --git a/src/Control/Lens/Wither.hs b/src/Control/Lens/Wither.hs index 72dd0be..8dd88a5 100644 --- a/src/Control/Lens/Wither.hs +++ b/src/Control/Lens/Wither.hs @@ -48,8 +48,7 @@ Every one of the following is a `Wither`. -} type Wither s t a b = forall f. Alternative f => (a -> f b) -> s -> f t -{- | If you see `AWither` in a signature for a function, -the function is expecting a `Wither`. -} +{- | `AWither` is monomorphically a `Wither`. -} type AWither s t a b = (a -> Altar a b b) -> s -> Altar a b t {- | `Witheroid`s generalize `Wither`s. From 0a301588dd1407d474a3744cc8111623bc64d19f Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Mon, 16 Feb 2026 18:56:17 -0800 Subject: [PATCH 19/32] Update Wither.hs --- src/Control/Lens/Wither.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Control/Lens/Wither.hs b/src/Control/Lens/Wither.hs index 8dd88a5..7b5d7c5 100644 --- a/src/Control/Lens/Wither.hs +++ b/src/Control/Lens/Wither.hs @@ -37,7 +37,6 @@ import Witherable {- | `Wither`s extends `Control.Lens.Traversal.Traversal`s by filtering. - Every one of the following is a `Wither`. * `Control.Lens.Iso.Iso` From 5f82d43d752f57b93d4deef350745579abf8e5b1 Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Mon, 16 Feb 2026 18:56:32 -0800 Subject: [PATCH 20/32] Update Grammar.hs --- src/Control/Lens/Grammar.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Control/Lens/Grammar.hs b/src/Control/Lens/Grammar.hs index f6c6c87..f0aeff1 100644 --- a/src/Control/Lens/Grammar.hs +++ b/src/Control/Lens/Grammar.hs @@ -309,10 +309,10 @@ We can generate parsers and printers as expected. [] In addition to context-sensitivity via `Monadic` combinators, -`CtxGrammar`s adds general filtration via `Filtrator` to `Grammar`s. -The `satisfy` function is a general character class. +`CtxGrammar`s add unrestricted filtration to `Grammar`s. +The `satisfy` combinator is an unrestricted token filter. And the `satisfied` pattern is used together with the `Choice` & -`Data.Profunctor.Cochoice` applicator `>?<` for general filtration. +`Data.Profunctor.Cochoice` applicator `>?<` for unrestricted filtration. >>> :{ palindromeG :: CtxGrammar Char String @@ -323,11 +323,11 @@ palindromeG = rule "palindrome" $ >>> [pal | word <- ["racecar", "word"], (pal, "") <- parseG palindromeG word] ["racecar"] -Since `CtxGrammar`s are embedded in Haskell which permits computable predicates, +Since `CtxGrammar`s are embedded in Haskell, permitting computable predicates, and `Filtrator` has a default definition for `Monadic` `Alternator`s, the context-sensitivity of `CtxGrammar` implies -general filtration of unrestricted grammars, -which can parse recursively enumerable languages. +unrestricted filtration of grammars by computable predicates, +which can recognize the class of recursively enumerable languages. -} type CtxGrammar token a = forall p. From fd1e9a2d743caba36c672edd059fb002f2f5e339 Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Mon, 16 Feb 2026 19:27:01 -0800 Subject: [PATCH 21/32] Update NestedPrismTH.hs --- src/Control/Lens/Internal/NestedPrismTH.hs | 41 ++++++++++++++++------ 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/Control/Lens/Internal/NestedPrismTH.hs b/src/Control/Lens/Internal/NestedPrismTH.hs index 83e2520..d0d7538 100644 --- a/src/Control/Lens/Internal/NestedPrismTH.hs +++ b/src/Control/Lens/Internal/NestedPrismTH.hs @@ -33,21 +33,40 @@ import qualified Data.Set as Set import Data.Set (Set) import Prelude --- | Generate a `Control.Lens.Prism.Prism` +-- | Similar to `Control.Lens.Internal.PrismTH.makePrisms`, +-- `makeNestedPrisms` generates a `Control.Lens.Prism.Prism` -- for each constructor of a data type. --- `Control.Lens.Iso.Iso`s generated when possible. --- `Control.Lens.Review.Review`s are created for constructors with existentially --- quantified constructors and GADTs. --- --- See `Control.Lens.Internal.PrismTH.makePrisms` for details and examples. +-- `Control.Lens.Iso.Iso`s are generated when possible. +-- `Control.Lens.Review.Review`s are generated for constructors +-- with existentially quantified constructors and GADTs. -- The difference in `makeNestedPrisms` -- is that constructors with @n > 2@ arguments -- will use right-nested pairs, rather than a flat @n@-tuple. --- This makes them suitable for use on the left-hand-side of --- `Control.Lens.PartialIso.>~`, --- `Control.Lens.PartialIso.>?` and `Control.Lens.PartialIso.>?<`; --- with repeated use of `Data.Profunctor.Distributor.>*<` --- on the right-hand-side, resulting in right-nested pairs. +-- This makes them suitable for bonding, +-- by use of the applicator `Control.Lens.PartialIso.>?` +-- to `Data.Profunctor.Monoidal.Monoidal` idiom notation +-- with `Data.Profunctor.Monoidal.>*<`, +-- or to `Data.Profunctor.Monadic.Monadic` qualified do-notation. +-- +-- /e.g./ +-- +-- @ +-- data FooBarBazBux a +-- = Foo Int +-- | Bar a +-- | Baz Int Char +-- | Bux Doube String Bool +-- makePrisms ''FooBarBazBux +-- @ +-- +-- will create +-- +-- @ +-- _Foo :: Prism' (FooBarBaz a) Int +-- _Bar :: Prism (FooBarBaz a) (FooBarBaz b) a b +-- _Baz :: Prism' (FooBarBaz a) (Int, Char) +-- _Bux :: Prism' (FooBarBaz a) (Double, (String, Bool)) +-- @ makeNestedPrisms :: Name -> DecsQ makeNestedPrisms typeName = do info <- D.reifyDatatype typeName From 497984522aa82813494439b923e4750717a396e5 Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Tue, 17 Feb 2026 08:54:11 -0800 Subject: [PATCH 22/32] simplify replicateP --- src/Data/Profunctor/Distributor.hs | 28 +++++++++++++++------------- src/Data/Profunctor/Monoidal.hs | 10 ++++++---- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/Data/Profunctor/Distributor.hs b/src/Data/Profunctor/Distributor.hs index f74b342..ceff5b3 100644 --- a/src/Data/Profunctor/Distributor.hs +++ b/src/Data/Profunctor/Distributor.hs @@ -93,7 +93,7 @@ class Monoidal p => Distributor p where {- | The zero structure morphism of a `Distributor`. - `zeroP` has a default for `Alternator`. + `zeroP` has a default for `Alternators`. prop> zeroP = empty -} @@ -103,7 +103,7 @@ class Monoidal p => Distributor p where {- | The sum structure morphism of a `Distributor`. - `>+<` has a default for `Alternator`. + `>+<` has a default for `Alternators`. prop> x >+< y = alternate (Left x) <|> alternate (Right y) -} @@ -315,7 +315,13 @@ instance Homogeneous Tree where {- | The `Alternator` class co-extends `Choice` and `Distributor`, as well as `Alternative`, adding the `alternate` method, -which is a lax monoidal structure morphism on sums. +which is a lax monoidal structure morphism on sums, with these +these laws relating them. + +prop> left' = alternate . Left +prop> right' = alternate . Right +prop> zeroP = empty +prop> x >+< y = alternate (Left x) <|> alternate (Right y) For the case of `Functor`s the analog of `alternate` can be defined without any other constraint, but the case of `Profunctor`s turns @@ -324,13 +330,8 @@ out to be slighly more complex. class (Choice p, Distributor p, forall x. Alternative (p x)) => Alternator p where - {- | - prop> left' = alternate . Left - prop> right' = alternate . Right - prop> zeroP = empty - prop> x >+< y = alternate (Left x) <|> alternate (Right y) - - `alternate` has a default for `Choice` & `Cochoice` partial profunctors. + {- | The structure morphism for an `Alternator`, + `alternate` has a default for `Choice` & `Cochoice` partial distributors. -} alternate :: Either (p a b) (p c d) @@ -418,7 +419,7 @@ several (SepBy beg end sep) p = iso toList fromList . eotList >~ beg >* (oneP >+< p >*< manyP (sep >* p)) *< end {- | -prop> several1 noSep p = someP p +prop> several1 noSep = someP -} several1 :: (IsList s, IsList t, Distributor p, Choice p) @@ -451,8 +452,9 @@ chain1 association pat (SepBy beg end sep) = leftOrRight chainl1 chainr1 {- | `intercalateP` adds a `SepBy` to `replicateP`. -} intercalateP - :: (Monoidal p, Choice p, AsEmpty s, AsEmpty t, Cons s t a b) - => Int -> SepBy (p () ()) -> p a b -> p s t + :: (Monoidal p, Choice p, AsEmpty s, Cons s s a a) + => Int {- ^ number of repetitions -} + -> SepBy (p () ()) -> p a a -> p s s intercalateP n (SepBy beg end _) _ | n <= 0 = beg >* lmap (const Empty) asEmpty *< end intercalateP n (SepBy beg end comma) p = diff --git a/src/Data/Profunctor/Monoidal.hs b/src/Data/Profunctor/Monoidal.hs index 91d1d60..7c6d163 100644 --- a/src/Data/Profunctor/Monoidal.hs +++ b/src/Data/Profunctor/Monoidal.hs @@ -127,11 +127,13 @@ ditraverse ditraverse p = traverse (\f -> lmap f p) (distribute id) {- | `replicateP` is analagous to `Control.Monad.replicateM`, -for `Monoidal` & `Choice` `Profunctor`s. -} +for `Monoidal` & `Choice` `Profunctor`s. When the number +of repetitions is less than or equal to 0, it returns `asEmpty`. +-} replicateP - :: (Monoidal p, Choice p, AsEmpty s, AsEmpty t, Cons s t a b) - => Int -> p a b -> p s t -replicateP n _ | n <= 0 = lmap (const Empty) asEmpty + :: (Monoidal p, Choice p, AsEmpty s, Cons s s a a) + => Int {- ^ number of repetitions -} -> p a a -> p s s +replicateP n _ | n <= 0 = asEmpty replicateP n a = a >:< replicateP (n-1) a {- | For any `Monoidal`, `Choice` & `Strong` `Profunctor`, From 9e19e1ee21fccbe8403dde82ceb1185d035f35c3 Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Tue, 17 Feb 2026 09:02:05 -0800 Subject: [PATCH 23/32] Update Distributor.hs --- src/Data/Profunctor/Distributor.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Data/Profunctor/Distributor.hs b/src/Data/Profunctor/Distributor.hs index ceff5b3..31ca2ea 100644 --- a/src/Data/Profunctor/Distributor.hs +++ b/src/Data/Profunctor/Distributor.hs @@ -93,7 +93,7 @@ class Monoidal p => Distributor p where {- | The zero structure morphism of a `Distributor`. - `zeroP` has a default for `Alternators`. + `zeroP` has a default for `Alternator`s. prop> zeroP = empty -} @@ -103,7 +103,7 @@ class Monoidal p => Distributor p where {- | The sum structure morphism of a `Distributor`. - `>+<` has a default for `Alternators`. + `>+<` has a default for `Alternator`s. prop> x >+< y = alternate (Left x) <|> alternate (Right y) -} From d0d8a5682995874bfc80937b6bbd8be51541a79c Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Tue, 17 Feb 2026 09:06:10 -0800 Subject: [PATCH 24/32] Update Distributor.hs --- src/Data/Profunctor/Distributor.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/Profunctor/Distributor.hs b/src/Data/Profunctor/Distributor.hs index 31ca2ea..dfbb109 100644 --- a/src/Data/Profunctor/Distributor.hs +++ b/src/Data/Profunctor/Distributor.hs @@ -456,6 +456,6 @@ intercalateP => Int {- ^ number of repetitions -} -> SepBy (p () ()) -> p a a -> p s s intercalateP n (SepBy beg end _) _ | n <= 0 = - beg >* lmap (const Empty) asEmpty *< end + beg >* asEmpty *< end intercalateP n (SepBy beg end comma) p = beg >* p >:< replicateP (n-1) (comma >* p) *< end From 06aa886b4543b1077379734a26183ee195f4eda0 Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Tue, 17 Feb 2026 09:07:27 -0800 Subject: [PATCH 25/32] Update Main.hs --- test/Main.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Main.hs b/test/Main.hs index 155ccb9..2b7079b 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -81,7 +81,7 @@ doctests = do testGrammarExample :: (Show a, Eq a) => Grammar Char a -> (a, String) -> Spec testGrammarExample grammar (expectedSyntax, expectedString) = do testCtxGrammarExample grammar (expectedSyntax, expectedString) - it ("should match from " <> expectedString <> " correctly") $ do + it ("should match " <> expectedString <> " correctly") $ do let actualMatch = expectedString =~ regbnfG grammar actualMatch `shouldBe` True From 03068f43a161eaae345d9162f1a1d343cb9d7b96 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 23:52:50 +0000 Subject: [PATCH 26/32] Initial plan From f0ed836114d338d03c0f10d77efe3134b68d39fa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 23:53:27 +0000 Subject: [PATCH 27/32] Increment minor version to 0.4.0.0 Co-authored-by: echatav <2265940+echatav@users.noreply.github.com> --- distributors.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distributors.cabal b/distributors.cabal index 9a92b07..4b3429a 100644 --- a/distributors.cabal +++ b/distributors.cabal @@ -5,7 +5,7 @@ cabal-version: 2.2 -- see: https://github.com/sol/hpack name: distributors -version: 0.3.0.0 +version: 0.4.0.0 synopsis: Unifying Parsers, Printers & Grammars description: Distributors provides mathematically inspired abstractions for coders to write parsers that can also be inverted to printers. category: Profunctors, Optics, Parsing From b96bfbe33c2f896e2e30c83d3d4f6606d779f095 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 23:55:11 +0000 Subject: [PATCH 28/32] Correct version to 0.3.0.1 Co-authored-by: echatav <2265940+echatav@users.noreply.github.com> --- distributors.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distributors.cabal b/distributors.cabal index 4b3429a..35f40e4 100644 --- a/distributors.cabal +++ b/distributors.cabal @@ -5,7 +5,7 @@ cabal-version: 2.2 -- see: https://github.com/sol/hpack name: distributors -version: 0.4.0.0 +version: 0.3.0.1 synopsis: Unifying Parsers, Printers & Grammars description: Distributors provides mathematically inspired abstractions for coders to write parsers that can also be inverted to printers. category: Profunctors, Optics, Parsing From 3f4680a2932861d496ad5b481badbccea41d3e6e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Feb 2026 00:00:22 +0000 Subject: [PATCH 29/32] Initial plan From e5805a562568628695214b90209c54f3dee6e05a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Feb 2026 00:00:58 +0000 Subject: [PATCH 30/32] Fix spelling error: Doube -> Double in NestedPrismTH.hs comment Co-authored-by: echatav <2265940+echatav@users.noreply.github.com> --- src/Control/Lens/Internal/NestedPrismTH.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Control/Lens/Internal/NestedPrismTH.hs b/src/Control/Lens/Internal/NestedPrismTH.hs index d0d7538..f501976 100644 --- a/src/Control/Lens/Internal/NestedPrismTH.hs +++ b/src/Control/Lens/Internal/NestedPrismTH.hs @@ -55,7 +55,7 @@ import Prelude -- = Foo Int -- | Bar a -- | Baz Int Char --- | Bux Doube String Bool +-- | Bux Double String Bool -- makePrisms ''FooBarBazBux -- @ -- From 9b53de6722bca82acf24ac1dde135295a3b00159 Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Wed, 25 Feb 2026 16:42:00 -0800 Subject: [PATCH 31/32] Update distributors.cabal --- distributors.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distributors.cabal b/distributors.cabal index 35f40e4..9a92b07 100644 --- a/distributors.cabal +++ b/distributors.cabal @@ -5,7 +5,7 @@ cabal-version: 2.2 -- see: https://github.com/sol/hpack name: distributors -version: 0.3.0.1 +version: 0.3.0.0 synopsis: Unifying Parsers, Printers & Grammars description: Distributors provides mathematically inspired abstractions for coders to write parsers that can also be inverted to printers. category: Profunctors, Optics, Parsing From 01507dfb4c233895bbddad2720baf0a39d4a9538 Mon Sep 17 00:00:00 2001 From: Eitan Chatav Date: Wed, 25 Feb 2026 19:28:43 -0800 Subject: [PATCH 32/32] Update Distributor.hs --- src/Data/Profunctor/Distributor.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/Profunctor/Distributor.hs b/src/Data/Profunctor/Distributor.hs index dfbb109..bc8d17d 100644 --- a/src/Data/Profunctor/Distributor.hs +++ b/src/Data/Profunctor/Distributor.hs @@ -347,7 +347,7 @@ class (Choice p, Distributor p, forall x. Alternative (p x)) {- | One or more. -} someP :: p a b -> p [a] [b] - someP p = _Cons >? p >*< manyP p + someP x = x >:< manyP x -- | Combines all `Alternative` choices in the specified list. choice :: (Foldable f, Alternative p) => f (p a) -> p a