Computations that Never Fail #
This file defines a predicate-as-typeclass stating that a probabilistic computation never produces failure mass, together with lemmas for how the property behaves under common monadic combinators.
Given a HasEvalSPMF m instance and a computation mx : m α in that monad, NeverFail mx means
that Pr[⊥ | mx] = 0, i.e. that the computation never fails.
Defined as a typeclass to allow it to be synthesized automatically in certain cases.
However we don't include any instances for bind as this blows up the search space.
Instances involving bind should be added manually as needed.
The existence of a HasEvalPMF m instance implies that NeverFail mx holds for any computaiton
in the monad, since the PMF doesn't allow any probability of failing.
NeverFail mx states that the computation mx : m α has zero probability of failure,
equivalently the mass of (evalDist mx) at none is 0.
Formally: NeverFail mx iff Pr[⊥ | mx] = 0.
Remarks:
- This class is a predicate (Prop-valued). It does not add data.
- Use the lemmas in this file (e.g.
bind_of_mem_support) to transport the property through monadic structure. We intentionally avoid abindinstance, as the natural condition depends on the support of the left-hand side.
Instances
Version of probFailure_eq_zero that avoids typeclass search.
A computation in a monad with HasEvalPMF can't fail as outputs sum to probability 1.
If mx is a pure return, it never fails.
This follows since evalDist (pure x) is the Dirac distribution on some x.
Precise bind lemma: if mx never fails and for all x in the support of mx the continuation
my x never fails, then the whole bind never fails.
Sketch: using evalDist_bind and the identity
Pr[⊥ | mx >>= my] = Pr[⊥ | mx] + ∑ x, Pr[= x | mx] * Pr[⊥ | my x],
the first term vanishes by NeverFail mx, while for x ∉ support mx the coefficient
Pr[= x | mx] is 0, and for x ∈ support mx the second factor vanishes by hypothesis.
Hence the sum is 0.
Weak bind lemma: if the right-hand side never fails for every possible input (∀ x),
then the bind never fails.
This is a convenience corollary of bind_of_mem_support; it is often easy to apply when
my is uniform in its input (e.g. ignores it) or is known to be never-failing globally.
Mapping a value through a total function preserves NeverFail.
If both the function computation and the argument computation never fail, then their applicative sequencing also never fails.
If mx and my never fail, then mx <* my never fails.
If mx and my never fail, then mx *> my never fails.