Open masaeedu opened 3 years ago
Diagonal is called Join
in https://hackage.haskell.org/package/bifunctors-5.5.10/docs/Data-Bifunctor-Join.html
This is not "safe" composition:
{-# LANGUAGE RankNTypes #-}
import Control.Lens
setter :: Setter' Int Int
setter = id
-- setter laws are:
--
-- over s id = id
-- over s f . over s g = over s (f . g)
diagonal :: Setter x o a b -> Setter i x a b -> Setter i o a b
diagonal s1 s2 = setting $ \f -> over s1 f . over s2 f
setter2 :: Setter' Int Int
setter2 = diagonal setter setter
lhs = over setter2 (+ 1) . over setter2 (* 2) $ 1 -- = 6
rhs = over setter2 ((+ 1) . (* 2)) $ 1 -- = 7
So at best it would live in https://hackage.haskell.org/package/lens-5.0.1/docs/Control-Lens-Unsound.html
This is not a problem with Bifunctor
definition as there parametricity ensures that parts are disjoint. (So both
is valid traversal & setter). If we however take arbitrary Setter
s, that is not true anymore.
Well, not every Bifunctor
is Bitraversable
, so it seems to me you'd still want some analog of both
for simple Bifunctor
s.
That said, this scheme will work for a diagonalization of anything that forms an n-ary functor in the mathematical sense, including e.g. ones found in mono-traversable (which don't actually form Bifunctor
s proper), or over 2/3 positions of a trifunctor, etc. So whether the general form of diagonal
lives in Control.Lens.Unsound
or elsewhere I believe it would be useful to have in the library.
Do you want
Bifunctor
analogue of both
, i.e. ? :: Bifunctor p => Setter (p a a) (p b b) a b
setterParComp :: Setter x o a b -> Setter i x a b -> Setter i o a b
(it forms lawful setter when setters don't touch same bits, like diagonal id id
example I showed)I'm fine with any of the three. please make a PR, and try to get @ekmett to agree on a naming.
My idea of setterParComp
is just saying what it is, like prismSum
and lensProduct
, the name which is probably unusable for you as you wish something "usable". I'm against having operator for it, it would be obscure outlier in a consistent (though large) operator soup. If there is a word which is usable as infix operator, that might work.
I don't know what would be a good name for Bifunctor
both
. There is both1
which is Traversable1
variant.
Sometimes you have a functor from a product category:
You can equivalently represent such a functor by a pair of mapping operations:
The basic idea behind
Setter
is to generalize the type (if not the mathematical concept) offmap
. So whereas:we have:
This gives:
fmap :: forall a b. Setter (f a) (f b) a b
And similarly:
mapFirst :: forall a b x. Setter (t a x) (t b x) a b
mapSecond :: forall a b x. (t x a) (t x b) a b
So far so good. Now, sometimes, it is useful to view a
Bifunctor
as a simpleFunctor
, and use the same morphism to map both type parameters. This can be viewed as precomposing a "diagonal functor" with ourBifunctor
:Since we're working with the
lens
library, we want to forget all about classes and newtypes and work with first class values. So pretending thatmapFirst
andmapSecond
are some arbitrary setters, the basic operation that is being done to them to obtainfmap @Diagonal t
above is:We can annotate this with a partially filled out type to see what was inferred:
We can start out by filling everything in the parameters with unrelated type variables, and see what breaks:
The minimum constraints we need to get this to typecheck again are:
Unifying these variables and renaming, we have:
And we can just read off the inferred types to fill out the rest of the holes:
I'd like to add the
diagonal
combinator toControl.Lens.Setter
, but:What do you folks think?