ekmett / profunctors

Haskell 98 Profunctors
http://hackage.haskell.org/package/profunctors
Other
70 stars 43 forks source link

Consider adding a methods to Strong and Choice #47

Open treeowl opened 7 years ago

treeowl commented 7 years ago

I was reading about profunctor-based lenses (specifically, http://oleg.fi/gists/posts/2017-03-31-compiling-lenses.html) and it pointed out that there's a method that could be added to Strong:

lensE :: (s -> a) -> (s -> b -> t) -> p a b -> p s t
lensE getter setter = dimap (id &&& getter) (uncurry setter) . second'

Indeed, I realized (and I'm sure I'm not the first to do so) that this one method offers defaults for dimap, first', and second':

dimapDefaultS :: Strong p
              => (s -> a)
              -> (b -> t)
              -> p a b -> p s t
dimapDefaultS f g = lensE f (const g)

firstDefault :: Strong p => p a b -> p (a, c) (b, c)
firstDefault = lensE fst (\(_, c) b -> (b, c))

secondDefault :: Strong p => p a b -> p (c, a) (c, b)
secondDefault = lensE snd (\(c, _) b -> (c, b))

Since tuples can be a bit expensive, I wonder if adding a method like this could be helpful.


Dually, Choice supports an all-powerful method:

prismE :: (b -> t) -> (s -> Either t a) -> p a b -> p s t
prismE constr pat = dimap pat (either id constr) . right'

leftDefault :: Choice p => p a b -> p (Either a c) (Either b c)
leftDefault = prismE Left (either Right (Left . Right))

rightDefault :: Choice p => p a b -> p (Either c a) (Either c b)
rightDefault = prismE Right (either (Left . Left) Right)

dimapDefaultC :: Choice p
              => (s -> a)
              -> (b -> t)
              -> p a b -> p s t
dimapDefaultC f g = prismE g (Right . f)
treeowl commented 7 years ago

Another option, that feels like it may possibly fit better with lens (rather than a profunctor lens library) is

lensical :: Functor f => (s -> a) -> (s -> b -> t) -> p a (f b) -> p s (f t)

But I have no sense of whether this generalization is useful.

treeowl commented 7 years ago

Such methods also add corresponding alternatives to Pastro. Simplest:

data Pasta p s t where
  Pasta :: (s -> a) -> (s -> b -> t) -> p a b -> Pasta p s t

instance Strong (Pasta p) where
  lensE getter setter (Pasta sa sbt p) =
    Pasta (sa . getter) (\s b1 -> setter s (sbt (getter s) b1)) p

pastaToPastro :: Pasta p s t -> Pastro p s t
pastaToPastro (Pasta sa sbt p) =
  Pastro (\(b,bt) -> bt b) p (\s -> (sa s, sbt s))

pastroToPasta :: Pastro p s t -> Pasta p s t
pastroToPasta (Pastro f p g) =
  Pasta (fst . g) (\s y -> f (y, snd (g s))) p