Reputation: 48631
Suppose I have
getXY :: X a -> Y a
setXY :: C a => X a -> Y a -> X a
Is it possible to write something lens-like that imposes C a
only when setting? I'm interested in both Van Laarhoven and profunctor-style optics.
Upvotes: 3
Views: 140
Reputation: 3924
I can't say for certain, but with exactly the types you've given, I don't see how.
That said, are you being overly restrictive in your types? Could it be possible that setXY
might be allowed to have the type:
setXY :: C b => X a -> Y b -> X b
In other words, the constraint C
matters for the second argument and the output but not for the input? If so, then you can make the following lens:
lensXY :: C b => Lens (X a) (X b) (Y a) (Y b)
lensXT = lens getXY setXY
This is still a somewhat strange lens. For instance, say you have a value of type x :: X Int
, but Int
is not an instance of C
. Then, you'll get an error if you try x ^. lensXY
. Funny enough, this isn't a flaw with the lens so much as with ^.
, which has an overly restrictive type.
Now consider if we redefine ^.
as follows:
(^.) :: s -> Lens s t a b -> a
s ^. l = getConst (l Const s)
Note that the function definition is exactly the same as the previous definition, but we've changed the type signature (it's more general for lenses but more constrained for everything else). Now, we can almost execute x ^. lensXY
, but the problem now is that GHC doesn't know what types to use for t
and b
. Specifically, it needs a type that satisfies the constraint C
. So, let's make one final assumption: that there is an instance C Bool
. Then, we can write x ^. lensXY @Bool
, and it type-checks and extracts the Y a
value even though a
is not an instance of C
.
On the other hand, using %~
or similar still demands the class constraint.
Upvotes: 2