/src/Capability/State/Internal/Class.hs

https://github.com/tweag/capability · Haskell · 173 lines · 73 code · 16 blank · 84 comment · 10 complexity · fd5f92f2a8f2d818f8a31c25f319dffd MD5 · raw file

  1. {-# LANGUAGE AllowAmbiguousTypes #-}
  2. {-# LANGUAGE FlexibleContexts #-}
  3. {-# LANGUAGE FlexibleInstances #-}
  4. {-# LANGUAGE FunctionalDependencies #-}
  5. {-# LANGUAGE InstanceSigs #-}
  6. {-# LANGUAGE KindSignatures #-}
  7. {-# LANGUAGE MagicHash #-}
  8. {-# LANGUAGE MultiParamTypeClasses #-}
  9. {-# LANGUAGE QuantifiedConstraints #-}
  10. {-# LANGUAGE RankNTypes #-}
  11. {-# LANGUAGE ScopedTypeVariables #-}
  12. {-# LANGUAGE TypeApplications #-}
  13. {-# LANGUAGE TypeFamilies #-}
  14. {-# LANGUAGE TypeInType #-}
  15. {-# LANGUAGE TypeOperators #-}
  16. {-# OPTIONS_HADDOCK hide #-}
  17. module Capability.State.Internal.Class
  18. ( HasState(..)
  19. , get
  20. , put
  21. , state
  22. , modify
  23. , modify'
  24. , gets
  25. , zoom
  26. , Reified (..)
  27. ) where
  28. import Capability.Constraints
  29. import Capability.Derive (derive)
  30. import Capability.Reflection
  31. import Capability.Source.Internal.Class
  32. import Capability.Sink.Internal.Class
  33. import Data.Coerce (Coercible, coerce)
  34. import Data.Kind (Type)
  35. import GHC.Exts (Proxy#, proxy#)
  36. -- | State capability
  37. --
  38. -- An instance should fulfill the following laws.
  39. -- At this point these laws are not definitive,
  40. -- see <https://github.com/haskell/mtl/issues/5>.
  41. --
  42. -- prop> get @t >>= \s1 -> get @t >>= \s2 -> pure (s1, s2) = get @t >>= \s -> pure (s, s)
  43. -- prop> get @t >>= \_ -> put @t s = put @t s
  44. -- prop> put @t s1 >> put @t s2 = put @t s2
  45. -- prop> put @t s >> get @t = put @t s >> pure s
  46. -- prop> state @t f = get @t >>= \s -> let (a, s') = f s in put @t s' >> pure a
  47. class (Monad m, HasSource tag s m, HasSink tag s m)
  48. => HasState (tag :: k) (s :: Type) (m :: Type -> Type) | tag m -> s
  49. where
  50. -- | For technical reasons, this method needs an extra proxy argument.
  51. -- You only need it if you are defining new instances of 'HasState.
  52. -- Otherwise, you will want to use 'state'.
  53. -- See 'state' for more documentation.
  54. state_ :: Proxy# tag -> (s -> (a, s)) -> m a
  55. -- | @get \@tag@
  56. -- retrieve the current state of the state capability @tag@.
  57. get :: forall tag s m. HasState tag s m => m s
  58. get = await @tag
  59. {-# INLINE get #-}
  60. -- | @put \@tag s@
  61. -- replace the current state of the state capability @tag@ with @s@.
  62. put :: forall tag s m. HasState tag s m => s -> m ()
  63. put = yield @tag
  64. {-# INLINE put #-}
  65. -- | @state \@tag f@
  66. -- lifts a pure state computation @f@ to a monadic action in an arbitrary
  67. -- monad @m@ with capability @HasState@.
  68. --
  69. -- Given the current state @s@ of the state capability @tag@
  70. -- and @(a, s') = f s@, update the state to @s'@ and return @a@.
  71. state :: forall tag s m a. HasState tag s m => (s -> (a, s)) -> m a
  72. state = state_ (proxy# @tag)
  73. {-# INLINE state #-}
  74. -- | @modify \@tag f@
  75. -- given the current state @s@ of the state capability @tag@
  76. -- and @s' = f s@, updates the state of the capability @tag@ to @s'@.
  77. modify :: forall tag s m. HasState tag s m => (s -> s) -> m ()
  78. modify f = state @tag $ \s -> ((), f s)
  79. {-# INLINE modify #-}
  80. -- | Same as 'modify' but strict in the new state.
  81. modify' :: forall tag s m. HasState tag s m => (s -> s) -> m ()
  82. modify' f = do
  83. s' <- get @tag
  84. put @tag $! f s'
  85. {-# INLINE modify' #-}
  86. -- | @gets \@tag f@
  87. -- retrieves the image, by @f@ of the current state
  88. -- of the state capability @tag@.
  89. --
  90. -- prop> gets @tag f = f <$> get @tag
  91. gets :: forall tag s m a. HasState tag s m => (s -> a) -> m a
  92. gets f = do
  93. s <- get @tag
  94. pure (f s)
  95. {-# INLINE gets #-}
  96. -- | Execute the given state action on a sub-component of the current state as
  97. -- defined by the given transformer @t@. The set of retained capabilities must
  98. -- be passed as @cs. If no capabilities are required,
  99. -- 'Capabilities.Constraints.None' can be used.
  100. --
  101. -- Examples:
  102. --
  103. -- > foo :: HasState "foo" Int m => m ()
  104. -- > zoom @"foo" @(Field "foo" "foobar") @None foo
  105. -- > :: (HasField' "foobar" record Int, HasState "foobar" record m) => m ()
  106. -- >
  107. -- > zoom @"foo" @(Field "foo" "foobar") @('[MonadIO]) bar
  108. -- > :: ( HasField' "foobar" record Int, HasState "foobar" record m
  109. -- > , MonadIO m) => m ()
  110. -- >
  111. -- > foo :: HasState "foo" Int m => m ()
  112. -- > bar :: (MonadIO m, HasState "foo" Int m) => m ()
  113. --
  114. -- Note: the 'Data.Generics.Product.Fields.HasField'' constraint comes from the
  115. -- @generic-lens@ package.
  116. --
  117. -- This function is experimental and subject to change.
  118. -- See <https://github.com/tweag/capability/issues/46>.
  119. zoom :: forall innertag t (cs :: [Capability]) inner m a.
  120. ( forall x. Coercible (t m x) (m x)
  121. , HasState innertag inner (t m)
  122. , All cs m )
  123. => (forall m'. All (HasState innertag inner ': cs) m' => m' a) -> m a
  124. zoom action =
  125. derive @t @'[HasState innertag inner] @cs action
  126. {-# INLINE zoom #-}
  127. --------------------------------------------------------------------------------
  128. data instance Reified tag (HasState tag s) m = ReifiedState
  129. { _stateSource :: Reified tag (HasSource tag s) m,
  130. _stateSink :: Reified tag (HasSink tag s) m,
  131. _state :: forall a. (s -> (a, s)) -> m a
  132. }
  133. instance
  134. ( Monad m,
  135. Reifies s' (Reified tag (HasState tag s) m)
  136. ) =>
  137. HasSource tag s (Reflected s' (HasState tag s) m)
  138. where
  139. await_ _ = coerce $ _await $ _stateSource $ reified @s'
  140. {-# INLINE await_ #-}
  141. instance
  142. ( Monad m,
  143. Reifies s' (Reified tag (HasState tag s) m)
  144. ) =>
  145. HasSink tag s (Reflected s' (HasState tag s) m)
  146. where
  147. yield_ _ = coerce $ _yield $ _stateSink $ reified @s'
  148. {-# INLINE yield_ #-}
  149. instance
  150. ( Monad m,
  151. Reifies s' (Reified tag (HasState tag s) m)
  152. ) =>
  153. HasState tag s (Reflected s' (HasState tag s) m)
  154. where
  155. state_ :: forall a. Proxy# tag -> (s -> (a, s)) -> Reflected s' (HasState tag s) m a
  156. state_ _ = coerce @((s -> (a, s)) -> m a) $ _state (reified @s')
  157. {-# INLINE state_ #-}