在启动更多管道之前,我需要在与客户端和获取客户端对象或其名称字符串之间来回做一些操作。
但我似乎无法让appSink让我有一个返回值。
我该怎么做?
checkAddClient :: Server -> ClientName -> AppData -> IO (Maybe Client)
checkAddClient server@Server{..} name app = atomically $ do
clientmap <- readTVar clients
if Map.member name clientmap
then return Nothing
else do
client <- newClient name app
writeTVar clients $ Map.insert name client clientmap
return (Just client)
readName server app = go
where
go = do
yield "What is your name? "
name <- lineAsciiC $ takeCE 80 =$= filterCE (/= _cr) =$= foldC
if BS.null name
then go
else do
ok <- liftIO $ checkAddClient server name app
case ok of
Nothing -> do
yield . BS.pack $ printf "The name '%s' is in use, please choose anothern" $ BS.unpack name
go
Just client -> do
yield . BS.pack $ printf "Welcome, %s!n" $ BS.unpack name
return client -- <-- Here is the problem!!
main :: IO ()
main = do
server <- newServer
runTCPServer (serverSettings 4000 "*") $ clientApp -> do
(clientC, client) <- appSource clientApp $$+ readName server clientApp =$ appSink clientApp
更新
这是我最终得到的解决方案:
readName :: Server -> AppData -> Sink BS.ByteString IO Client
readName server app = go
where
go = do
yield "What is your name? " $$ appSink app
name <- lineAsciiC $ takeCE 80 =$= filterCE (/= _cr) =$= foldC
if BS.null name
then go
else do
ok <- liftIO $ checkAddClient server name app
case ok of
Nothing -> do
yield (BS.pack $ printf "The name '%s' is in use, please choose anothern" $ BS.unpack name) $$ appSink app
go
Just client -> do
yield (BS.pack $ printf "Welcome, %s!n" $ BS.unpack name) $$ appSink app
return client
main :: IO ()
main = do
server <- newServer
runTCPServer (serverSettings 4000 "*") $ clientC -> do
client <- appSource clientC $$ readName server clientC
print $ clientName client
这是主要管道API的限制:除了最下游的组件之外,您无法从任何东西获得结果值。有几个解决办法:
-
有一个更高级的管道API,允许捕获上游终结器。您感兴趣的函数是Upstream。请注意,这是解决问题的"正确"方法,但这种更高级的API并不是主要的方法,这是有原因的:它有六个类型参数,容易混淆人们。
-
不是将
readName
融合到appSink
,而是将appSink
传递到readName
,并在每次yield
调用时进行融合。例如:yield (BS.pack $ printf "...") $$ appSink app
这可能是简单性和类型安全性之间的最佳平衡。
-
创建一个
IORef
或其他可变变量,并将客户端的名称放入该可变变量中。