That's exactly right. `pipes-aeson` can take advantage of the additional encoding information to provide full lenses, whereas parsers in general cannot.

On 2/9/2014 8:33 AM, Pierre R wrote:
Thanks that clears things up. I immediately didn't recognize the Getter in
parsed
:: (Monad m, ParserInput a)
=> Attoparsec.Parser a b -- ^ Attoparsec parser
-> Producer a m r -- ^ Raw input
-> Producer b m (Either (ParsingError, Producer a m r) r)

So if I get this right, it is not good to have `Lens'` in `pipes-attoparsec` because it is not clear the transformation is reversible for each `attoparsec` parsers but it would be nice to have one for the specific `json'` parser in `pipes-aeson`.

On Sunday, February 9, 2014 2:51:09 PM UTC+1, Gabriel Gonzalez wrote:

    So there are two uses of lenses in the `pipes` ecosystem:
    `pipes-parse` and `pipes-group` (i.e. the `FreeT` stuff).  What I
    was referring to was the use of lenses in `pipes-group`.  The
    rough rule of thumb for when to use lenses in `pipes-group` is:
    pretty much always (i.e. every "splitter" should be a lens).

    I can also answer your question about when to use lenses in
    `pipes-parse`, too.  The answer is: if the transformation is
    reversible, make it a lens.  If the transformation is not
    reversible, make it a function between `Producer`s (i.e. a
    "Getter", which is what it's isomorphic to).

    This is why `pipes-parse` is nothing but "Getter"s: none of the
    transformations are reversible.

    To learn more about this last point, read the `pipes-parse`
    tutorial, particularly the section on `Getter`s:

    
http://hackage.haskell.org/package/pipes-parse-3.0.1/docs/Pipes-Parse-Tutorial.html#g:4
    
<http://hackage.haskell.org/package/pipes-parse-3.0.1/docs/Pipes-Parse-Tutorial.html#g:4>

    On 02/09/2014 05:01 PM, Pierre R wrote:
    Is there any documentation about when it is not a good idea to
    define `lens` ?

    I am thinking about `pipes-attoparsec` that has chosen not to
    define any.

    I know there is a thread that talk about this. It might be nice
    to document this kind of argument.

    If `lens` is not always the best approach (even for skilled lens
    users), then I would move them in a separated module as Renzo
    suggested (such as `Pipes.Parse.Lens` and I would document the
    pro and cons there)

    On Saturday, February 8, 2014 5:04:01 PM UTC+1, Gabriel Gonzalez
    wrote:

        I got a couple of confused users upon the update to the
        lens-based API,
        mainly because it wasn't obvious to them how lenses worked
        and how to
        use `view` to turn a `Lens a b` into `a -> b`.  For example,
        they didn't
        realize that they could get back the original `decodeGetMany`
        by using
        `view decodeGetL`.

        So I wanted to ask if there were any objections to me adding
        unidirectional non-lens versions of several lenses.  I think
        the easiest
        way to explain what I mean is to just give a few examples. Take
        `Pipes.Parse.groupBy`, for example:

             groupBy
                 :: Monad m
                 => (a -> a -> Bool)
                 -> Lens' (Producer a m x) (Producer a m (Producer a
        m x))
             groupBy equals k p0 = fmap join (k (to p0))
               where
             --  to :: Monad m => Producer a m r -> Producer a m
        (Producer a m x)
                 to p = do
                     x <- lift (next p)
                     case x of
                         Left   r      -> return (return r)
                         Right (a, p') -> (yield a >> p') ^. span
        (equals a)

        I'd probably take the internal `to` function and promote it to a
        top-level exported function, named `groupBy_`:

             groupBy_ :: Monad m => (a -> a -> Bool) -> Producer a m
        x ->
        Producer a m (Producer a m x)
             groupBy_ equals p = ...

        Then define the `groupBy` lens in terms of `groupBy_`:

             groupBy equals k p = fmap join (k (groupBy_ p))

        Another example is the `lines` function from `Pipes.ByteString`:

             lines
                 :: Monad m
                 => Iso' (Producer ByteString m x) (FreeT (Producer
        ByteString
        m) m x)
             lines = Data.Profunctor.dimap _lines (fmap _unlines)
               where
                 -- _lines
                 --     :: Monad m
                 --     => Producer ByteString m x -> FreeT (Producer
        ByteString
        m) m x
                 _lines p0 = PG.FreeT (go0 p0)
                   where
                     go0 p = do
                         x <- next p
                         case x of
                             Left   r       -> return (PG.Pure r)
                             Right (bs, p') ->
                                 if (BS.null bs)
                                 then go0 p'
                                 else return $ PG.Free $ go1 (yield
        bs >> p')
                     go1 p = do
                         p' <- p^.line
                         return $ PG.FreeT $ do
                             x  <- nextByte p'
                             case x of
                                 Left   r       -> return (PG.Pure r)
                                 Right (_, p'') -> go0 p''

                 -- _unlines
                 --     :: Monad m
                 --      => FreeT (Producer ByteString m) m x ->
        Producer
        ByteString m x
                 _unlines = concats . PG.maps addNewline

                 -- addNewline
                 --     :: Monad m => Producer ByteString m r ->
        Producer
        ByteString m r
                 addNewline p = p <* yield (BS.singleton nl)

        I'd promote `_lines` and `_unlines` to top-level exported
        functions,
        except I would change the names to `lines_` and `unlines_`:

             lines_ :: Monad m => Producer ByteString m x -> FreeT
        (Producer
        ByteString m) m x

             unlines_ :: Monad m => FreeT (Producer ByteString m) m x ->
        Producer ByteString m x

        ... and define the `lines` lens in terms of them:

             lines = Data.Profunctor.dimap lines_ (fmap unlines_)

        So I would still be keeping the lenses and isomorphisms, but
        also adding
        unidirectional non-lens equivalents to give beginners an easier
        toe-hold.  What do you all think?

-- You received this message because you are subscribed to the
    Google Groups "Haskell Pipes" group.
    To unsubscribe from this group and stop receiving emails from it,
    send an email to [email protected] <javascript:>.
    To post to this group, send email to [email protected]
    <javascript:>.

--
You received this message because you are subscribed to the Google Groups "Haskell Pipes" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].

--
You received this message because you are subscribed to the Google Groups "Haskell 
Pipes" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].

Reply via email to