I agree with Gabriel for the following reasons:

* The errors given by the lenses are horrific. It's extremely hard to 
prototype using the lenses and this is bound to cause frustration for 
people who aren't familiar with lenses. A lens-less API with simpler types 
is absolutely necessary if you're looking for wide adoption by both 
seasoned and amateur users.

The error in Gabriels last post was one I happened to get during naive 
usage of the lenses in a tiny test project and it made me so frustrated I 
almost dropped the entire pipes ecosystem.

* While lens definitely has merits, it's annoying to have to pull in and 
maintain the bounds on a lens dependency if the rest of the project doesn't 
use lens. lens-family-core doesn't solve this issue, it just makes the 
dependency smaller. By providing these inside the package, the writer of 
the next package downstream doesn't need to worry about it but has the 
option to use the lenses if they are already using them as a dependency.

On Sunday, February 9, 2014 3:04:01 AM UTC+11, 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].
To post to this group, send email to [email protected].

Reply via email to