-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Tools for writing better type errors -- -- Please see the README on GitHub at -- https://github.com/isovector/type-errors#readme @package type-errors @version 0.1.0.0 module Type.Errors -- | A description of a custom type error. data ErrorMessage -- | Show the text as is. [Text] :: () => Symbol -> ErrorMessage -- | Pretty print the type. ShowType :: k -> ErrorMessage [ShowType] :: forall t. () => t -> ErrorMessage -- | Put two pieces of error message next to each other. [:<>:] :: () => ErrorMessage -> ErrorMessage -> ErrorMessage -- | Stack two pieces of error message on top of each other. [:$$:] :: () => ErrorMessage -> ErrorMessage -> ErrorMessage infixl 6 :<>: infixl 5 :$$: -- | Pretty print a list. -- --
--   >>> :show_error PrettyPrintList '[Bool]
--   ...
--   ... 'Bool'
--   ...
--   
-- --
--   >>> :show_error PrettyPrintList '[1, 2]
--   ...
--   ... '1', and '2'
--   ...
--   
-- --
--   >>> :show_error PrettyPrintList '["hello", "world", "cool"]
--   ...
--   ... "hello", "world", and "cool"
--   ...
--   
type family PrettyPrintList (vs :: [k]) :: ErrorMessage -- | Like ShowType, but quotes the type if necessary. -- --
--   >>> :show_error ShowTypeQuoted Int
--   ...
--   ... 'Int'
--   ...
--   
-- --
--   >>> :show_error ShowTypeQuoted "symbols aren't quoted"
--   ...
--   ... "symbols aren't quoted"
--   ...
--   
type family ShowTypeQuoted (t :: k) :: ErrorMessage -- | The type-level equivalent of error. -- -- 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.")
--   
type family TypeError (a :: ErrorMessage) :: b -- | 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. -- --
--   >>> :{
--   foo :: TypeError ('Text "Too eager; prevents compilation") => ()
--   foo = ()
--   :}
--   ...
--   ... Too eager; prevents compilation
--   ...
--   
-- --
--   >>> :{
--   foo :: DelayError ('Text "Lazy; emitted on use") => ()
--   foo = ()
--   :}
--   
-- --
--   >>> foo
--   ...
--   ... Lazy; emitted on use
--   ...
--   
type DelayError err = Eval (DelayErrorFcf err) -- | Like DelayError, but implemented as a first-class family. -- DelayErrorFcf is useful when used as the last argument to -- IfStuck and UnlessStuck. data DelayErrorFcf :: ErrorMessage -> Exp k -- | 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. type NoError = (() :: Constraint) -- | Like NoError, but implemented as a first-class family. -- NoErrorFcf is useful when used as the last argument to -- IfStuck and UnlessStuck. type NoErrorFcf = Pure NoError -- | 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. -- -- This is a generalization of kcsongor's Break machinery -- described in detecting the undetectable. type family IfStuck (expr :: k) (b :: k1) (c :: Exp k1) :: k1 -- | Like IfStuck, but specialized to the case when you don't want -- to do anything if expr isn't stuck. -- --
--   >>> :{
--   observe_no_rep
--       :: WhenStuck
--            (Rep t)
--            (DelayError ('Text "No Rep instance for " ':<>: ShowTypeQuoted t))
--       => t
--       -> ()
--   observe_no_rep _ = ()
--   :}
--   
-- --
--   >>> observe_no_rep HasNoRep
--   ...
--   ... No Rep instance for 'HasNoRep'
--   ...
--   
-- --
--   >>> observe_no_rep True
--   ()
--   
type WhenStuck expr b = IfStuck expr b NoErrorFcf -- | Like IfStuck, but leaves no residual constraint when -- expr is stuck. This can be used to ensure an expression -- isn't stuck before analyzing it further. -- -- See the example under UnlessPhantomFcf for an example of this -- use-case. type UnlessStuck expr c = IfStuck expr NoError c -- | A meta-variable for marking which argument should be a phantom when -- working with UnlessPhantom and UnlessPhantomFcf. -- -- PHANTOM is polykinded and can be used in several settings. -- -- See UnlessPhantom for examples. type PHANTOM = VAR -- | 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. -- -- For example, consider the definition: -- --
--   >>> :{
--   data Qux a b = Qux b
--   :}
--   
-- -- which is phantom in a: -- --
--   >>> :eval_error UnlessPhantom (Qux PHANTOM Int) ('Text "Ok")
--   ()
--   
-- -- but not in b: -- --
--   >>> :eval_error UnlessPhantom (Qux Int PHANTOM) ('Text "Bad!")
--   ...
--   ... Bad!
--   ...
--   
-- -- Unfortunately there is no known way to emit an error message if the -- variable is a phantom. type UnlessPhantom exp err = Eval (UnlessPhantomFcf exp err) -- | Like UnlessPhantom, but implemented as a first-class family. As -- such, this allows UnlessPhantomFcf to be chained after -- IfStuck to only be run if an expression isn't stuck. -- -- This can be used to avoid emitting false error messages when a type -- variable isn't phantom, just ambiguous. -- --
--   >>> :{
--   observe_phantom
--       :: UnlessStuck
--            f
--            (UnlessPhantomFcf
--              (f PHANTOM)
--              ('Text "It's not phantom!"))
--       => f p
--       -> ()
--   observe_phantom _ = ()
--   :}
--   
-- --
--   >>> observe_phantom Proxy
--   ()
--   
-- --
--   >>> observe_phantom (Just 5)
--   ...
--   ... It's not phantom!
--   ...
--   
-- -- In the next example, we leave observe_phantom unsaturated, -- and therefore f isn't yet known. Without guarding the -- UnlessPhantomFcf behind UnlessStuck, this would -- incorrectly produce the message "It's not phantom!" -- --
--   >>> observe_phantom
--   ...
--   ... No instance for (Show ...
--   ...
--   
data UnlessPhantomFcf :: k -> ErrorMessage -> Exp Constraint -- | Subst expr a b substitutes all instances of a -- for b in expr. -- --
--   >>> :kind! Subst (Either Int Int) Int Bool
--   ...
--   = Either Bool Bool
--   
-- --
--   >>> :kind! Subst (Either Int Bool) Int [Char]
--   ...
--   = Either [Char] Bool
--   
-- --
--   >>> :kind! Subst (Either Int Bool) Either (->)
--   ...
--   = Int -> Bool
--   
type family Subst (e :: k1) (var :: k2) (sub :: k2) :: k1 -- | VAR is a meta-varaible which marks a substitution in -- SubstVar. The result of SubstVar expr val is -- expr[val/VAR]. -- -- VAR is polykinded and can be used in several settings. -- -- See SubstVar for examples. type VAR = SubMe Var -- | Like Subst, but uses the explicit meta-variable VAR to -- mark substitution points. -- --
--   >>> :kind! SubstVar (Either VAR Bool) [Char]
--   ...
--   = Either [Char] Bool
--   
-- --
--   >>> :kind! SubstVar (VAR Int Bool :: Type) (->)
--   ...
--   = Int -> Bool
--   
type family SubstVar (e :: k1) (r :: k2) :: k1 -- | Kind of type-level expressions indexed by their result type. type Exp a = a -> Type -- | Expression evaluator. type family Eval (e :: Exp a) :: a data Pure (b :: a) (c :: a) :: forall a. () => a -> a -> Type