impl-0.2.0.0: Impl.hs
{-# OPTIONS_HADDOCK show-extensions,not-home #-}
-- | Impl is intended to be used as an alternative to the normal default typeclass methods machinery of Haskell.
-- In contrast with @intrinsic-superclasses@, we must specify each link of the implementation heirarchy with an instance of Impl, rather than infer it from the superclass heirarchy.
-- The benefit of this more explicit style is complete control over default methods provided by subclasses, at the cost of some automation for the class creator.
-- Impl is most valuable when instantiating deep (or even undecidably recursive) typeclass hierarchies for multiple new datatypes, which is most common in client code.
module Impl (
-- * The core Impl class
Impl(..),Method(..)
,Symbol -- | Reexported from @base@
,NamedMethods, NamedExpQ
,type (:->)
-- * Utilities for Named arguments
-- | @impl@ uses "Named" arguments, which work best with @OverloadedLabels@
,type (:!) -- | A required named argument.
--
-- >>> #foo 'a' :: "foo" :! Char
,type (:?) -- | An optional named argument
--
-- >>> #foo 'b' :: "foo" :? Char
,($$)
,defaults -- | A special 'Param' to fill in the remaining 'Optional' arguments with 'Nothing'
--
-- @foo :: ("bar" :! String) -> ("baz" :? Char) -> ("quox" :? Int) -> IO ()@
--
-- >>> foo $$ #bar "Hello" $$ defaults :: IO ()
,arg {- |
'arg' unwraps a named parameter with the specified name. One way to use it is
to match on arguments with @-XViewPatterns@:
@
fn (arg \#t -> t) (arg \#f -> f) = ...
@
This way, the names of parameters can be inferred from the patterns: no type
signature for @fn@ is required. In case a type signature for @fn@ is
provided, the parameters must come in the same order:
@
fn :: "t" :! Integer -> "f" :! Integer -> ...
fn (arg \#t -> t) (arg \#f -> f) = ... -- ok
fn (arg \#f -> f) (arg \#t -> t) = ... -- does not typecheck
@
-}
, arg'
,Param -- | A parameter passable as a named argument. Used implicitly by '($$)' with @OverloadedLabels
,NamedF(Arg,Arg') -- | A named argument that could be required or optional depending on the @f@ parameter
-- * TH reexports and utilities
,methodsFor, type (++), TypeQ, DecsQ
) where
import Language.Haskell.TH hiding (Name)
import Named
import Named.Internal (Param,NamedF(ArgF))
import GHC.TypeLits (Symbol)
import Impl.Utils
-- | Typeclasses implementing Impl can build declaratios for their entire superclass heirarchy
-- from a collection of required or optional named methods, allowing potentially complex logic for defaulting.
-- See the @example@ internal library for how to implement instances of 'Impl'.
class Impl c where
type Methods c :: [Method Symbol]
-- | Instantiate the implementing class along with all its superclasses
-- Ex:
--
-- > impl @Monad [t|[]|]
-- > $$ #return [|\x -> [x]|]
-- > $$ #bind [|flip concatMap|]
impl :: TypeQ -> NamedMethods c :-> DecsQ
-- | >>> :kind! NamedExpQ '[Required "foo", Optional "bar"]
-- = '["foo" :! ExpQ,"bar" :? ExpQ]
type family NamedExpQ ss where
NamedExpQ '[] = '[]
NamedExpQ (Required s ': ss) = (s :! ExpQ) ': NamedExpQ ss
NamedExpQ (Optional s ': ss) = (s :? ExpQ) ': NamedExpQ ss
-- | "Named" TH 'Exp's for the class method implementations
type NamedMethods c = NamedExpQ (Methods c)
arg' :: Name name -> a -> (name :? a) -> a
{- | A variation of 'arg' for optional arguments. Requires a default value to handle
the case when the optional argument was omitted:
@ fn (arg' \#answer 42 -> ans) = ... @
In case you want to get a value wrapped in 'Maybe' instead, 'Arg'' -}
arg' = argDef
pattern Arg' :: Maybe a -> name :? a
-- | Construct or match an optional named argument
pattern Arg' a' = ArgF a'
($$) :: WithParam p fn fn' => fn -> Param p -> fn'
{- | Pass a named (optional or required) argument to a function in any order.
@foo :: ("bar" :! String) -> ("baz" :? Char) -> IO ()@
>>> foo $$ #baz 'a' :: ("bar" :! String) -> IO ()
-}
($$) = (!)