diff --git a/qutil/domains.py b/qutil/domains.py
index f72f9eb438e6184d5779c229f10b94847c2fb6b6..1467714caf126465e05f9c5b28ce4790e7faff32 100644
--- a/qutil/domains.py
+++ b/qutil/domains.py
@@ -183,7 +183,31 @@ class Interval(Domain[_RealT], Bounded[_RealT], ABC):
 
 @dataclass
 class DiscreteInterval(Interval[_IntegralT], Bounded[_IntegralT]):
-    """A discrete interval that only allows integral values."""
+    """A discrete interval that only allows integral values.
+
+    Examples
+    --------
+    >>> x = 7
+    >>> domain = DiscreteInterval(lower=-4.3, upper={5, lambda: x})
+    >>> domain
+    DiscreteInterval(lower=-4, upper=5)
+    >>> 4 in domain
+    True
+    >>> 4.0 in domain  # only ints
+    False
+    >>> domain.next_closest(1.7)
+    2
+    >>> domain.next_smallest(1.7)
+    1
+
+    Bounds can be dynamic and are lazily evaluated:
+
+    >>> x = 2
+    >>> 4 in domain
+    False
+    >>> domain
+    DiscreteInterval(lower=-4, upper=2)
+    """
     lower: _BoundT[_IntegralT] = Bound[_IntegralT](default=-inf, integral=True)
     upper: _BoundT[_IntegralT] = Bound[_IntegralT](default=+inf, integral=True)
 
@@ -217,7 +241,25 @@ class DiscreteInterval(Interval[_IntegralT], Bounded[_IntegralT]):
 
 
 class ContinuousInterval(Interval[_RealT]):
-    """A continuous interval that allows any value within its bounds."""
+    """A continuous interval that allows any value within its bounds.
+
+    Examples
+    --------
+    >>> domain = ContinuousInterval(-3.1, 1.7)
+    >>> 0 in domain
+    True
+    >>> 1.7 in domain
+    True
+    >>> 10 in domain
+    False
+    >>> -10 < domain
+    True
+    >>> domain.next_smallest(0.91)
+    0.91
+    >>> domain.next_largest(6)
+    1.7
+
+    """
 
     def next_closest(self, value: _RealT) -> _RealT:
         if value < self:
@@ -234,8 +276,27 @@ class ContinuousInterval(Interval[_RealT]):
 
 
 @dataclass
-    """A rational interval that only allows integral denominators."""
 class ReciprocalDiscreteInterval(DiscreteInterval[_IntegralT], Reciprocal[_RealT]):
+    """A rational interval that only allows integral denominators.
+
+    Examples
+    --------
+    The interval [2.3/10, 2.3/9, ..., 2.3/1]:
+    >>> domain = ReciprocalDiscreteInterval(lower=1, upper=10, numerator=2.3)
+    >>> 2.3/8 in domain
+    True
+    >>> 2.4/8 in domain
+    False
+    >>> 2.3/11 < domain
+    True
+    >>> domain.next_smallest(0.3)  # 2.3/8
+    0.2875
+    >>> domain.next_largest(0.3)  # 2.3/7
+    0.32857142857142857
+    >>> domain.next_closest(0.3)  # 2.3/8
+    0.2875
+
+    """
     lower: _BoundT[_IntegralT] = ReciprocalBound[_IntegralT](default=1, integral=True)
     upper: _BoundT[_IntegralT] = ReciprocalBound[_IntegralT](default=inf, integral=True)
     numerator: _RealT = 1.0
@@ -296,7 +357,23 @@ class ReciprocalDiscreteInterval(DiscreteInterval[_IntegralT], Reciprocal[_RealT
 
 @dataclass
 class BoundedSet(frozenset, Domain[_RealT], Bounded[_RealT]):
-    """A finite set of numbers with lower and upper bounds."""
+    """A finite set of numbers with lower and upper bounds.
+
+    Examples
+    --------
+    >>> domain = BoundedSet([1, 1.0, 3, -2.3, 7], lower=-4, upper=5)
+    >>> domain  # behaves like frozenset
+    BoundedSet({1, 3, -2.3, 7}, lower=-4, upper=5)
+    >>> 3 in domain
+    True
+    >>> -1.7 in domain
+    False
+    >>> 7 in domain  # out of bounds
+    False
+    >>> domain.next_largest(2.7)
+    3
+
+    """
     iterable: InitVar[Iterable[_RealT]] = ...
     lower: _BoundT[_RealT] = Bound[_RealT](default=-inf, integral=False)
     upper: _BoundT[_RealT] = Bound[_RealT](default=+inf, integral=False)