This module provides useful tools for writing better type-errors. For
a quickstart guide to the underlying TypeError machinery,
check out Dmitrii Kovanikov's excellent blog post
A story told by Type Errors.
The polymorphic kind of this type allows it to be used in several settings.
For instance, it can be used as a constraint, e.g. to provide a better error
message for a non-existent instance,
-- in a context
instance TypeError (Text "Cannot Show functions." :$$:
Text "Perhaps there is a missing argument?")
=> Show (a -> b) where
showsPrec = error "unreachable"
It can also be placed on the right-hand side of a type-level function
to provide an error for an invalid case,
type family ByteSize x where
ByteSize Word16 = 2
ByteSize Word8 = 1
ByteSize a = TypeError (Text "The type " :<>: ShowType a :<>:
Text " is not exportable.")
Error messages produced via TypeError are often too strict, and will
be emitted sooner than you'd like. The solution is to use DelayError,
which will switch the error messages to being consumed lazily.
A helper definition that doesn't emit a type error. This is
occassionally useful to leave as the residual constraint in IfStuck when
you only want to observe if an expression isn't stuck.
IfStuck expr b c leaves b in the residual constraints whenever
expr is stuck, otherwise it Evaluates c.
Often you want to leave a DelayError in b in order to report an error
when expr is stuck.
The c parameter is a first-class family, which allows you to perform
arbitrarily-complicated type-level computations whenever expr isn't stuck.
For example, you might want to produce a typeclass Constraint here.
Alternatively, you can nest calls to IfStuck in order to do subsequent
processing.
IfStuck (_1AnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKindAnythingOfAnyKind) bc = b
This library provides tools for performing lexical substitutions over
types. For example, the function UnlessPhantom asks you to mark phantom
variables via PHANTOM.
Unfortunately, this substitution cannot reliably be performed via
type families, since it will too often get stuck. Instead we provide te,
which is capable of reasoning about types symbolically.
Any type which comes with the warning "This type family is always stuck."must be used in the context of te and the magic [t| quasiquoter. To
illustrate, the following is stuck:
Example1 expression
>>> :{foo :: SubstVar VAR Boolfoo = True:}...... Couldn't match expected type ...SubstVar VAR Bool...... with actual type ...Bool......
This type family is always stuck. It must be used in the context of te.
UnlessPhantom expr err determines if the type described by expr
is phantom in the variables marked via PHANTOM. If it's not, it produces
the error message err.
Unfortunately there is no known way to emit an error message if the variable
is a phantom.
Often you'll want to guard UnlessPhantom against IfStuck, to ensure you
don't get errors when things are merely ambiguous. You can do this by
writing your own fcf whose implementation is UnlessPhantom:
Example1 expression
>>> :{data NotPhantomErrorFcf :: k -> Exp Constrainttype instance Eval (NotPhantomErrorFcf f) = $(te[t| UnlessPhantom (f PHANTOM) ( ShowTypeQuoted f ':<>: 'Text " is not phantom in its argument!") |]):}
Example1 expression
>>> :{observe_phantom :: UnlessStuck f (NotPhantomErrorFcf f) => f p -> ()observe_phantom _ = ():}
We then notice that using observe_phantom against Proxy
doesn't produce any errors, but against Maybe does:
Example1 expression
>>> observe_phantom Proxy()
Example1 expression
>>> observe_phantom (Just 5)...... 'Maybe' is not phantom in its argument!...
Finally, we leave observe_phantom unsaturated, and therefore f isn't yet
known. Without guarding the UnlessPhantom behind UnlessStuck, this would
incorrectly produce the message "f is not phantom in its argument!"