"""Equality and inequality for types
.. autosummary::
:toctree:
Eq
eq
ne
"""
import hypothesis.strategies as st
from hypothesis import given
from .typeclass import Type
from haskpy.internal import class_function
from haskpy import testing
from haskpy.types.function import function
[docs]class Eq(Type):
"""Equality and inequality comparison
Minimal complete definition:
..
(__eq__ | __ne__) & sample_type
Minimal complete definition for type constructors:
..
(__eq_generic__ | (__eq_test__ & (__eq__ | __ne__))) & sample_eq_type
"""
[docs] def __eq__(self, other):
"""Equality comparison: ``Eq a => a -> a -> bool``
Can be used as ``==`` operator.
The default implementation uses ``__ne__``.
"""
return not self.__ne__(other)
[docs] def __ne__(self, other):
"""Inequality comparison: ``Eq a => a -> a -> bool``
Can be used as ``!=`` operator.
The default implementation uses ``__eq__``.
"""
return not self.__eq__(other)
#
# Sampling functions for property tests
#
@class_function
def sample_eq_type(cls):
# By default, assume that the type is always Eq. Subclasses should
# override this when needed, for instance, if a type from a type
# constructor is Eq only if it's type argument is Eq (e.g., Maybe)
return cls.sample_type()
#
# Test typeclass laws
#
@class_function
def assert_eq_reflexivity(cls, x):
assert (x == x) is True
return
@class_function
@given(st.data())
def test_eq_reflexivity(cls, data):
"""Test ``x == x = True``"""
a = data.draw(cls.sample_eq_type())
x = data.draw(a)
cls.assert_eq_reflexivity(x)
return
@class_function
def assert_eq_symmetry(cls, x, y):
assert (x == y) == (y == x)
return
@class_function
@given(st.data())
def test_eq_symmetry(cls, data):
"""Test ``x == y = y == x``"""
a = data.draw(cls.sample_eq_type())
x = data.draw(a)
y = data.draw(a)
cls.assert_eq_symmetry(x, y)
return
@class_function
def assert_eq_transitivity(cls, x, y, z):
cond = (x == y) and (y == z)
then = (x == z)
assert (cond and then) or (not cond)
return
@class_function
@given(st.data())
def test_eq_transitivity(cls, data):
"""Test if ``x == y && y == z = True``, then ``x == z = True``"""
a = data.draw(cls.sample_eq_type())
x = data.draw(a)
y = data.draw(a)
z = data.draw(a)
cls.assert_eq_transitivity(x, y, z)
return
@class_function
def assert_eq_substitutivity(cls, x, y, f):
cond = (x == y)
then = (f(x) == f(y))
assert (cond and then) or (not cond)
return
@class_function
@given(st.data())
def test_eq_substitutivity(cls, data):
"""Test if ``x == y = True``, then ``f(x) == f(y) = True``"""
# Draw types
a = data.draw(cls.sample_eq_type())
b = data.draw(testing.sample_eq_type())
# Draw values
x = data.draw(a)
y = data.draw(a)
f = data.draw(testing.sample_function(b))
# Note: the only requirement for arbitrary functions is that the input
# variable has __eq__ implemented. And we have that for Eq type so this
# test can always be run.
cls.assert_eq_substitutivity(x, y, f)
return
@class_function
def assert_eq_negation(cls, x, y):
neq = (x != y)
eq = (x == y)
assert (neq == (not eq))
return
@class_function
@given(st.data())
def test_eq_negation(cls, data):
"""Test ``x != y = not (x == y)``"""
a = data.draw(cls.sample_eq_type())
x = data.draw(a)
y = data.draw(a)
cls.assert_eq_negation(x, y)
return
#
# Test default implementations
#
@class_function
def assert_eq_eq(cls, x, y):
assert (x == y) == eq(x, y)
assert (x == y) == cls.__eq__(x, y)
return
@class_function
@given(st.data())
def test_eq_eq(cls, data):
a = data.draw(cls.sample_eq_type())
x = data.draw(a)
y = data.draw(a)
cls.assert_eq_eq(x, y)
return
@class_function
def assert_eq_ne(cls, x, y):
from haskpy.functions import ne
assert (x != y) == ne(x, y)
assert (x != y) == cls.__ne__(x, y)
return
@class_function
@given(st.data())
def test_eq_ne(cls, data):
a = data.draw(cls.sample_eq_type())
x = data.draw(a)
y = data.draw(a)
cls.assert_eq_eq(x, y)
return
[docs]@function
def eq(x, y):
"""Equality: ``Eq a => a -> a -> Bool``
Note that one can use `==` operator instead of this function. But operators
cannot be partially applied in Python, so for that usecase this function
can be useful.
.. code-block:: python
>>> from haskpy import List, map
>>> map(eq(42), List(1, 2, 42, 666, 42)
List(False, False, True, False, True)
"""
return x == y
[docs]@function
def ne(x, y):
"""Inequality: ``Eq a => a -> a -> Bool``"""
return x != y