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].
To post to this group, send email to [email protected].

Reply via email to