/src/Silica/Internal/Context.hs

https://github.com/mrkgnao/silica · Haskell · 366 lines · 143 code · 54 blank · 169 comment · 2 complexity · e5b9d61a869eec3577bc474eecd0d63c MD5 · raw file

  1. {-# LANGUAGE CPP #-}
  2. {-# LANGUAGE Rank2Types #-}
  3. {-# LANGUAGE TypeFamilies #-}
  4. {-# LANGUAGE KindSignatures #-}
  5. {-# LANGUAGE FlexibleContexts #-}
  6. {-# LANGUAGE FlexibleInstances #-}
  7. {-# LANGUAGE MultiParamTypeClasses #-}
  8. {-# LANGUAGE FunctionalDependencies #-}
  9. #if __GLASGOW_HASKELL__ >= 707
  10. {-# LANGUAGE RoleAnnotations #-}
  11. #endif
  12. -----------------------------------------------------------------------------
  13. -- |
  14. -- Module : Silica.Internal.Context
  15. -- Copyright : (C) 2012-2016 Edward Kmett
  16. -- License : BSD-style (see the file LICENSE)
  17. -- Maintainer : Edward Kmett <ekmett@gmail.com>
  18. -- Stability : experimental
  19. -- Portability : non-portable
  20. --
  21. ----------------------------------------------------------------------------
  22. module Silica.Internal.Context
  23. ( IndexedFunctor(..)
  24. , IndexedComonad(..)
  25. , IndexedComonadStore(..)
  26. , Sellable(..)
  27. , Context(..), Context'
  28. , Pretext(..), Pretext'
  29. , PretextT(..), PretextT'
  30. ) where
  31. import Control.Applicative
  32. import Control.Arrow
  33. import Control.Category
  34. import Control.Comonad
  35. import Control.Comonad.Store.Class
  36. import Silica.Internal.Indexed
  37. import Data.Functor.Compose
  38. import Data.Functor.Contravariant
  39. import Data.Functor.Identity
  40. import Data.Profunctor
  41. import Data.Profunctor.Rep
  42. import Data.Profunctor.Sieve
  43. import Data.Profunctor.Unsafe
  44. import Prelude hiding ((.),id)
  45. ------------------------------------------------------------------------------
  46. -- IndexedFunctor
  47. ------------------------------------------------------------------------------
  48. -- | This is a Bob Atkey -style 2-argument indexed functor.
  49. --
  50. -- It exists as a superclass for 'IndexedComonad' and expresses the functoriality
  51. -- of an 'IndexedComonad' in its third argument.
  52. class IndexedFunctor w where
  53. ifmap :: (s -> t) -> w a b s -> w a b t
  54. ------------------------------------------------------------------------------
  55. -- IndexedComonad
  56. ------------------------------------------------------------------------------
  57. -- | This is a Bob Atkey -style 2-argument indexed comonad.
  58. --
  59. -- It exists as a superclass for 'IndexedComonad' and expresses the functoriality
  60. -- of an 'IndexedComonad' in its third argument.
  61. --
  62. -- The notion of indexed monads is covered in more depth in Bob Atkey's
  63. -- "Parameterized Notions of Computation" <http://bentnib.org/paramnotions-jfp.pdf>
  64. -- and that construction is dualized here.
  65. class IndexedFunctor w => IndexedComonad w where
  66. -- | extract from an indexed comonadic value when the indices match.
  67. iextract :: w a a t -> t
  68. -- | duplicate an indexed comonadic value splitting the index.
  69. iduplicate :: w a c t -> w a b (w b c t)
  70. iduplicate = iextend id
  71. {-# INLINE iduplicate #-}
  72. -- | extend a indexed comonadic computation splitting the index.
  73. iextend :: (w b c t -> r) -> w a c t -> w a b r
  74. iextend f = ifmap f . iduplicate
  75. {-# INLINE iextend #-}
  76. ------------------------------------------------------------------------------
  77. -- IndexedComonadStore
  78. ------------------------------------------------------------------------------
  79. -- | This is an indexed analogue to 'ComonadStore' for when you are working with an
  80. -- 'IndexedComonad'.
  81. class IndexedComonad w => IndexedComonadStore w where
  82. -- | This is the generalization of 'pos' to an indexed comonad store.
  83. ipos :: w a c t -> a
  84. -- | This is the generalization of 'peek' to an indexed comonad store.
  85. ipeek :: c -> w a c t -> t
  86. ipeek c = iextract . iseek c
  87. {-# INLINE ipeek #-}
  88. -- | This is the generalization of 'peeks' to an indexed comonad store.
  89. ipeeks :: (a -> c) -> w a c t -> t
  90. ipeeks f = iextract . iseeks f
  91. {-# INLINE ipeeks #-}
  92. -- | This is the generalization of 'seek' to an indexed comonad store.
  93. iseek :: b -> w a c t -> w b c t
  94. -- | This is the generalization of 'seeks' to an indexed comonad store.
  95. iseeks :: (a -> b) -> w a c t -> w b c t
  96. -- | This is the generalization of 'experiment' to an indexed comonad store.
  97. iexperiment :: Functor f => (b -> f c) -> w b c t -> f t
  98. iexperiment bfc wbct = (`ipeek` wbct) <$> bfc (ipos wbct)
  99. {-# INLINE iexperiment #-}
  100. -- | We can always forget the rest of the structure of 'w' and obtain a simpler
  101. -- indexed comonad store model called 'Context'.
  102. context :: w a b t -> Context a b t
  103. context wabt = Context (`ipeek` wabt) (ipos wabt)
  104. {-# INLINE context #-}
  105. ------------------------------------------------------------------------------
  106. -- Sellable
  107. ------------------------------------------------------------------------------
  108. -- | This is used internally to construct a 'Silica.Internal.Bazaar.Bazaar', 'Context' or 'Pretext'
  109. -- from a singleton value.
  110. class Corepresentable p => Sellable p w | w -> p where
  111. sell :: p a (w a b b)
  112. ------------------------------------------------------------------------------
  113. -- Context
  114. ------------------------------------------------------------------------------
  115. -- | The indexed store can be used to characterize a 'Silica.Lens.Lens'
  116. -- and is used by 'Silica.Lens.cloneLens'.
  117. --
  118. -- @'Context' a b t@ is isomorphic to
  119. -- @newtype 'Context' a b t = 'Context' { runContext :: forall f. 'Functor' f => (a -> f b) -> f t }@,
  120. -- and to @exists s. (s, 'Silica.Lens.Lens' s t a b)@.
  121. --
  122. -- A 'Context' is like a 'Silica.Lens.Lens' that has already been applied to a some structure.
  123. data Context a b t = Context (b -> t) a
  124. -- type role Context representational representational representational
  125. instance IndexedFunctor Context where
  126. ifmap f (Context g t) = Context (f . g) t
  127. {-# INLINE ifmap #-}
  128. instance IndexedComonad Context where
  129. iextract (Context f a) = f a
  130. {-# INLINE iextract #-}
  131. iduplicate (Context f a) = Context (Context f) a
  132. {-# INLINE iduplicate #-}
  133. iextend g (Context f a) = Context (g . Context f) a
  134. {-# INLINE iextend #-}
  135. instance IndexedComonadStore Context where
  136. ipos (Context _ a) = a
  137. {-# INLINE ipos #-}
  138. ipeek b (Context g _) = g b
  139. {-# INLINE ipeek #-}
  140. ipeeks f (Context g a) = g (f a)
  141. {-# INLINE ipeeks #-}
  142. iseek a (Context g _) = Context g a
  143. {-# INLINE iseek #-}
  144. iseeks f (Context g a) = Context g (f a)
  145. {-# INLINE iseeks #-}
  146. iexperiment f (Context g a) = g <$> f a
  147. {-# INLINE iexperiment #-}
  148. context = id
  149. {-# INLINE context #-}
  150. instance Functor (Context a b) where
  151. fmap f (Context g t) = Context (f . g) t
  152. {-# INLINE fmap #-}
  153. instance a ~ b => Comonad (Context a b) where
  154. extract (Context f a) = f a
  155. {-# INLINE extract #-}
  156. duplicate (Context f a) = Context (Context f) a
  157. {-# INLINE duplicate #-}
  158. extend g (Context f a) = Context (g . Context f) a
  159. {-# INLINE extend #-}
  160. instance a ~ b => ComonadStore a (Context a b) where
  161. pos = ipos
  162. {-# INLINE pos #-}
  163. peek = ipeek
  164. {-# INLINE peek #-}
  165. peeks = ipeeks
  166. {-# INLINE peeks #-}
  167. seek = iseek
  168. {-# INLINE seek #-}
  169. seeks = iseeks
  170. {-# INLINE seeks #-}
  171. experiment = iexperiment
  172. {-# INLINE experiment #-}
  173. instance Sellable (->) Context where
  174. sell = Context id
  175. {-# INLINE sell #-}
  176. -- | @type 'Context'' a s = 'Context' a a s@
  177. type Context' a = Context a a
  178. ------------------------------------------------------------------------------
  179. -- Pretext
  180. ------------------------------------------------------------------------------
  181. -- | This is a generalized form of 'Context' that can be repeatedly cloned with less
  182. -- impact on its performance, and which permits the use of an arbitrary 'Conjoined'
  183. -- 'Profunctor'
  184. newtype Pretext p a b t = Pretext { runPretext :: forall f. Functor f => p a (f b) -> f t }
  185. -- type role Pretext representational nominal nominal nominal
  186. -- | @type 'Pretext'' p a s = 'Pretext' p a a s@
  187. type Pretext' p a = Pretext p a a
  188. instance IndexedFunctor (Pretext p) where
  189. ifmap f (Pretext k) = Pretext (fmap f . k)
  190. {-# INLINE ifmap #-}
  191. instance Functor (Pretext p a b) where
  192. fmap = ifmap
  193. {-# INLINE fmap #-}
  194. instance Conjoined p => IndexedComonad (Pretext p) where
  195. iextract (Pretext m) = runIdentity $ m (arr Identity)
  196. {-# INLINE iextract #-}
  197. iduplicate (Pretext m) = getCompose $ m (Compose #. distrib sell . sell)
  198. {-# INLINE iduplicate #-}
  199. instance (a ~ b, Conjoined p) => Comonad (Pretext p a b) where
  200. extract = iextract
  201. {-# INLINE extract #-}
  202. duplicate = iduplicate
  203. {-# INLINE duplicate #-}
  204. instance Conjoined p => IndexedComonadStore (Pretext p) where
  205. ipos (Pretext m) = getConst $ coarr m $ arr Const
  206. {-# INLINE ipos #-}
  207. ipeek a (Pretext m) = runIdentity $ coarr m $ arr (\_ -> Identity a)
  208. {-# INLINE ipeek #-}
  209. ipeeks f (Pretext m) = runIdentity $ coarr m $ arr (Identity . f)
  210. {-# INLINE ipeeks #-}
  211. iseek a (Pretext m) = Pretext (lmap (lmap (const a)) m)
  212. {-# INLINE iseek #-}
  213. iseeks f (Pretext m) = Pretext (lmap (lmap f) m)
  214. {-# INLINE iseeks #-}
  215. iexperiment f (Pretext m) = coarr m (arr f)
  216. {-# INLINE iexperiment #-}
  217. context (Pretext m) = coarr m (arr sell)
  218. {-# INLINE context #-}
  219. instance (a ~ b, Conjoined p) => ComonadStore a (Pretext p a b) where
  220. pos = ipos
  221. {-# INLINE pos #-}
  222. peek = ipeek
  223. {-# INLINE peek #-}
  224. peeks = ipeeks
  225. {-# INLINE peeks #-}
  226. seek = iseek
  227. {-# INLINE seek #-}
  228. seeks = iseeks
  229. {-# INLINE seeks #-}
  230. experiment = iexperiment
  231. {-# INLINE experiment #-}
  232. instance Corepresentable p => Sellable p (Pretext p) where
  233. sell = cotabulate $ \ w -> Pretext (`cosieve` w)
  234. {-# INLINE sell #-}
  235. ------------------------------------------------------------------------------
  236. -- PretextT
  237. ------------------------------------------------------------------------------
  238. -- | This is a generalized form of 'Context' that can be repeatedly cloned with less
  239. -- impact on its performance, and which permits the use of an arbitrary 'Conjoined'
  240. -- 'Profunctor'.
  241. --
  242. -- The extra phantom 'Functor' is used to let us lie and claim
  243. -- 'Silica.Getter.Getter'-compatibility under limited circumstances.
  244. -- This is used internally to permit a number of combinators to gracefully
  245. -- degrade when applied to a 'Silica.Fold.Fold' or
  246. -- 'Silica.Getter.Getter'.
  247. newtype PretextT p (g :: * -> *) a b t = PretextT { runPretextT :: forall f. Functor f => p a (f b) -> f t }
  248. #if __GLASGOW_HASKELL__ >= 707
  249. -- really we want PretextT p g a b t to permit the last 3 arguments to be representational iff p and f accept representational arguments
  250. -- but that isn't currently an option in GHC
  251. type role PretextT representational nominal nominal nominal nominal
  252. #endif
  253. -- | @type 'PretextT'' p g a s = 'PretextT' p g a a s@
  254. type PretextT' p g a = PretextT p g a a
  255. instance IndexedFunctor (PretextT p g) where
  256. ifmap f (PretextT k) = PretextT (fmap f . k)
  257. {-# INLINE ifmap #-}
  258. instance Functor (PretextT p g a b) where
  259. fmap = ifmap
  260. {-# INLINE fmap #-}
  261. instance Conjoined p => IndexedComonad (PretextT p g) where
  262. iextract (PretextT m) = runIdentity $ m (arr Identity)
  263. {-# INLINE iextract #-}
  264. iduplicate (PretextT m) = getCompose $ m (Compose #. distrib sell . sell)
  265. {-# INLINE iduplicate #-}
  266. instance (a ~ b, Conjoined p) => Comonad (PretextT p g a b) where
  267. extract = iextract
  268. {-# INLINE extract #-}
  269. duplicate = iduplicate
  270. {-# INLINE duplicate #-}
  271. instance Conjoined p => IndexedComonadStore (PretextT p g) where
  272. ipos (PretextT m) = getConst $ coarr m $ arr Const
  273. {-# INLINE ipos #-}
  274. ipeek a (PretextT m) = runIdentity $ coarr m $ arr (\_ -> Identity a)
  275. {-# INLINE ipeek #-}
  276. ipeeks f (PretextT m) = runIdentity $ coarr m $ arr (Identity . f)
  277. {-# INLINE ipeeks #-}
  278. iseek a (PretextT m) = PretextT (lmap (lmap (const a)) m)
  279. {-# INLINE iseek #-}
  280. iseeks f (PretextT m) = PretextT (lmap (lmap f) m)
  281. {-# INLINE iseeks #-}
  282. iexperiment f (PretextT m) = coarr m (arr f)
  283. {-# INLINE iexperiment #-}
  284. context (PretextT m) = coarr m (arr sell)
  285. {-# INLINE context #-}
  286. instance (a ~ b, Conjoined p) => ComonadStore a (PretextT p g a b) where
  287. pos = ipos
  288. {-# INLINE pos #-}
  289. peek = ipeek
  290. {-# INLINE peek #-}
  291. peeks = ipeeks
  292. {-# INLINE peeks #-}
  293. seek = iseek
  294. {-# INLINE seek #-}
  295. seeks = iseeks
  296. {-# INLINE seeks #-}
  297. experiment = iexperiment
  298. {-# INLINE experiment #-}
  299. instance Corepresentable p => Sellable p (PretextT p g) where
  300. sell = cotabulate $ \ w -> PretextT (`cosieve` w)
  301. {-# INLINE sell #-}
  302. instance (Profunctor p, Contravariant g) => Contravariant (PretextT p g a b) where
  303. contramap _ = (<$) (error "contramap: PretextT")
  304. {-# INLINE contramap #-}
  305. ------------------------------------------------------------------------------
  306. -- Utilities
  307. ------------------------------------------------------------------------------
  308. -- | We can convert any 'Conjoined' 'Profunctor' to a function,
  309. -- possibly losing information about an index in the process.
  310. coarr :: (Representable q, Comonad (Rep q)) => q a b -> a -> b
  311. coarr qab = extract . sieve qab
  312. {-# INLINE coarr #-}