2015-08-31 19 views
11

ile Çalışıyorum Şu anda Bryan O'Sullivan'ın resource-pool kitaplığında çalışıyorum ve withResource işlevini genişletmeyle ilgili bir sorum var. (MonadBaseControl IO m) => Pool a -> (a -> m b) -> m b'dan (MonadBaseControl IO m) => Pool a -> (a -> m (Bool, b)) -> m b'a withResource işlevinin imzasını değiştirmek istiyorum.
Elde etmek istediğim, eylemin, havuza geri gönderilmesi veya yok edilmesi durumunda, boolean değerinin, boolean değerinin gösterileceği yerde (Bool, b) tuple dönmesidir.`MonadBaseControl` API

withResource :: forall m a b. (MonadBaseControl IO m) => Pool a -> (a -> m (Bool, b)) -> m b 
{-# SPECIALIZE withResource :: Pool a -> (a -> IO (Bool,b)) -> IO b #-} 
withResource pool act = fmap snd result 
    where 
    result :: m (Bool, b) 
    result = control $ \runInIO -> mask $ \restore -> do 
     resource <- takeResource pool 
     ret <- restore (runInIO (act resource)) `onException` 
      destroyResource pool resource 

     void . runInIO $ do 
     (keep, _) <- restoreM ret :: m (Bool, b) 

     if keep 
      then liftBaseWith . const $ putResource pool resource 
      else liftBaseWith . const $ destroyResource pool resource 

     return ret 

Ve bunu Belki MonadBaseControl API hakkı kullanmıyorum ... gibi bakmak gerekiyordu nasıl bu olmadığını, bir duygu var:

Şimdi benim şimdiki uygulama aşağıdaki gibi görünüyor. Bunun hakkında ne düşünüyorsunuz ve daha idiyotik olmak için onu nasıl geliştirebilirim?

+0

Müthiş bir bakış iyi görünüyor. Seni bu konuda ne rahatsız ediyor? – luqui

+1

@luqui Beni biraz rahatsız eden, runInIO'yu iki kez çalıştırmam gerektiğidir, bu da daha ayrıntılı kodlara neden olur. IO monad içindeki "ret" (ilk "runInIO" çağrısının sonucu) açmak için daha iyi bir yolu var mı? – bmk

cevap

2

Bu yaklaşımda temel bir sorun olduğunu hissediyorum. StM M a'un a'a eşit/izomorf olduğu monadlar için işe yarayacaktır. Ama diğer monadlar için bir problem olacak. MaybeT IO'u düşünelim. a -> MaybeT IO (Bool, b) türünde bir eylem başarısız olabilir, bu nedenle üretilen Bool değeri olmayacaktır. Ve

void . runInIO $ do 
    (keep, _) <- restoreM ret :: m (Bool, b) 
    ... 

kod denetim akışı restoreM duracaktır, yürütülmez. Ve ListT IO için daha da kötü olacak, putResource ve destroyResource birden çok kez çalıştırılacak. senin fonksiyonunun basitleştirilmiş bir versiyonudur bu örnek program, düşünün:

{-# LANGUAGE FlexibleContexts, ScopedTypeVariables, RankNTypes, TupleSections #-} 
import Control.Monad 
import Control.Monad.Trans.Control 
import Control.Monad.Trans.List 

foo :: forall m b . (MonadBaseControl IO m) => m (Bool, b) -> m b 
foo act = fmap snd result 
    where 
    result :: m (Bool, b) 
    result = control $ \runInIO -> do 
     ret <- runInIO act 

     void . runInIO $ do 
     (keep, _) <- restoreM ret :: m (Bool, b) 

     if keep 
      then liftBaseWith . const $ putStrLn "return" 
      else liftBaseWith . const $ putStrLn "destroy" 

     return ret 

main :: IO() 
main = void . runListT $ foo f 
    where 
    f = msum $ map (return . (,())) [ False, True, False, True ] 

O

destroy 
return 
destroy 
return 

basalım Ve boş liste için, hiçbir şey hiçbir temizleme denilen olacağını, yani baskılı alır senin işlevin.


Amacınıza daha iyi bir şekilde nasıl ulaşacağınızdan emin olmadığımı söylemeliyim. Ben IO() argüman çalıştırıldığında, cari kaynak geçersiz ve yok edilmesi işaretler olduğunu, bir fonksiyon nerede olacağını imza

withResource :: forall m a b. (MonadBaseControl IO m) 
      => Pool a -> (a -> IO() -> m b) -> m b 

yönünde keşfetmek için çalışacaktı. (Veya, daha fazla kolaylık sağlamak için, m() kaldırılmış olarak IO() değiştirin). Daha sonra dahili olarak,tabanlı olduğu için, işlevini çağırarak sıfırlanacak bir yardımcı MVar yaratacağım ve sonunda, değere bağlı olarak, kaynağı döndürün veya yok edin.

+0

Yardımsever cevap için çok teşekkür ederim. Şimdi 'BelkiT' ve 'ListT' monads ile sorunları görebiliyorum ... Tekrar teşekkürler! – bmk