optics icon indicating copy to clipboard operation
optics copied to clipboard

Add getterVL

Open tomjaguarpaw opened this issue 1 month ago • 9 comments

A user was confused why lensVL didn't work on a Control.Lens.Getter. The answer is, of course, because lensVL expects a Lens, not a Getter. So the user should use getterVL instead. But it doesn't exist! Is there any reason why it couldn't or shouldn't?

tomjaguarpaw avatar Nov 29 '25 09:11 tomjaguarpaw

Given that the builds on my PR fail on 8.2 and 8.4 due to missing Data.Functor.Contravariant, I guess the reason for this missing function is to avoid depending on contravariant. If so then perhaps optics should just bite the bullet and do https://github.com/well-typed/optics/issues/532. If optics is going to stick with 8.4 support, could the documentation be updated to explain why getterVL doesn't exist?

tomjaguarpaw avatar Nov 29 '25 10:11 tomjaguarpaw

Can't you use Optics.to (Lens.view l) to convert getters. I don't feel that VL conversion adds anything. (You simply implement Lens.view in getter in your PR).

phadej avatar Nov 29 '25 13:11 phadej

Sure you can, just like you can use Optics.lens (Lens.get l) (Lens.set l) instead of lensVL. But it seems like having opticVL for every optic[^1] type

  1. is good for consistency
  2. avoids needing to add lens to your dependencies

[^1]: At least, every optic type that exists in both libraries. I'm not sure if there are any that don't.

tomjaguarpaw avatar Nov 29 '25 14:11 tomjaguarpaw

I agree with @tomjaguarpaw: consistency is worthwhile here. Dropping GHC 8.4 and earlier to address this and #535 seems like an acceptable trade-off to me, although I wonder if it's also feasible to conditionally depend on contravariant for older base vesions?

adamgundry avatar Dec 01 '25 08:12 adamgundry

My concern is that (my) idea of optics-vl is to convert functions in libraries (say Cabal-syntax) providing lens compatible definitions to optics (e.g. https://hackage-content.haskell.org/package/Cabal-syntax-3.16.0.0/docs/Distribution-Types-Library-Lens.html)

So

Sure you n, just like you can use Optics.lens (Lens.get l) (Lens.set l) instead of lensV

we agree, that is not good, having lensVL has value.

However, if a library provides Getter s a functions, instead of just (or in addition to) s -> a, I'd say, that library ought to be fixed. In other words, if I were to see getterVL used, I'd call it a code smell; something in design is probably not right there.

So while being complete has value, I feel that we shouldn't not need it in practice.


FWIW, similarly with Folds. Libraries usually provide something in the shape of foldr, instead of (Applicative f, Contravariant f) => ...; or maybe they have something in shape of traverse_ / for_; a Fold like in lens really exists in developments using lens.


So in summary think of optics-vl as "helpers for converting functions in libraries, like Cabal-syntax"; not a full suite of converting lens stuff into optics. My opinion is that getterVL shouldn't exist, or if would, it should be really warned about.

phadej avatar Dec 01 '25 12:12 phadej

However, if a library provides Getter s a functions, instead of just (or in addition to) s -> a, I'd say, that library ought to be fixed

Here is the Getter in question: https://hackage-content.haskell.org/package/jose-0.12/docs/Crypto-JOSE-JWA-JWK.html#v:asPublicKey

and here is a real world case of a user getting confused by it: https://discourse.haskell.org/t/another-lens-optics-type-difference/13335?u=tomjaguarpaw

It seems to me that it costs optics very little to support such use cases.

tomjaguarpaw avatar Dec 01 '25 14:12 tomjaguarpaw

Here is the

And I argue, that is just silly. Why not have

class AsPublicKey k where
  asPublicKey :: k -> Maybe k

and not confuse users with optics at all.

EDIT: And jose already pulls in lens into your transitive dependencies, so depending on lens isn't going to make compile time worse.

phadej avatar Dec 01 '25 16:12 phadej

And I argue, that is just silly. Why not have

I can see the argument, but then why have Getter at all? You may as well have lens and getter functions.

And jose already pulls in lens into your transitive dependencies

Sure, but in general that library could be used with optics as long as optics (or someone) provides the conversion functions.

tomjaguarpaw avatar Dec 01 '25 17:12 tomjaguarpaw

I can see the argument, but then why have Getter at all?

In lens? Because you can compose optics, compose Getter with Traversal, and the result will be a Fold.

But I'd argue that in that case, using a library, you'd write foldOf (traversed . to asPublicKey) things.

I.e. you need to give functions like to types (yet in lens it creates something a bit more general than lens' Getter); but you shouldn't need to use Getter in library APIs (nor really Fold, any read-only optics TBH; and probably not Setter alone either as (a -> a) -> (s -> s) composes nicely and is useful for non lens users).

EDIT: So TL;DR, really the only case where I cannot think of a better API then using lens definition is prisms. Without optics vocabulary, providing separate match and build functions is not convenient. For everything else libraries ought to stick to "boring Haskell" (and I consider traverse a boring Haskell function, it just happens to work as is as lens optic).

But in many cases only one direction of a Prism is enough, and it might make sense to provide just that. E.g. AsError in jose problably needs only other half (build). I'm not 100% sure, I haven't looked closely, but that's my experience with errory types.

phadej avatar Dec 01 '25 17:12 phadej