John Hughes in his "Generalising Monads to Arrows" writes (chapter 8):
We formalise the property that
first fdepends only on first components of pairs as follows:first f >>> arr fst = arr fst >>> f
I understand that the law filters out implementations of such sort:
newtype KleisliMaybe a b = KMb { runKMb :: a -> Maybe b }
instance Category KleisliMaybe where
...
instance Arrow KleisliMaybe where
first f = KMb $ const Nothing
...
But the wording seems a bit odd for this case (I would have chosen "first has no side-effects" or something like that for such an instance).
So, what are other reasons to keep it around?
Moreover, there is another law: first f >>> second (arr g) = second (arr g) >>> first f. I did not find any implementations it filters out (I did - see the edit). How does this law help us?
Edit: more thoughts on the latter law.
Take a look at the following snippet:
newtype KleisliWriter = KW { runKW :: a -> (String, b) }
instance Category KleisliWriter where
...
instance Arrow KleisliWriter where
arr f = KW $ \ x -> ("", f x)
first (KW f) = KW $ \ ~(a, d) -> f a >>= (\ x -> ("A", (x, d)))
second (KW f) = KW $ \ ~(d, b) -> f b >>= (\ x -> ("B", (d, x)))
Such an instance behaves this way:
GHCi> c = KW $ \ x -> ("C", x)
GHCi> fst . runKW (first c >>> second (arr id)) $ (1, 2)
"CAB"
GHCi> fst . runKW (second (arr id) >>> first c) $ (1, 2)
"BCA"
As far as I get it, we have got no law for second f = swap >>> first f >>> swap. Therefore, we can forbid both second and first to have any side-effects with this law. Yet, the original wording still does not seem to hint at that again:
...we formalise the intuition that the second component of the pair is unaffected by
first fas a law...
Are those laws just pure formalisations for solid proofs?
Short answer: There's a different pair of laws that cover "
firstandsecondhave no side-effects":After thinking about it, I THINK that both laws you've identified:
are, in fact, redundant because they follow from those no-side-effects laws, the other laws, and a couple of "free theorems".
Your counterexamples violate the no-side-effects laws, so that's why they also violate LAW-A and/or LAW-B. If someone has a true counterexample that obeys the no-side-effects laws but violates LAW-A or LAW-B, I'd be very interested in seeing it.
Long answer:
The property "
firsthas no side effects (of its own, at least)" is better formalized by the law stated earlier in Section 8 of that article:Recall that Hughes says an arrow is "pure" (equivalently, "has no side-effects") if it can be written
arr expr. So, this law states that, given any computation that is already pure and so can be writtenarr f, applyingfirstto that computation also results in a pure computation (because it is of the formarr exprwithexpr = first f). Therefore,firstintroduces no impurities / no effects of its own.The other two laws:
are intended to capture the idea that for a particular
instance Arrow Fooand a particular arrow actionf :: Foo B C, the action:acts on the first components of its input/output pairs as if the second components weren't there. The laws correspond to the properties:
Cand any side effects depend only on inputB, not inputd(i.e., no dependence ond)dpasses through unchanged, unaffected by the inputBor any side effects (i.e., no effect ond)With respect to LAW-A, if we consider the action
first f :: Foo (B,d) (C,d)and focus on theCcomponent of its output using a pure function to extract it:then the result is the same as if we first forcibly remove the second component using a pure action:
and allow the original action
fto act only onB:Here, the structure of the
first f >>> arr fstaction leaves open the possibility thatfirst fcan depend on thedcomponent of the input in formulating its side effects and constructing theCcomponent of its output; but, the structure of thearr fst >>> faction eliminates this possibility by removing thedcomponent via a pure action before allowing any non-trivial computation byf. The fact that these two actions are equal (the law) makes it clear thatfirst fproduces aCoutput (and side effects, throughf, sincefirsthas no additional effects of its own) from theBinput in a manner that can't also depend on thedinput.LAW-B is is harder. The most obvious way of formalizing this property would be the pseudolaw:
which directly states that
first fdoesn't change the extracted (arr snd) second component. However, Hughes points out that this is too restrictive, because it doesn't allowfirst fto have side effects (or at least any that can survive the pure actionarr snd). Instead, he provides the more complicated law:The idea here is that, if
first fever modified thedvalue, then there would be some case where the following two actions would be different:But, because of LAW-B, we have:
and so the actions are the same, contrary to our assumption.
HOWEVER, I conjecture that LAW-A and LAW-B are both redundant, because I believe (see my hesitation below) they follow from the other laws plus a "free theorem" for the signature:
Assuming
firstandsecondsatisfy the no-side-effects laws:then LAW-B can be rewritten as:
and this last statement is just the free theorem for
first f. (Intuitively, sincefirst fis polymorphic in the type ofd, any pure action ondis necessarily "invisible" tofirst f, sofirst fand any such action will commute.) Similarly, there's a free theorem for:that captures the idea that, since this signature is polymorphic in
d, no pure pre-action ondcan affect the action:But the left-hand side can be rewritten:
giving the right-hand side.
I only hesitate here because I'm not used to thinking about "free theorems" for possibly effectful arrows instead of functions, and so I'm not 100% sure that it goes through.
I'd be very interested to see if someone can come up with true counterexamples for these laws that violate LAW-A or LAW-B but satisfy the no-side-effects laws. The reason your counterexamples violate LAW-A and LAW-B is that they violate the no-side-effects laws. For your first example:
and for your second: