I just wrote a long post in the thread related to the earlier blog posts on Chalk about a related topic, so I’ll keep this one shorter.
I have been working in this area for several years, and the problem you encountered with ‘maybe’ outcomes seems to be handled by “deferred goals” which is one technique used in Prolog engines to implement constraints like disequality (and even inequalities on number “kinds”).
A quick summary would be that disequalities in types/logic are easy to deal with by deferring the goal (and the output can be the unsolved deferred goals in the case of a ‘maybe’ that can be pretty printed as disequalities or inequalities), but negation is tough. I have not found any of the simple negations to be sound (failure, inconsistency etc.) and right now the only sound option appears to be to rewrite the clauses into disequalities, which is easy for simple cases “not(X = Y)” becomes “X /= Y”, but harder for the general case including recursion.
Regarding specialisation, most specific match can lead to instability (new code changing existing behaviours), although i think it is a viable approach, i prefer requiring coherence (no overlapping), but using the disequality constraint to disambiguate. For example the following is problematic:
(a).
(X).
But this is not:
(a).
(X), X /= a.
This requires that typeclass dependencies take part in matching (IE the bounds of a trait implementation must be satisfied to choose it). This must be resolved recursively, and requires backtracking to solve. I don’t know if i am the first to say this, but i have said it before on LtU, “any sufficiently complex type system ends up re-implementing a Prolog-like logic language badly” 