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].

Reply via email to