Also, `zoom` needs a `Lens`, too (technically just a `Focusing`, but you
get the idea). This lets you do things like:
example :: Parser ByteString IO ()
example = do
-- Decode 10 `Int`s into a list
ints <- zoom (decoded . splitAt 10) drawAll
lift (print ints)
-- Now decode 10 parsed values into a list(using some arbitrary
`attoparsec` parser)
xs <- zoom (parsed parseX . splitAt 10) drawAll
lift (print xs)
If you made `decoded` a `Getter` then you would have to apply it on the
`Producer` end and then you wouldn't be able mix different `ByteString`
parsers like in the above example. Intuitively, this would be because
the `attoparsec` parsers wouldn't know where to correctly begin because
a `Getter` doesn't have enough information to restore unused leftovers
from decoding binary values.
On 02/08/2014 10:23 PM, Danny Navarro wrote:
Oh, I forgot that `over` needs a `Setter`. Now I can see why it's not
worth giving it up.
On Sat, Feb 8, 2014 at 4:12 PM, Gabriel Gonzalez
<[email protected]> wrote:
For people who haven't been following this discussion on Github, I'm
paraphrasing the answer I gave here:
https://github.com/k0001/pipes-binary/issues/12#issuecomment-34539242
For `decoded` (and similar lenses with error continuation return
values) the additional power of an `Iso` is more trouble than its
worth. For example, given an `Iso` like:
Iso' (Producer a m r) (Producer b m (Either (SomeError ...) ...))
... the user wouldn't be able to use the reverse direction unless
they first fixed the return value to be an `Either`. The only case
where the return value is already going to be in the correct form is
if they just decoded it using the same `Iso'`, but in those cases you
don't need something as powerful as an `Iso'`, because you can just
use `over`, which only requires a `Lens`. For example, let's say I
wanted to decode a stream of `Int`s, increment all of them, and then
re-encode them. I would just write:
over decoded (>-> Pipes.Prelude.map ((+1) :: Int -> Int))
:: Monad m => Producer ByteString m r -> Producer ByteString m r
That only requires a `Lens` and not an `Iso`.
Note that not all such transformations can be expressed in terms of
`over`. For example, you can't use `over` to take the first N
decoded ints this way (because the re-encoded result must have the
same return value). For these cases, the idiomatic solution I
recommend is to use `encoder`, which is just `for cat encode`:
example = view decoded someByteStream >-> Pipes.Prelude.take 5
>-> encoder
encoder :: (Monad m, Binary a) => Pipe a ByteString m r
encoder = for cat encode
-- ... plus some shortcut fusion rules
I just submitted a pull request for `encoder` here:
https://github.com/k0001/pipes-binary/pull/15
In this case I definitely think that `decoded` should not be a
`Getter` and should stay at least a `Lens`. The reversibility is
good enough for most purposes and you don't really need the `Iso` to
re-encode things. Also, `zoom decoded` and `over decoded` are too
useful to give up.
However, I was planning on suggesting providing non-lens versions of
many lenses for beginners. I will discuss this in a separate thread.
On 02/08/2014 09:50 PM, Danny Navarro wrote:
While discussing whether `decoded` in `pipes-binary` should be an
`Iso'` a `Getter'` or a `Lens'` [1], I started thinking what would
be the advantages of each one from the parsing point of view.
The obvious advantage of the `Iso'` would be that it's automatically
reversible using `from`[2], so you get `encoded` for free. But it
turns out it's not truly reversible when facing errors,
`lens-family` doesn't include the `from` function and it's in the
end not that obvious to use for end users.
Using `Lens'`es for `decoded` and `encoded` raises the question that
if we are considering the lenses irreversible, why not just use
`Getter'`s? They wouldn't violate the lens laws and could be
composed correctly with other lenses, folds, traversals..., when
applicable. After all, if we have `Getter`s that transform
`Producer`s as we need, what's the use of the *setter* part of a
`Producer` lens?
Well, it turns out that `zoom` can't use `Getter`s [3], but I fail
to understand how it uses the *setter* part, changing what the
*setter* does, doesn't seem to affect `zoom`. There is also
`magnify` which works like `zoom` with `Getter`s but it can't
operate on the state monad [4].
So I was thinking why not include a version of `zoom` in
`pipes-parse` that works with `Getter`s, using `mapStateT` for
example [5], and base all pipes parsing on `Getter`s by default?
What would be the disadvantages of this approach?
[1] https://github.com/k0001/pipes-binary/issues/12
[2]
http://hackage.haskell.org/package/lens-4.0.1/docs/Control-Lens-Iso.html#v:from
[3]
http://hackage.haskell.org/package/lens-4.0.1/docs/Control-Lens-Zoom.html#v:zoom
[4]
http://hackage.haskell.org/package/lens-4.0.1/docs/Control-Lens-Zoom.html#v:magnify
[5]
http://hackage.haskell.org/package/transformers-0.3.0.0/docs/Control-Monad-Trans-State-Strict.html#v:mapStateT
-- Danny Navarro
--
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].