Last active
January 10, 2019 03:28
-
-
Save Solonarv/328ad45c0674215938d08026a16efe07 to your computer and use it in GitHub Desktop.
Monadic capabilities using vinyl.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{-# language FunctionalDependencies #-} | |
{-# language KindSignatures #-} | |
{-# language DerivingVia #-} | |
{-# language ScopedTypeVariables #-} | |
{-# language TypeApplications #-} | |
-- some extensions elided, GHC should tell you what they are | |
module Capability where | |
import Control.Monad.Reader | |
import Control.Monad.State | |
import Control.Monad.Trans.Class | |
import Data.Vinyl | |
import UnliftIO | |
-- * Core definitions | |
newtype Cap (m :: Type -> Type) k = Cap { unCap :: k m } | |
type Caps m = Rec (Cap m) | |
-- might want to switch to ARec if you expect to have a lot of capabilities | |
type (∈!) (k :: (Type -> Type) -> Type) ks = RElem k ks | |
class Monad m => MonadCapability ks m | m -> ks where | |
getCaps :: m (Caps m ks) | |
default getCaps :: (MonadCapability n, MonadTrans t, Monad (t n), t n ~ m) => m (Caps m ks) | |
getCaps = lift getCaps | |
newtype CapabilityT ks m a = CapabilityT { runCapabilityT :: Caps (CapabilityT ks m a) ks -> m a } | |
deriving via (ReaderT (Caps (CapabilityT ks m a) ks) m a) | |
(Functor, Applicative, Monad, MonadState, MonadIO, MonadUnliftIO) | |
-- this should ideally contain *all* the mtl classes, I'm just lazy | |
instance Monad m => MonadCapability ks (CapabilityT ks m) where | |
getCaps = CapabilityT pure | |
instance MonadCapability m => MonadCapability (ReaderT e m) | |
instance MonadCapability m => MonadCapability (StateT s m) | |
-- repeat for every monad transformer available | |
-- * Some combinators | |
getCap :: forall k ks m. (k ∈! ks, MonadCapability ks m) => m (k m) | |
getCap = unCap . rget @k <$> getCaps | |
withCap :: forall k ks m a. (k ∈! ks, MonadCapability ks m) => (k m -> m a) -> m a | |
withCap sel = getCap >>= sel | |
withCap1 :: forall k ks m a b. (k ∈! ks, MonadCapability ks m) => (k m -> a -> m b) -> a -> m b | |
withCap1 sel a = do cap <- getCap; sel cap a | |
withCap2 :: forall k ks m a b c. (k ∈! ks, MonadCapability ks m) => (k m -> a -> b -> m c) -> a -> b -> m c | |
withCap2 sek a b = do cap <- getCap; sel cap a b | |
-- can be trivially extended for higher-arity functions | |
-- * An example | |
data Terminal m = Terminal { terminalPutStr :: String -> m () } | |
putStr :: (Terminal ∈! ks, MonadCapability ks m) => String -> m () | |
putStr = withCap1 terminalPutStr |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment