如何在运行导管后获得价值? [英] How can I get a value after running a conduit?

查看:129
本文介绍了如何在运行导管后获得价值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



但我可以在客户端之间来回切换,并在启动更多管道之前获取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的局限性:除了最下游的组件外,您无法从其他任何结果中获取结果值。有几种解决方法:


  1. 有一个更高级的管道API,它可以捕获上游终结器。您感兴趣的功能是 withUpstream 。请注意,这是解决问题的正确方法,但这种更高级的API并不是主要API的原因:它有六个类型参数,并且容易让人们感到困惑。

  2. $ b
  3. 不是将 readName appSink 合并,而是将 appSink 转换为 readName ,并在每个产生调用时将其熔合。例如:

      yield(BS.pack $ printf...)$$ appSink app 

    这可能是简单性和类型安全性之间的最佳平衡。


  4. <创建一个 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:

  1. 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.

  2. Instead of fusing readName to appSink, pass appSink into readName and fuse it at each yield call. E.g.:

    yield (BS.pack $ printf "...") $$ appSink app
    

    This is probably the best balance between simplicity and type safety.

  3. Create an IORef or other mutable variable, and put the client's name into that mutable variable.

这篇关于如何在运行导管后获得价值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆