summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfredefox <>2017-05-19 20:22:00 (GMT)
committerhdiff <hdiff@hdiff.luite.com>2017-05-19 20:22:00 (GMT)
commite3baa641bbe698d8bfb0381aa1a7be28170ac05a (patch)
treeab0d3f4ebbf3e3de85e7f28499b8e4eea57d8ed5
version 0.1.0.00.1.0.0
-rw-r--r--LICENSE30
-rw-r--r--README.md1
-rw-r--r--Setup.hs2
-rw-r--r--app/Example.lhs42
-rw-r--r--shade.cabal66
-rw-r--r--src/Control/Monad/Shade.hs70
6 files changed, 211 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7d49f67
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Frederik Hanghøj Iversen (c) 2017
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Frederik Hanghøj Iversen nor the names of other
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5e5138f
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+# shade
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
index 0000000..9a994af
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/app/Example.lhs b/app/Example.lhs
new file mode 100644
index 0000000..e4ed566
--- /dev/null
+++ b/app/Example.lhs
@@ -0,0 +1,42 @@
+> {-# LANGUAGE MultiParamTypeClasses #-}
+> module Main ( main ) where
+>
+> import Data.Functor.Identity
+> import Control.Monad
+>
+> import Control.Monad.Shade
+
+A shade can be used to save heterogeneous types in containers with the same
+type e.g. using a type-class as a common denominator:
+
+> showshade :: Applicative m => Show a => a -> Shade m String
+> showshade a = pure (show a)
+>
+> hetero :: Shade Identity String
+> hetero = mconcat [ showshade () , showshade 2 , showshade "hej" ]
+
+The values inside a shade are stored in a context. We can swap this context
+by defining a transfer function:
+
+> idToIO :: Identity b -> IO b
+> idToIO (Identity v) = putStrLn "Transferring" *> pure v
+
+The context is switched using `transfer` and we can access the value in this
+new context by using `shadow`:
+
+> runInIO :: Shade Identity a -> IO a
+> runInIO = shadow . transfer idToIO
+
+The point to note about this example is that the values are stored in an
+shades with the identity as context. We can manipulate this context
+including the value. We cannot, however inspect the value since it is
+universally quantified.
+
+> noisy :: String -> Shade Identity (IO ())
+> noisy s = pure (putStrLn s)
+
+> main :: IO ()
+> main = do
+> r <- runInIO hetero
+> putStrLn r
+> join . runInIO . mconcat . map (noisy . show) $ [0..10]
diff --git a/shade.cabal b/shade.cabal
new file mode 100644
index 0000000..5240974
--- /dev/null
+++ b/shade.cabal
@@ -0,0 +1,66 @@
+name: shade
+version: 0.1.0.0
+synopsis: A control structure used to combine heterogenous types with delayed effects.
+description:
+ > {-# LANGUAGE MultiParamTypeClasses #-}
+ > module Main ( main ) where
+ >
+ > import Data.Functor.Identity
+ > import Control.Monad
+ >
+ > import Control.Monad.Shade
+
+ A shade can be used to save heterogeneous types in containers with the same
+ type e.g. using a type-class as a common denominator:
+
+ > showshade :: Applicative m => Show a => a -> Shade m String
+ > showshade a = pure (show a)
+ >
+ > hetero :: Shade Identity String
+ > hetero = mconcat [ showshade () , showshade 2 , showshade "hej" ]
+
+ The values inside a shade are stored in a context. We can swap this context
+ by defining a transfer function:
+
+ > idToIO :: Identity b -> IO b
+ > idToIO (Identity v) = putStrLn "Transferring" *> pure v
+
+ The context is switched using `transfer` and we can access the value in this
+ new context by using `shadow`:
+
+ > runInIO :: Shade Identity a -> IO a
+ > runInIO = shadow . transfer idToIO
+
+ The point to note about this example is that the values are stored in an
+ shades with the identity as context. We can manipulate this context
+ including the value. We cannot, however inspect the value since it is
+ universally quantified.
+
+homepage: https://github.com/fredefox/shade#readme
+license: BSD3
+license-file: LICENSE
+author: Frederik Hanghøj Iversen
+maintainer: fhi.1990@gmail.com
+copyright: Copyright: (c) 2017 Frederik Hanghøj Iversen
+category: Web
+build-type: Simple
+extra-source-files: README.md
+cabal-version: >=1.10
+
+library
+ hs-source-dirs: src
+ exposed-modules: Control.Monad.Shade
+ build-depends: base >= 4.7 && < 5
+ default-language: Haskell2010
+
+executable shade
+ hs-source-dirs: app
+ main-is: Example.lhs
+ ghc-options: -threaded -rtsopts -with-rtsopts=-N
+ build-depends: base
+ , shade
+ default-language: Haskell2010
+
+source-repository head
+ type: git
+ location: https://github.com/fredefox/shade
diff --git a/src/Control/Monad/Shade.hs b/src/Control/Monad/Shade.hs
new file mode 100644
index 0000000..6b01c6f
--- /dev/null
+++ b/src/Control/Monad/Shade.hs
@@ -0,0 +1,70 @@
+{-| A control structure used to combine heterogenous types with delayed effects. -}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+module Control.Monad.Shade
+ ( Shade()
+ , shade
+ , hide
+ , shadow
+ , transfer
+ ) where
+
+import Control.Monad
+
+-- | A shade consists of a hidden value and an image of that value. The hidden
+-- values are stored in a context and cannot be accessed directly.
+data Shade m b = forall a . Shade (m a) (a -> b)
+
+-- | 'fmap' applies a function to the result of the projected value inside the
+-- values original context.
+instance Functor (Shade m) where
+ f `fmap` Shade m p = Shade m (f . p)
+
+-- | 'pure' is the identity projection of the original value stored in a pure
+-- context.
+--
+-- @a '<*>' b@ combines the contexts of the hidden values and applies the shadow
+-- of @b@ value onto the shadow of @a@.
+instance Applicative m => Applicative (Shade m) where
+ pure x = Shade (pure x) id
+ Shade m0 p0 <*> Shade m1 p1
+ = Shade ((,) <$> m0 <*> m1)
+ $ \(a0, a1) -> p0 a0 (p1 a1)
+
+-- | @m '>>=' f@ applies @f@ to the projected value inside the original context
+-- of @m@. The result is the a shade which becomes the source object in the
+-- result. This resut is nested twice inside the same context, and these are
+-- joined together.
+instance Monad m => Monad (Shade m) where
+ return = pure
+ Shade m0 p0 >>= f = Shade (join m) id
+ where
+ m = shadow . f . p0 <$> m0
+
+-- | 'mempty' is simply the shadow and source of the neutral element of the
+-- stored value.
+--
+-- 'mappend' combines the contexts of two shadows and mappends their stored
+-- values.
+instance (Applicative m, Monoid b) => Monoid (Shade m b) where
+ mempty = pure mempty
+ mappend a b = mappend <$> a <*> b
+
+-- | Insert a contextual value and its projection into a shade.
+shade :: m a -> (a -> b) -> Shade m b
+shade = Shade
+
+-- | Hide a boxed value inside a shade with the identity as projection.
+hide :: m a -> Shade m a
+hide a = Shade a id
+
+-- | The projection of the hidden value (the "shadow").
+shadow :: Functor m => Shade m b -> m b
+shadow (Shade a p) = p <$> a
+
+-- | Changed the context of a hidden value. The first argument must be
+-- universally quantified since no assumptions may be made as to what value is
+-- stored inside the shade.
+transfer :: (forall a . m a -> n a) -> Shade m t -> Shade n t
+transfer f (Shade m t) = Shade (f m) t