Created
January 16, 2023 21:05
-
-
Save bristermitten/3e260ca46cfb80848164eacc3153d3ac to your computer and use it in GitHub Desktop.
polysemy stuff
This file contains hidden or 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 TemplateHaskell #-} | |
module Polysemy.Socket where | |
import Control.Exception (bracketOnError) | |
import Data.ByteString qualified as BS | |
import Network.Socket (AddrInfo (addrAddress), SockAddr, Socket, SocketOption (ReuseAddr), accept, bind, gracefulClose, listen, openSocket, setCloseOnExecIfNeeded, setSocketOption, withFdSocket) | |
import Network.Socket qualified as S | |
import Network.Socket.ByteString | |
import Polysemy (Member, Members, Sem, interpret, makeSem, pureT, reinterpretH) | |
import Polysemy.Embed | |
import Prelude hiding (State, get, put) | |
newtype SemSocket = SemSocket {unSemSocket :: Socket} deriving (Show, Eq) | |
data ReadSocket m a where | |
NextByte :: ReadSocket m (Maybe Word8) | |
NextN :: Int -> ReadSocket m (Maybe ByteString) | |
makeSem ''ReadSocket | |
runRead :: Member (Embed IO) r => SemSocket -> Sem (ReadSocket ': r) a -> Sem r a | |
runRead (SemSocket socket) = interpret $ \case | |
NextByte -> embed $ do | |
bytes <- recv socket 1 | |
pure $ if BS.null bytes then Nothing else Just (BS.head bytes) | |
NextN n -> embed $ do | |
bytes <- recv socket n | |
pure $ if BS.null bytes then Nothing else Just bytes | |
data WriteSocket m a where | |
Write :: ByteString -> WriteSocket m () | |
makeSem ''WriteSocket | |
runWrite :: Member (Embed IO) r => SemSocket -> Sem (WriteSocket ': r) a -> Sem r a | |
runWrite (SemSocket socket) = interpret $ \case | |
Write bs -> embed $ sendAll socket bs | |
data SocketInfo m a where | |
GetPeerName :: SocketInfo m SockAddr | |
makeSem ''SocketInfo | |
runSocketInfo :: Member (Embed IO) r => SemSocket -> Sem (SocketInfo ': r) a -> Sem r a | |
runSocketInfo (SemSocket socket) = interpret $ \case | |
GetPeerName -> embed $ S.getPeerName socket | |
data HandleSocket m a where | |
Open :: AddrInfo -> HandleSocket m SemSocket | |
Close :: SemSocket -> HandleSocket m () | |
Loop :: SemSocket -> m a -> HandleSocket m a | |
makeSem ''HandleSocket | |
runHandleSocket :: Members '[ReadSocket, WriteSocket, SocketInfo] r => Sem (HandleSocket ': r) a -> Sem (Embed IO ': r) a | |
runHandleSocket = reinterpretH $ \case | |
Close (SemSocket sock) -> do | |
embed $ gracefulClose sock 5000 | |
pureT () | |
Open addr -> do | |
x <- embed $ bracketOnError (openSocket addr) S.close $ \sock -> do | |
setSocketOption sock ReuseAddr 1 | |
withFdSocket sock setCloseOnExecIfNeeded | |
bind sock $ addrAddress addr | |
listen sock 1024 | |
pure (SemSocket sock) | |
pureT x | |
Loop (SemSocket sock) action -> do | |
x <- embed $ | |
vacuous $ | |
infinitely $ | |
bracketOnError (accept sock) (S.close . fst) $ | |
\(conn, _) -> | |
void $ do | |
let sem = SemSocket conn | |
undefined | |
pureT x |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment