2015-08-31 5 views
11

ich zur Zeit mit dem Bryan O'Sullivan resource-pool Bibliothek spielte und haben eine Frage in Bezug auf die withResource Funktion erstreckt. Ich möchte die Signatur der withResource Funktion (MonadBaseControl IO m) => Pool a -> (a -> m b) -> m b-(MonadBaseControl IO m) => Pool a -> (a -> m (Bool, b)) -> m b ändern.
Was ich erreichen möchte ist, dass die Aktion (Bool, b) Tupel zurückgeben sollte, wo der Booleschen Wert zeigt an, ob die geliehene Ressource sollte wieder in den Pool oder zerstört gestellt werden.Arbeiten mit dem `MonadBaseControl` API

Jetzt ist meine aktuelle Implementierung sieht wie folgt aus:

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 

Und ich habe das Gefühl, dass dies nicht der Fall ist, wie es soll aussehen ... Vielleicht bin ich nicht die MonadBaseControl API direkt verwenden. Was denkst du darüber und wie kann ich es verbessern, um idiomatischer zu werden?

+0

Ein flüchtiger Blick sieht gut aus. Was stört dich daran? – luqui

+1

@luqui Was mich ein bisschen stört ist, dass ich 'runInIO' zweimal ausführen muss, was zu einem ausführlicheren Code führt. Gibt es eine bessere Möglichkeit, das "ret" (das Ergebnis des ersten 'runInIO'-Aufrufs) in der IO-Monade auszupacken? – bmk

Antwort

2

Ich habe das Gefühl, dass es ein grundsätzliches Problem bei diesem Ansatz ist. Für Monaden, für die StM M a gleich/isomorph zu a ist, wird es funktionieren. Aber für andere Monaden wird es ein Problem geben. Betrachten wir MaybeT IO. Eine Aktion vom Typ a -> MaybeT IO (Bool, b) kann fehlschlagen, daher wird kein Bool Wert erzeugt. Und der Code in

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

wird nicht ausgeführt, der Steuerablauf bei restoreM stoppen. Und für ListT IO wird es noch schlimmer, wie putResource und destroyResource werden mehrmals ausgeführt werden. Betrachten Sie dieses Beispielprogramm, das eine vereinfachte Version Ihrer Funktion ist:

{-# 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 ] 

Es

destroy 
return 
destroy 
return 

Und für eine leere Liste drucken wird, wird nichts gedruckt, was bedeutet, würde keine Bereinigung in aufgerufen werden deine Funktion.


Ich muss sagen, ich bin mir nicht sicher, wie Sie Ihr Ziel besser erreichen können. Ich würde versuchen, in Richtung der Unterschrift

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

zu erkunden, wo das IO() Argument eine Funktion wäre, dass wenn sie ausgeführt werden, die aktuelle Ressource ungültig und markiert sie vernichtet werden. (Alternativ können Sie IO() durch gehobenes m() ersetzen). Dann intern, wie es IO -Basis ist, würde ich schaffen nur einen Helfer MVar, die durch den Aufruf die Funktion zurückgesetzt werden würde, und am Ende, basierend auf dem Wert, entweder zurückgeben oder die Ressource zerstören.

+0

Vielen Dank für die hilfreiche Antwort. Ich kann jetzt die Probleme mit 'MaybeT' und 'ListT' Monaden sehen ... Danke nochmal! – bmk