-- 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.2.0.0 -- | 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. 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 UnlessPhantom for an example of this -- use-case. type UnlessStuck expr c = IfStuck expr NoError c -- | 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: -- --
--   >>> :{
--   foo :: SubstVar VAR Bool
--   foo = True
--   :}
--   ...
--   ... Couldn't match expected type ...SubstVar VAR Bool...
--   ... with actual type ...Bool...
--   ...
--   
-- -- But running it via te makes everything work: -- --
--   >>> :{
--   foo :: $(te[t| SubstVar VAR Bool |])
--   foo = True
--   :}
--   
-- -- If you don't want to think about when to use te, it's a no-op -- when used with everyday types: -- --
--   >>> :{
--   bar :: $(te[t| Bool |])
--   bar = True
--   :}
--   
te :: Q Type -> Q Type -- | This type family is always stuck. It must be used in the context of -- te. -- -- A meta-variable for marking which argument should be a phantom when -- working with UnlessPhantom. -- -- PHANTOM is polykinded and can be used in several settings. -- -- See UnlessPhantom for examples. type family PHANTOM :: k -- | 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. -- -- For example, consider the definition: -- --
--   >>> :{
--   data Qux a b = Qux b
--   :}
--   
-- -- which is phantom in a: -- --
--   >>> :eval_error $(te[t| UnlessPhantom (Qux PHANTOM Int) ('Text "Ok") |])
--   ()
--   
-- -- but not in b: -- --
--   >>> :eval_error $(te[t| UnlessPhantom (Qux Int PHANTOM) ('Text "Bad!") |])
--   ...
--   ... Bad!
--   ...
--   
-- -- 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: -- --
--   >>> :{
--   data NotPhantomErrorFcf :: k -> Exp Constraint
--   type instance Eval (NotPhantomErrorFcf f) =
--     $(te[t| UnlessPhantom (f PHANTOM)
--                           ( ShowTypeQuoted f
--                      ':<>: 'Text " is not phantom in its argument!")
--           |])
--   :}
--   
-- --
--   >>> :{
--   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: -- --
--   >>> observe_phantom Proxy
--   ()
--   
-- --
--   >>> 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!" -- --
--   >>> observe_phantom
--   ...
--   ... No instance for (Show (f0 p0 -> ()))
--   ...
--   
type family UnlessPhantom :: k -> ErrorMessage -> Constraint -- | This type family is always stuck. It must be used in the context of -- te. -- -- Subst expr a b substitutes all instances of a -- for b in expr. -- --
--   >>> :kind! $(te[t| Subst (Either Int Int) Int Bool |])
--   ...
--   = Either Bool Bool
--   
-- --
--   >>> :kind! $(te[t| Subst (Either Int Bool) Int [Char] |])
--   ...
--   = Either [Char] Bool
--   
-- --
--   >>> :kind! $(te[t| Subst (Either Int Bool) Either (,) |])
--   ...
--   = (Int, Bool)
--   
type family Subst :: k1 -> k1 -> k2 -> k2 -- | This type family is always stuck. It must be used in the context of -- te. -- -- 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 family VAR :: k -- | This type family is always stuck. It must be used in the context of -- te. -- -- Like Subst, but uses the explicit meta-variable VAR to -- mark substitution points. -- --
--   >>> :kind! $(te[t| SubstVar (Either VAR VAR) Bool |])
--   ...
--   = Either Bool Bool
--   
-- --
--   >>> :kind! $(te[t| SubstVar (Either VAR Bool) [Char] |])
--   ...
--   = Either [Char] Bool
--   
-- --
--   >>> :kind! $(te[t| SubstVar (VAR Int Bool) (,) |])
--   ...
--   = (Int, Bool)
--   
type family SubstVar :: k1 -> k2 -> k2 -- | 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