2014-07-07 15 views
13

Git dilinin, birden çok kanalı yoklamak ve ilk olarak hangi kanalın boş olmadığına bağlı olarak belirli bir eylemi gerçekleştirmek için kullanılabilecek bir select bildirimi vardır.Go'nun Haskell STM kanalları için select deyiminin karşılığı nasıl uygulanır?

E.g. chanA, chanB veya chanC ya olmayan boşalana kadar

select { 
    case a := <- chanA: 
    foo(a) 
    case b := <- chanB: 
    baz(b) 
    case c := <- chanC: 
    bar(c) 
} 

Bu da örnek chanB olmayan boş için, eğer chanB okuyacak, bekleyecek ve b sonucu depolamak, sonra baz(b) diyoruz. default: maddesinin eklenmesi de mümkündür, yani select ifadesi kanallarda beklemez ve bunun yerine tüm kanallar boşsa default tümcesini ne yaparsa yapar.

STM TChan s Haskell'de bunun gibi bir şeyin uygulanmasının en iyi yolu ne olurdu? Bir if-else zinciri tarafından naif olarak yapılabilir: her bir isEmptyChan no'lu değişkenin olup olmadığını kontrol edin ve eğer boş değilse, ondan okuma yapın ve uygun işlevi çağırın, veya tüm kanallar boşsa retry'u arayın. Bunu yapmak için daha zarif/deyimsel bir yol olup olmadığını merak ediyordum?

Go'nun select bildiriminin kendi durumlarında gönderme ifadelerini de içerebileceğini ve yalnızca kanalı boşsa bir gönderim deyimini tamamlayabileceğini unutmayın. Bu işlevsellik de çoğaltılabiliyor olsaydı harika olurdu, ancak bunun için şık bir yol olup olmayacağından emin değilim.

Sadece biraz ilgili ama bir şey şu anda fark ve bunu yayınlamak için nereye emin değilim:

"uygulaması biri kadar iplik engelleyebilir: retry açıklama alanına Control.Monad.STM sayfasında bir yazım hatası var Okumak TVars, udpated olmuştur. "

+1

Control.Concurrent.Async 'den as. –

+4

Gitmenin, mevcut ilk işlemi gerçekleştirmediğini, ancak rastgele seçilen herhangi birini kullanabileceğini unutmayın. Özellikle kanalları açmaz, çünkü seçim yolunda daha sonra veya şanssız olarak tanımlanırlar. – Dustin

+0

Bu, Go'nun 'select' seçeneğinden tamamen farklıdır. Go'daki kanallar, "TChan" ın aksine (aslında onları yararlı kılar) ve "select" komutları gönderim işlemleriyle kullanılabilir. – rightfold

cevap

5

Açlık

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 
    ] 

sonsuza önlemek için kaçınarak, bunun yerine bir mkSelect :: [STM()] -> STM (STM()) olduğunu "gizler" yaratabileceği bir TVAR [STM()] ve her döndüren o gibi kullanılır:

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 

Bu tekniği genişleterek, bir eylemi gerçekleştirdiyse veya hangi eylemin gerçekleştirileceğini veya hatta tüm eylemler engellenene kadar bile döngü oluşturduğunu bildiren bir seçime sahip olabilirsiniz.

11

Sen orElse kullanarak (okuma ve yazma için her ikisi de) select anlambilim uygulayabilirsiniz (not: bu ghc özeldir.) Örneğin :

forever $ atomically $ 
    writeTChan chan1 "hello" `orElse` writeTChan chan2 "world" `orElse` ... 

fikir olduğunu ne zaman bir eylem yeniden deneme (örneğin chan yazıyorsun, ama dolu ya da chan okuyorsun, ama boş), ikinci eylem gerçekleştiriliyor. default bildirimi, zincirdeki son eylem olarak sadece return() şeklindedir.

Ekleme: @ @Dustin kaydetti, git iyi bir nedenle rastgele bir dal seçer. Muhtemelen en kolay çözüm, her yinelemede hareketleri karıştırmaktır, çoğu durumda iyi olmalıdır. Tekrarlanan semantikleri çoğaltmak (sadece aktif dalları karıştırmak) biraz daha zordur. Muhtemelen tüm şubeler için isEmptyChan el ile denetleme yolu gitmektir.

+3

Dustin'in yukarıda belirttiği gibi bu, açlıktan dolayı sorun yaşamayacak mı? – Dan

+3

@Dan Doğru olduğuna inanıyorum: (Yuras'ın örneğini 'readTChan' kullanıyorduk) 'readTChan chanN' tüm chans <' N' boş iken okunabilirdi. Yani açlık olasılığı, ilk başta beklediğinizden daha da kötüdür. – jberryman

+0

@Dan Haklısınız, bir not ekledim. – Yuras