Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何为Haskell STM通道实现Go的select语句的等效项?_Haskell_Select_Channel_Stm - Fatal编程技术网

如何为Haskell STM通道实现Go的select语句的等效项?

如何为Haskell STM通道实现Go的select语句的等效项?,haskell,select,channel,stm,Haskell,Select,Channel,Stm,Go语言有一个select语句,可用于轮询多个通道,并根据哪个通道首先非空来执行特定操作 例如 这将一直等到chanA、chanB或chanC为非空,然后如果例如chanB为非空,它将从chanB读取并将结果存储在b中,然后调用bazb。还可以添加一个default:子句,这意味着select语句不会在通道上等待,而是在所有通道都为空时执行default子句的任何操作 对于Haskell中的STM TChans,最好的实现方式是什么?它可以通过if-else链天真地完成:检查每个chan是否为空

Go语言有一个select语句,可用于轮询多个通道,并根据哪个通道首先非空来执行特定操作

例如

这将一直等到chanA、chanB或chanC为非空,然后如果例如chanB为非空,它将从chanB读取并将结果存储在b中,然后调用bazb。还可以添加一个default:子句,这意味着select语句不会在通道上等待,而是在所有通道都为空时执行default子句的任何操作

对于Haskell中的STM TChans,最好的实现方式是什么?它可以通过if-else链天真地完成:检查每个chan是否为空,如果它不是空的,则从中读取并调用相应的函数,或者如果所有通道都为空,则调用retry。我想知道是否有更优雅/惯用的方法来实现这一点

请注意,Go的select语句也可以在其大小写中包含send语句,并且只有在其通道为空时才会完成send语句。如果该功能也能被复制,那就太好了,尽管我不确定是否有一种优雅的方法可以做到这一点

只是有点关联,但我刚刚注意到了一点,我不确定该在哪里发布:页面上的“重试说明”中有一个拼写错误:

该实现可能会阻止线程,直到它从中读取的TVAR之一已被提取。

您可以使用注释实现读和写的选择语义:它特定于ghc。 例如:

forever $ atomically $
  writeTChan chan1 "hello" `orElse` writeTChan chan2 "world" `orElse` ...
想法是,当一个动作重试时,例如,你正在给成龙写信,但它已满;或者你正在读成龙的书,但它是空的,第二个动作被执行。 默认语句只是作为链中最后一个操作的返回

加: 正如@Dustin所指出的,go选择随机分支是有充分理由的。可能最简单的解决方案是在每次迭代中洗牌操作,在大多数情况下应该是可以的。复制go语义只适当地洗牌活动分支有点困难。可能手动检查所有分支的isEmptyChan是一种方法。

避免饥饿

foreverK :: (a -> m a) -> a -> m ()
foreverK loop = go
 where go = loop >=> go

-- Existential, not really required, but feels more like the Go version
data ChanAct = Action (TChan a) (a -> STM ())

perform :: STM ()
perform (Action c a) = readTChan c >>= a

foreverSelectE :: [ChanAct] -> STM ()
foreverSelectE = foreverSelect . map perform

foreverSelect :: [STM ()] -> STM ()
foreverSelect = foreverK $ \xs -> first xs >> return (rotate1 xs)

-- Should only be defined for non-empty sequences, but return () is an okay default.
-- Will NOT block the thread, but might do nothing.
first :: [STM ()] -> STM ()
first = foldr orElse (return ())

-- Should only be defined for non-empty sequences, really.
-- Also, using a list with O(1) viewL and snoc could be better.
rotate1 :: [a] -> [a]
rotate1 []    = []
rotate1 (h:t) = t ++ [h]

example = foreverSelectE
    [ Action chanA foo
    , Action charB baz
    , Action chanC bar
    ]
为了避免永久,您可以使用mkSelect::[STM]->STM STM STM来隐藏TVar[STM],并在每次使用时将其旋转,如下所示:

example1 :: STM ()
example1 = do
    select <- mkSelect [actions] -- Just set-up
    stuff1
    select -- does one of the actions
    stuff2
    select -- does one of the actions

main = OpenGL.idleCallback $= atomically example1

扩展该技术,您可以有一个select来报告它是否执行了一个操作或它执行了哪个操作,甚至循环,直到所有操作都将被阻止,等等。

您可能希望查看Control.Concurrent.Async中的race。值得注意的是,go不会执行第一个可用的操作,而是任何一个可用的操作,随机抽取。它不会仅仅因为通道在选择路径中定义得晚或不走运而导致通道饥饿。这与Go的选择完全不同。Go中的通道是有界的,不像TChan使它们变得实际有用,select可以用于发送操作。但是,正如Dustin在上面评论的那样,这不会有饥饿的问题吗?@Dan我相信你是对的:假设Yuras的示例使用readTChan readTChan chanN,则只有当所有通道example1 :: STM () example1 = do select <- mkSelect [actions] -- Just set-up stuff1 select -- does one of the actions stuff2 select -- does one of the actions main = OpenGL.idleCallback $= atomically example1