generic-lens
generic-lens copied to clipboard
Recursing with typed as well as types
data A = A deriving Generic
data B = B deriving Generic
data C = C deriving Generic
data D = D deriving Generic
data E = E deriving Generic
data F = F deriving Generic
data G = G deriving Generic
data A1 = A1 A B C deriving Generic
data A2 = A2 A
data A3 = A3 B C D
data A4 = A4 D E F deriving Generic
data A5 = A5 A1 A4 G deriving Generic
data A6 = A6 A1 A2 deriving Generic
-- examples
let a5 = A5 (A1 A B C) (A4 D E F) G
let a6 = A6 (A1 A B C) (A2 A)
-- OK!
let f = a5 ^. typed @A1
-- OK!
let g = a5 ^. typed @A1 . typed @B
-- OK!
let h = safeHead $ a5 ^.. types @B
-- Not OK - A5 has no field B
let i = a5 ^. typed @B
-- Not OK - A6 has no field A, but ideally it should be Not OK - A6 has duplicate A
let j = a6 ^. typed @A
I noticed there was #75 which seems to suggest it's possible, but having scoured the linked issues and pull requests I can't quite find out how.
In retrospect, I think this probably falls into problems with sum types; there is no Lens' (Maybe A1) B
...
I implemented this feature a while ago (it might be on a branch? I can't remember), but never merged it because of how brittle I thought it was. Since the lens focuses on a single element, it only makes sense if the whole type has exactly one value of that type (as your A6 example shows). We have to look deep, and if any type in the tree has no Generic instance then the procedure fails. (though the "types" combinator suffers from the same issue, and that has a workaround)
I thought about maybe taking an additional numeric argument to specify the maximum depth, but that seemed inelegant. Since then, I've actually whished for this feature a few times myself, and I'm now thinking that this might be a good thing to do after all.
I won't have too much time/energy in the near future to work on it though. I'll keep it in mind the next time I come back here!
Makes sense, thanks! I'll have a peruse and see if it's on a branch somewhere :)
I just bumped into this -- I wanted a setup where I could "peel off" layers of the environment in a MonadReader (the regular Has pattern I think), started doing it manually, found that generic-lens could automate it for me. typed
not recursing means it's a bit off the behaviour I wanted. I tried providing an instance HasType Outer Inner
that would go through a Middle
type, but of course it overlaps.
I think a deep search would give me the exact functionality I want, since if I did end trying to use a Has instance where the type has != 1 value of the type, I'd want a type error (in other words, if I was doing it manually, I wouldn't have written such an instance). If I could receive a few pointers for this I might be able to look into it? Not done generics before though...!