-
Notifications
You must be signed in to change notification settings - Fork 15
/
Monad.hs
105 lines (87 loc) · 3.39 KB
/
Monad.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LinearTypes #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
module Directory.Server.Monad where
import qualified Control.Monad.Catch as Catch
import qualified Control.Monad.IO.Class.Linear as Linear
import qualified Control.Functor.Linear as Linear
import Control.Monad.Logger
import Control.Monad.Reader
import qualified Data.Functor.Linear as Data
import Data.Kind (Type)
import Directory.Server.Monad.Classes
import Foreign.JNI.Safe
import Prelude
import Prelude.Linear (Ur(..))
import qualified Prelude.Linear as Linear hiding (IO)
import qualified System.Directory as Directory
import qualified System.IO.Linear as Linear
import qualified Unsafe.Linear as Unsafe
data Environment = Environment
{ envRootDirectory :: FilePath
}
newtype Server a = Server (LoggingT (ReaderT Environment IO) a)
deriving
( Applicative
, Functor
, Monad
, MonadIO
, MonadLogger
, Catch.MonadThrow
, Catch.MonadCatch
, Catch.MonadMask
, MonadReader Environment
)
instance MonadFileSystem Server where
doesDirectoryExist = liftIO . Directory.doesDirectoryExist
listDirectory = liftIO . Directory.listDirectory
canonicalizePath = liftIO . Directory.canonicalizePath
newtype LServer a = LServer { unLServer :: Server a }
instance Linear.MonadIO LServer where
liftIO = Unsafe.toLinear (\a -> LServer (liftIO (Linear.withLinearIO (unsafeUnrestrict a)))) where
unsafeUnrestrict :: Linear.IO a -> Linear.IO (Ur a)
unsafeUnrestrict action = action Linear.>>= Unsafe.toLinear (\a -> Linear.return (Ur a))
runLServer :: Environment -> LServer () -> IO ()
runLServer env (LServer (Server m)) =
withLocalFrame_ (liftPreludeIO $ runReaderT (runStdoutLoggingT m) env)
instance Data.Functor LServer where
fmap f = Unsafe.toLinear Linear.$ \(LServer m) ->
LServer (fmap (\x -> f x) m)
instance Linear.Functor LServer where
fmap = Unsafe.toLinear2 Linear.$ \(f :: a %1-> b) (LServer m) ->
LServer (fmap (\x -> f x) m)
instance Data.Applicative LServer where
pure = LServer . pure
(<*>) = Unsafe.toLinear2 Linear.$ \(LServer f) (LServer a) ->
LServer (fmap (\(g :: a %1-> b) -> (\x -> g x)) f <*> a)
instance Linear.Applicative LServer where
pure = Unsafe.toLinear (LServer . pure)
(<*>) = (Data.<*>)
instance Linear.Monad LServer where
(>>=) = Unsafe.toLinear2 Linear.$ \(LServer m) (f :: a %1-> LServer b) ->
LServer $ m >>= \x -> case f x of
LServer n -> n
(>>) = Unsafe.toLinear2 Linear.$ \(LServer m0) (LServer m1) ->
LServer (m0 >> m1)
instance MonadFinally LServer where
finally = Unsafe.toLinear2 Linear.$ \(LServer m) (LServer cleanup) ->
LServer (m `Catch.finally` cleanup)
instance MonadMask LServer where
mask = Unsafe.toLinear umask
where
umask :: (Unmask LServer -> LServer b) -> LServer b
umask f = LServer $ Catch.mask $ \(u :: forall a. Server a -> Server a) ->
case f (Unmask (Unsafe.toLinear (LServer . u . unLServer))) of
LServer n -> n
class (Linear.Monad m, Prelude.Monad (LiftedM m)) => MonadLift m where
type LiftedM m :: Type -> Type
lift :: LiftedM m a -> m a
lift m = liftU m Linear.>>= \(Ur a) -> Linear.return a
liftU :: LiftedM m a -> m (Ur a)
instance MonadLift LServer where
type LiftedM LServer = Server
lift = LServer
liftU = LServer . fmap Ur