{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE ImplicitParams #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverlappingInstances #-} {-# LANGUAGE Rank2Types #-} {-# LANGUAGE UndecidableInstances #-} {-| "Data.Implicit" provides both named and unnamed implicit parameters that support default values (given by the 'Default' class from the @data-default@ package). It makes no use of the @ImplicitParams@ extension and instead everything is done using type classes. Here is an example of unnamed implicit parameters: @ {\-\# LANGUAGE FlexibleContexts #-\} import "Data.Implicit" putParam :: 'Implicit_' String => IO () putParam = putStrLn $ \"Param was: \" ++ show ('param_' :: String) @ We define @putParam@, which is a simple function which takes an implicit parameter of type @String@, and prints it to the screen. The 'param_' function is used to retrieve the unnamed implicit parameter of type @String@ from @putParam@'s context. The type signature is necessary to force 'param_' to return a @String@, as this cannot be inferred due to the polymorphism of @show@. >>> putParam Param was "" This is how we call @putParam@ without specifying its implicit parameters. If an implicit parameter is left unspecified, its value is defaulted to 'def', assuming that its type has a 'Default' instance. If not, then it is a type error not to specify the value of an implicit parameter. >>> putParam $~ "hello, world" Param was "hello, world" The operator '$~' takes a function @f@ and a value to which to set the homotypic implicit parameter on @f@. It applies the implicit parameter to @f@ and returns the result. There is also a prefix version of @$~@ whose arguments are flipped called 'setParam_'. Here is an example of named implicit parameters: @ {\-\# LANGUAGE DataKinds, FlexibleContexts, RankNTypes #-\} import "Data.Implicit" import "Data.Proxy" putFooBar :: ('Implicit' \"foo\" String, 'Implicit' \"bar\" String) => IO () putFooBar = do putStrLn $ \"foo was: \" ++ show foo putStrLn $ \"bar was: \" ++ show bar foo :: 'Implicit' \"foo\" String => String foo = 'param' (Proxy :: Proxy \"foo\") bar :: 'Implicit' \"bar\" String => String bar = 'param' (Proxy :: Proxy \"bar\") setFoo :: String -> ('Implicit' \"foo\" String => a) -> a setFoo = 'setParam' (Proxy :: Proxy \"foo\") setBar :: String -> ('Implicit' \"bar\" String => a) -> a setBar = 'setParam' (Proxy :: Proxy \"bar\") @ The 'Implicit' constraint is the named equivalent of 'Implicit_'. It takes an additional argument of kind 'Symbol' (which requires the @DataKinds@ extension; see the "GHC.TypeLits" module) to specify the name of the implicit parameter. 'param' and 'setParam' work like their unnamed counterparts 'param_' and 'setParam_', but they also take a proxy argument to specify the name of the implicit parameter. The code above defines the wrappers @foo@ and @bar@ and @setFoo@ and @setBar@ around @param@ and @setParam@ respectively, which hide all the (slightly ugly) proxy stuff. >>> putFooBar foo was: "" bar was: "" Once again, the defaults of unspecified implicit parameters are given by the 'Default' class. >>> setFoo "hello, world" putFooBar foo was: "hello, world" bar was: "" >>> setBar "goodbye" $ setFoo "hello, world" putFooBar foo was: "hello, world" bar was: "goodbye" An infix version of @setParam@ is also provided, '$$~'. Using @$$~@, the above example would be: >>> putFooBar $$~ (Proxy :: Proxy "foo", "hello, world") $$~ (Proxy :: Proxy "bar", "goodbye") foo was: "hello, world" bar was: "goodbye -} module Data.Implicit ( Implicit , param , setParam , ($$~) , Implicit_ , param_ , setParam_ , ($~) ) where import Data.Default (Default, def) import GHC.TypeLits (Symbol) import GHC.Exts (Any) import Unsafe.Coerce (unsafeCoerce) ------------------------------------------------------------------------------ -- | The constraint @'Implicit' \"foo\" String@ on a function @f@ indicates -- that an implicit parameter named @\"foo\"@ of type @String@ is passed to -- @f@. -- -- The name @\"foo\"@ is a type of kind 'Symbol' (from the "GHC.TypeLits" -- module). The @DataKinds@ extension is required to refer to 'Symbol'-kinded -- types. class Implicit (s :: Symbol) a where _param :: proxy s -> a ------------------------------------------------------------------------------ instance Default a => Implicit s a where _param _ = def ------------------------------------------------------------------------------ -- | 'param' retrieves the implicit parameter named @s@ of type @a@ from the -- context @'Implicit' s a@. The name @s@ is specified by a proxy argument -- passed to @param@. param :: Implicit s a => proxy s -> a param = _param ------------------------------------------------------------------------------ -- | 'setParam' supplies a value for an implicit parameter named @s@ to a -- function which takes a homotypic and homonymous implicit parameter. The -- name @s@ is specified by a proxy argument passed to @setParam@. setParam :: proxy s -> a -> (Implicit s a => b) -> b setParam = using ------------------------------------------------------------------------------ -- | An infix version of 'setParam' with flipped arguments. ($$~) :: (Implicit s a => b) -> (proxy s, a) -> b ($$~) f (proxy, a) = using proxy a f ------------------------------------------------------------------------------ -- | The constraint @'Implicit_' String@ on a function @f@ indicates that an -- unnamed implicit parameter of type @String@ is passed to @f@. type Implicit_ = Implicit Any ------------------------------------------------------------------------------ -- | 'param_' retrieves the unnamed implicit parameter of type @a@ from the -- context @'Implicit_' a@. param_ :: Implicit_ a => a param_ = param (Proxy :: Proxy Any) ------------------------------------------------------------------------------ -- | 'setParam_' supplies a value for an unnamed implicit parameter to a -- function which takes a homotypic implicit parameter. setParam_ :: a -> (Implicit_ a => b) -> b setParam_ = setParam (Proxy :: Proxy Any) ------------------------------------------------------------------------------ -- | An infix version of 'setParam_' with flipped arguments. ($~) :: (Implicit_ a => b) -> a -> b infixr 1 $~ f $~ a = using (Proxy :: Proxy Any) a f ------------------------------------------------------------------------------ data Proxy (s :: Symbol) = Proxy ------------------------------------------------------------------------------ newtype Lift a = Lift a ------------------------------------------------------------------------------ newtype Tagged (s :: Symbol) a = Tagged a ------------------------------------------------------------------------------ data Dict c where Dict :: c => Dict c ------------------------------------------------------------------------------ using :: proxy s -> a -> (Implicit s a => b) -> b using p a = with (unlift (dict p a)) where with :: Dict c -> (c => b) -> b with Dict b = b unlift :: Dict (c (Lift p)) -> Dict (c p) unlift = unsafeCoerce dict :: proxy s -> a -> Dict (Implicit s (Lift a)) dict _ a' = let ?param = Tagged a' in Dict ------------------------------------------------------------------------------ instance (?param :: Tagged s a) => Implicit s (Lift a) where _param _ = let Tagged a = ?param in Lift a