如何在运行导管后获得价值? [英] How can I get a value after running a conduit?
问题描述
但我可以在客户端之间来回切换,并在启动更多管道之前获取Client对象或其名称字符串。 t似乎得到appSink让我有一个返回值。
我该怎么做?
checkAddClient :: Server - >客户名称 - > AppData - > IO(也许客户端)
checkAddClient服务器@服务器{..}名称app =原子地$ do
clientmap< - readTVar客户端
如果Map.member名称clientmap
然后返回Nothing
else else
client < - newClient name app
writeTVar clients $ Map.insert name client clientmap
return(Just client)
readName server app = go
where
go = do
yield你叫什么名字?
name< - lineAsciiC $ takeCE 80 = $ = filterCE(/ = _cr )= $ = foldC
如果BS.null名称
然后转到
else else
ok< - liftIO $ checkAddClient服务器名称应用程序
确定
没有 - >做
收益。 BS.pack $ printf名称'%s'正在使用中,请选择另一个\\\
$ BS.unpack名称
去
只需客户端 - >做
收益。 BS.pack $ printfWelcome,%s!\\\
$ BS.unpack name
return client - < - 这是问题!
$ b $ main :: IO()
main = do
服务器< - newServer
runTCPServer(serverSettings 4000*)$ \clientApp - >
(clientC,client)< - appSource clientApp $$ + readName server clientApp = $ appSink clientApp
UPDATE
以下是我最终得出的解决方案: class =lang-hs prettyprint-override> readName :: Server - > AppData - > Sink BS.ByteString IO Client
readName server app = go
where
go = do
yield你叫什么名字?$$ appSink app
name< - lineAsciiC $ takeCE 80 = $ = filterCE(/ = _cr)= $ = foldC
如果BS.null名称
然后去
else else
ok< - liftIO $ checkAddClient服务器名称应用程序
确定
没有 - >做
收益(BS.pack $ printf名称'%s'正在使用中,请选择另一个\\\
$ BS.unpack名称)$$ appSink app
go
只需客户端 - >
yield(BS.pack $ printfWelcome,%s!\\\
$ BS.unpack name)$$ appSink app
return client
main :: IO()
main = do
server< - newServer
runTCPServer(serverSettings 4000*)$ \clientC - >
客户端< - appSource客户端C $$读取名称服务器客户端C
打印$客户端客户端
这是主要API的局限性:除了最下游的组件外,您无法从其他任何结果中获取结果值。有几种解决方法:
-
有一个更高级的管道API,它可以捕获上游终结器。您感兴趣的功能是 withUpstream 。请注意,这是解决问题的正确方法,但这种更高级的API并不是主要API的原因:它有六个类型参数,并且容易让人们感到困惑。
$ b -
不是将
readName
与appSink
合并,而是将appSink
转换为readName
,并在每个产生
调用时将其熔合。例如:
yield(BS.pack $ printf...)$$ appSink app
这可能是简单性和类型安全性之间的最佳平衡。
- <创建一个
IORef
或其他可变变量,并将客户名称放入该可变变量中。
I need to do a little back and forth between with client and get either the Client object or their name string before starting up more pipelines.
But I can't seem to get appSink to let me have a return value.
How should I do this?
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 another\n" $ 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
UPDATE
Here is the solution I ended up with:
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 another\n" $ 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
This is a limitation of the primary conduit API: you're not able to get the result value from anything but the most downstream component. There are a few workarounds:
There's a more advanced conduit API which does allow capturing upstream finalizers. The function you'd be interested in is withUpstream. Note that this is the "right" approach to the problem, but there's a reason this more advanced API isn't the primary one: it has six type parameters, and tends to confuse people.
Instead of fusing
readName
toappSink
, passappSink
intoreadName
and fuse it at eachyield
call. E.g.:yield (BS.pack $ printf "...") $$ appSink app
This is probably the best balance between simplicity and type safety.
Create an
IORef
or other mutable variable, and put the client's name into that mutable variable.
这篇关于如何在运行导管后获得价值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!