You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A few days ago, I had the chance to speak with @djspiewak about the law testing we perform in cats, and our conversation became about the style in which laws are written.
A good number of our laws at the moment are written in this style:
defantiSymmetryEq(x: A, y: A, f: A=>A):IsEq[Boolean] =
(!E.eqv(x, y) ||E.eqv(f(x), f(y))) <->true
which adheres to this simple structure:
defrule:IsEq[Boolean] = (!a) || b <->true
This style is not particularly loved by Daniel (nor me) as it's a bit hermetic and doesn't let you reason in terms of "rewrite equivalences", and for this reason, I began writing a PR that refactors these laws.
While writing though I realized that a law like
defrule:IsEq[Boolean] = (!a) || b <->true
can't generally be rewritten in a more readable way such as
defrule:IsEq[Boolean] = a <-> b
since a <-> b expresses a co-implication while (!a) || b <-> true expresses a mere implication a --> b
To demonstrate this it's enough to compare the truth table of !a || b and the one of a => b (the co-implication truth table is added too)
a
b
!a
!a or b
a => b
a <=> b
T
T
F
T
T
T
T
F
F
F
F
F
F
T
T
T
T
F
F
F
T
T
T
T
This made me realize that there are cases in our law tests where the only thing we can always prove is an implication between two expressions, but not vice-versa: if you consider the example I pasted earlier:
defantiSymmetryEq(x: A, y: A, f: A=>A):IsEq[Boolean] =
(!E.eqv(x, y) ||E.eqv(f(x), f(y))) <->true
it can be easily noticed that this should be an implication since the equality of the arguments ALWAYS implies that f(x) == f(y), but the equality of the function's value in two points doesn't imply that the points coincide.
I personally found that expressing this kind of relationship between expressions is important, and that is important to express it in an easy and readable way.
For this reason, I'm here to propose adding another operator alongside the well-known <->, which expresses an implication and that will let us rewrite the previous law as
defantiSymmetryEq(x: A, y: A, f: A=>A):IsEq[Boolean] =E.eqv(x, y) -->E.eqv(f(x), f(y))
Making it more readable and easy to express (again, IMHO).
Another beneficial side effect of introducing this operator is that we can clearly express weaker and stronger laws in a clear and distinguishable way.
This refers to the fact that I found a bunch of laws written in the weak form !(a) || b <-> true (that were probably copy-pasted from surrounding laws) that can be rewritten in the stronger form a <-> b, making them more meaningful and valuable as they will demonstrate (or better, verify a gazillion of times) even the b => a implication (think for example E.eqv(x, y) <-> E.eqv(bijective_f(x), bijective_f(y)))
Last but not least, --> can be leveraged to rewrite complicated laws like
(ofc, we'll need to introduce a && combinator for expressions built with -->)
I would love to know what you think about it. I personally believe that this can add value to Discipline, and that's the reason I'm proposing it.
I haven't talked about any implementation detail as the modification seems easy to me (I obviously have a branch in my fork with a POC), and as I wanted to focus more on the content itself.
Also, --> is just an idea. I'm prepared to 🚲-shed about it 😇
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
TLDR: can we add
-->
to Discipline?A few days ago, I had the chance to speak with @djspiewak about the law testing we perform in cats, and our conversation became about the style in which laws are written.
A good number of our laws at the moment are written in this style:
which adheres to this simple structure:
This style is not particularly loved by Daniel (nor me) as it's a bit hermetic and doesn't let you reason in terms of "rewrite equivalences", and for this reason, I began writing a PR that refactors these laws.
While writing though I realized that a law like
can't generally be rewritten in a more readable way such as
since
a <-> b
expresses a co-implication while(!a) || b <-> true
expresses a mere implicationa --> b
To demonstrate this it's enough to compare the truth table of
!a || b
and the one ofa => b
(the co-implication truth table is added too)a
b
!a
!a or b
a => b
a <=> b
This made me realize that there are cases in our law tests where the only thing we can always prove is an implication between two expressions, but not vice-versa: if you consider the example I pasted earlier:
it can be easily noticed that this should be an implication since the equality of the arguments ALWAYS implies that
f(x) == f(y)
, but the equality of the function's value in two points doesn't imply that the points coincide.I personally found that expressing this kind of relationship between expressions is important, and that is important to express it in an easy and readable way.
For this reason, I'm here to propose adding another operator alongside the well-known
<->
, which expresses an implication and that will let us rewrite the previous law asMaking it more readable and easy to express (again, IMHO).
Another beneficial side effect of introducing this operator is that we can clearly express weaker and stronger laws in a clear and distinguishable way.
This refers to the fact that I found a bunch of laws written in the weak form
!(a) || b <-> true
(that were probably copy-pasted from surrounding laws) that can be rewritten in the stronger forma <-> b
, making them more meaningful and valuable as they will demonstrate (or better, verify a gazillion of times) even theb => a
implication (think for exampleE.eqv(x, y) <-> E.eqv(bijective_f(x), bijective_f(y))
)Last but not least,
-->
can be leveraged to rewrite complicated laws likein a simpler way
(ofc, we'll need to introduce a
&&
combinator for expressions built with-->
)I would love to know what you think about it. I personally believe that this can add value to Discipline, and that's the reason I'm proposing it.
I haven't talked about any implementation detail as the modification seems easy to me (I obviously have a branch in my fork with a POC), and as I wanted to focus more on the content itself.
Also,
-->
is just an idea. I'm prepared to 🚲-shed about it 😇Beta Was this translation helpful? Give feedback.
All reactions