2014-07-24 15 views
6

Ich möchte zufällige Berechnungen in Haskell mit einem Timeout unter Verwendung der Control.Monad.Random-Bibliothek auswerten. Die folgenden funktioniert gut:Haskell - Timing-Out-Berechnungen in der Rand-Monade

ghci> import System.Timeout 
ghci> import Control.Monad.Random 
ghci> timeout 1000 . evalRandIO $ getRandomR (True, False) 
Just True 

Doch dieser Ansatz scheint nicht zu funktionieren, wenn wir eine Berechnung des Typs Rand StdGen a haben, die nie (das heißt auswertet nach unten) endet. Zum Beispiel:

ghci> let f = f :: Rand StdGen Bool 
ghci> timeout 1000 $ evalRandIO f 
Just 

Hier GHCI druckt "Just" und dann auf unbestimmte Zeit hängt, versucht f zu bewerten. Kann jemand, der mehr über die Haskell-Laufzeit weiß als ich, erklären, warum dies geschieht und wie man es umgehen kann? Meine Vermutung ist, dass der Ausdruck evalRandIO f zu WHNF ausgewertet wird und so timeout 10 denkt, dass die Berechnung beendet wird, aber ich habe wirklich keine Ahnung.

Antwort

5

Dies könnte mehr Sinn machen, wenn Sie so etwas wie

> Just x <- timeout 1000 $ evalRandIO f 
> :t x 
x :: Bool 
> x 
Interrupted. 

Die Berechnung selbst ist abgeschlossen, es WHNF ist nämlich zu tun waren erreicht, fangen sie timeout so nicht. Die timeout 1000-Funktion selbst wird vervollständigt und gibt Just undefined zurück. Ein Beispiel, wo Sie timeout bekommen wäre eine Boden Auswertung zu fangen

> import Control.DeepSeq 
> :set +m -- Multiline mode 
> let f :: Bool 
|  f = f 
| 
> timeout 1000000 $ deepseq f (return f) 
Nothing 

Sie werden sehen, dass es für eine Sekunde hängt, dann kehrt Nothing wenn deepseq nicht f so nicht beendet Auswertung es return f durchführen kann.

Also ja, Ihr Problem stammt aus der Tatsache, dass f = f zu WHNF statt NF ausgewertet wird. Um NF zu erzwingen, müssen Sie etwas wie deepseq verwenden. Ein anderes möglicherweise einfacheres Beispiel wäre, einfach den Operator $! zu verwenden, wie zum Beispiel:

> let f :: Rand StdGen Bool 
|  f = f 
| 
> timeout 1000000 $ evalRandIO $! f 
Nothing 
+0

Vielen Dank! Ich muss deutlich über seq/deepseq und die Haskell-Laufzeit lernen. – user3873438

+0

@ user3873438 Um fair zu sein, musste ich eine GHCi-Sitzung laden, um es selbst herauszufinden. Faulheit verursacht einige interessante Probleme, aber die meiste Zeit müssen Sie nicht einmal darüber nachdenken. Wenn Sie Probleme haben, die in einer bestimmten Reihenfolge passieren müssen, müssen Sie sich erst einmal Gedanken über die Striktheit machen. In diesem Fall sollte evalRandIO f vor dem Ende von timeout 1000 auftreten. Bei einer solchen Beziehung ist es wichtig, explizit auf Strenge hinzuweisen, was 'deepseq' und' $! 'Tun. – bheklilr

+0

Sie müssen auch über Faulheit für Raumlecks und Parallelität sorgen. – PyRulez