{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ExplicitForAll #-}

-- | Companion module to "Dep.Has" for disambiguanting record components within an environment.

--

-- Similar in purpose to the [Qualifier annotation](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Qualifier.html) in Java Spring.

--

--  >>> :{

--  newtype Foo d = Foo {foo :: String -> d ()} deriving Generic

--  makeIOFoo :: MonadIO m => Foo m

--  makeIOFoo = Foo (liftIO . putStrLn)

--  makeIOFoo' :: MonadIO m => Foo m

--  makeIOFoo' = Foo (\_ -> liftIO $ putStrLn "this is secondary")

--  env :: InductiveEnv '[Foo, Tagged "secondary" Foo] Identity IO

--  env = AddDep @Foo (Identity makeIOFoo)

--      $ AddDep @(Tagged "secondary" Foo) (Identity (tagged makeIOFoo'))

--      $ EmptyEnv 

-- :}

--

-- >>> :{

--  foo (dep env) "this is foo"

-- :}

-- this is foo

--

-- >>> :{

--  foo (untag @"secondary" (dep env)) "this is foo"

-- :}

-- this is secondary

--

--

-- When using functions from "Dep.SimpleAdvice" (which tend to depend on coercions) with 'Tagged' components, remember to import the newtype's constructor.

module Dep.Tagged
  (
    Tagged (..),
    tagged,
    untag
  )
where

import Data.Kind
import Data.Typeable
import GHC.Generics qualified as G

-- | Very similar to the @Data.Tagged@ type from the \"tagged\" package, but

-- with an extra monad type argument.  The intended use is to disambiguate

-- record components within an environment, when there are multiple records of the same

-- type.

type Tagged :: k -> ((Type -> Type) -> Type) -> (Type -> Type) -> Type
newtype Tagged s r_ m = Tagged {Tagged s r_ m -> r_ m
unTagged :: r_ m}
  deriving
    ( 
      (forall x. Tagged s r_ m -> Rep (Tagged s r_ m) x)
-> (forall x. Rep (Tagged s r_ m) x -> Tagged s r_ m)
-> Generic (Tagged s r_ m)
forall x. Rep (Tagged s r_ m) x -> Tagged s r_ m
forall x. Tagged s r_ m -> Rep (Tagged s r_ m) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall k (s :: k) (r_ :: (* -> *) -> *) (m :: * -> *) x.
Rep (Tagged s r_ m) x -> Tagged s r_ m
forall k (s :: k) (r_ :: (* -> *) -> *) (m :: * -> *) x.
Tagged s r_ m -> Rep (Tagged s r_ m) x
$cto :: forall k (s :: k) (r_ :: (* -> *) -> *) (m :: * -> *) x.
Rep (Tagged s r_ m) x -> Tagged s r_ m
$cfrom :: forall k (s :: k) (r_ :: (* -> *) -> *) (m :: * -> *) x.
Tagged s r_ m -> Rep (Tagged s r_ m) x
G.Generic,
      Typeable
    )

-- When inserting into an environment a component that you want to disambiguate, provide the the tag using a type variable and then supply the record value. 

tagged :: forall s r_ m . r_ m -> Tagged s r_ m
tagged :: r_ m -> Tagged s r_ m
tagged = r_ m -> Tagged s r_ m
forall k (s :: k) (r_ :: (* -> *) -> *) (m :: * -> *).
r_ m -> Tagged s r_ m
Tagged

-- | Alias for 'unTagged'.

--

-- When invoking a method from a tagged dependency,

-- provide the tag using a type application and compose with the record selector.

untag :: Tagged s r_ m -> r_ m
untag :: Tagged s r_ m -> r_ m
untag = Tagged s r_ m -> r_ m
forall k (s :: k) (r_ :: (* -> *) -> *) (m :: * -> *).
Tagged s r_ m -> r_ m
unTagged

-- $setup

--

-- >>> :set -XTypeApplications

-- >>> :set -XMultiParamTypeClasses

-- >>> :set -XImportQualifiedPost

-- >>> :set -XTemplateHaskell

-- >>> :set -XStandaloneKindSignatures

-- >>> :set -XNamedFieldPuns

-- >>> :set -XFunctionalDependencies

-- >>> :set -XFlexibleContexts

-- >>> :set -XDataKinds

-- >>> :set -XBlockArguments

-- >>> :set -XFlexibleInstances

-- >>> :set -XTypeFamilies

-- >>> :set -XDeriveGeneric

-- >>> :set -XViewPatterns

-- >>> import Data.Kind

-- >>> import Control.Monad.IO.Class

-- >>> import GHC.Generics (Generic)

-- >>> import Dep.Has

-- >>> import Dep.Env

--