DanBurton / lens-family-th

Template Haskell to generate lenses for lens-family and lens-family-core
BSD 3-Clause "New" or "Revised" License
7 stars 4 forks source link

More general type signatures for `makeTraversals` #12

Closed Gabriella439 closed 9 years ago

Gabriella439 commented 9 years ago

Right now I'm working on a package for exhaustive pattern matching using Traversals (you can find the code here). However, for the trick to work with auto-generated Traversals I need them to have more general types.

For example, let's assume that we were to generate Traversals for the Either type:

data Either a b

makeTraversals ''Either

This would generate something like this:

_Left :: Applicative f => (a -> f a) -> (Either a b -> f (Either a b))
_Left k (Left a) = fmap Left (k a)
_Left _  x       = pure x

_Right :: Applicative f => (b -> f b) -> (Either a b -> f (Either a b))
_Right k (Right a) = fmap Right (k a)
_Right _  x        = pure x

However, I actually need the following more general types, which requires complicating the implementation:

_Left :: Applicative f => (a -> f a') -> (Either a b -> f (Either a' b))
_Left k (Left  a) = fmap Left (k a)
_Left _ (Right b) = pure (Right b)

_Right :: Applicative f => (b -> f b') -> (Either a b -> f (Either a b'))
_Right k (Left  a) = pure (Left a)
_Right k (Right b) = fmap Right (k b)

The type signatures are not the complicated part because they can be omitted (the inferred types would still be correct). What makes this more complex is that you can no longer implement the Traversal in terms of two cases (the matching constructor and the fallback). Instead, you must now provide a case for every constructor.

This is not urgent, because I can work around this by using the makePrisms function from Control.Lens.TH, which does produce the fully general type signatures.

DanBurton commented 9 years ago

I finally put the elbow grease into making this happen. It's still largely untested, but per business-as-usual in Haskell, once it compiled, it seemed to work. You can see for yourself that the traversals generated in examples/traversal-test.lhs are more general now:

$ cabal build
$ ghci examples/traversal-test.lhs
ghci> :t over _B
over _B :: (b -> b') -> Opt b c d -> Opt b' c d
ghci> over _B show (B ())
B "()"

Kindly test this out a little when you can. Meanwhile, I'll review, clean up the code a bit, and maybe even add a test suite. Once I hear back from you ( @Gabriel439 ) I'll release the new version.

Gabriella439 commented 9 years ago

I just gave it a test drive and it works great! I tried it out with my total library and also tried exercising a lot of corner cases and it behaves just the way I expect.

DanBurton commented 9 years ago

Uploaded the new version: 0.4.1.0. Cheers!