2009-07-13 4 views
4

나는 국가 모나드에 대한 튜토리얼을 통해 아이디어를 얻었다 고 생각합니다. this nice tutorial에서 예를 들어Haskell에서 숨겨진 방법으로 상태를 초기화하려면 어떻게해야합니까 (PRNG처럼)?

:

*Main> runState getRandom 0 
(0,12345) 
*Main> runState getRandom 0 
(0,12345) 
*Main> runState getRandom 1    
(16838,1103527590) 

을하지만 난 여전히 PRNG에 내가 전화 할 때마다 씨앗을 전달해야

import Data.Word 

type LCGState = Word32 

lcg :: LCGState -> (Integer, LCGState) 
lcg s0 = (output, s1) 
    where s1 = 1103515245 * s0 + 12345 
     output = fromIntegral s1 * 2^16 `div` 2^32 


getRandom :: State LCGState Integer 
getRandom = get >>= \s0 -> let (x,s1) = lcg s0 
          in put s1 >> return x 

OK, 그래서 getRandom를 사용할 수 있습니다. 나는 하스켈 구현에서 사용할 수있는 PRNG가 필요하지 않음을 알고

Prelude> :module Random 
Prelude Random> randomRIO (1,6 :: Int) 
(...) -- GHC prints some stuff here 
6 
Prelude Random> randomRIO (1,6 :: Int) 
1 

그래서 아마 국가 모나드를 오해, 나는 대부분의 튜토리얼에서 볼 수있는 것을 는 "지속"상태가 될 것 같지 않기 때문에, 하지만 상태를 스레드하는 데 편리한 방법입니다.

그래서 ... 무작위 모듈 과 같이 자동으로 초기화되는 상태 (시간과 다른 예측할 수없는 데이터를 사용하는 일부 함수에서 가능)를 어떻게 가질 수 있습니까?

고맙습니다.

답변

6

randomRIOIO 모나드를 사용합니다. 통역사가 IO 모나드에서 작동하기 때문에 이것은 통역사에서 잘 작동하는 것 같습니다. 그것은 당신이 당신의 예에서보고있는 것입니다; 코드의 최상위 레벨에서 실제로 그렇게 할 수는 없습니다. 어쨌든 모든 모나드처럼 do-expression에 넣어야합니다.

일반적으로 코드는 IO 모나드를 사용하지 마십시오. 코드가 IO 모나드를 사용하기 때문에 외부 상태에 영원히 묶여 있기 때문에 빠져 나갈 수 없습니다 (즉, IO를 사용하는 코드가있는 경우). 모나드를 호출하는 코드는 IO 모나드를 사용해야하므로 안전한 방법은 없습니다. 그러므로 IO 모나드는 외부 환경에 대한 액세스, 절대적으로 필요한 작업에만 사용해야합니다.

로컬 자체 포함 상태와 같은 경우 IO 모나드를 사용하면 안됩니다. 앞에서 언급 한대로 State 모나드를 사용하거나 ST 모나드를 사용할 수 있습니다. ST 모나드에는 IO 모나드와 동일한 기능이 많이 포함되어 있습니다. 즉 STRef 가변 세포가 있으며, 이는 IORef과 유사합니다. 그리고 IO에 비해 ST에 대한 좋은 점은 수행이 끝나면 ST 모나로 runST을 호출하여 IO로 수행 할 수없는 모나드로부터 계산 결과를 얻을 수 있다는 것입니다.

상태를 "숨김"하는 것은 단지 모스에 대해 하스켈에서 do-expressions 문법의 일부로 제공됩니다. 명시 적으로 상태를 전달해야한다고 생각되면 모나드 구문을 올바르게 사용하지 않는 것입니다. 여기

는 IO 모나드에 IORef 사용하는 코드이다

여기
import Data.IORef 
foo :: IO Int -- this is stuck in the IO monad forever 
foo = do x <- newIORef 1 
     modifyIORef x (+ 2) 
     readIORef x 
-- foo is an IO computation that returns 3 

인 ST 모나드를 사용하는 코드 :

import Control.Monad.ST 
import Data.STRef 
bar :: Int 
bar = runST (do x <- newSTRef 1 
       modifySTRef x (+ 2) 
       readSTRef x) 
-- bar == 3 

코드의 단순함은 본질적으로 동일하다; 후자의 경우 모나드에서 값을 얻을 수 있고, 전자에서는 다른 IO 계산에 넣지 않고는 값을 얻을 수 없다는 점만 제외하면 말입니다.

+0

고마워요! 그것은 매우 도움이되었습니다. 귀하의 의견에 관해서는 "전자의 경우 다른 IO 계산에 넣지 않고서는 안된다"- 그 이유는 IO 모나드가 모나드 변압기를 사용하여 여러 개의 모나드를 결합 할 때 가장 내면에 있어야한다는 것입니다. – Jay

+4

내가 생각하기에, PRNG는 필연적으로 IO 모나드를 사용할 것입니다. 맞습니까? 엔트로피가 필요하므로 "어딘가"의 데이터가 필요합니다. 실제로 * 투명하게 투명하지 않은 함수를 적어도 하나 필요로합니다 (그렇지 않으면 암호화 코드와 같이 유용하지 않습니다)! :-) – Jay

4
secretStateValue :: IORef SomeType 
secretStateValue = unsafePerformIO $ newIORef initialState 
{-# NOINLINE secretStateValue #-} 

이제 IO 모나드에서 readIORef and writeIORef의 secretStateValue에 액세스하십시오.

+2

악마, 표준 '랜덤'모듈이 사용하는 것과 똑같은 속임수입니다 ... – ephemient

+1

말하자면, '랜덤'은'readIORef' +'writeIORef' 쌍 대신'atomicModifyIORef'를 사용합니다; 그렇게하는 것이 좋은 생각 인 것 같습니다. – ephemient

+2

실제로, atomicModifyIORef를 사용하여 여러 스레드에서 액세스 할 경우 더 좋은 아이디어입니다. – bdonlan

4

그래서 대부분의 자습서에서 볼 수있는 것은 "영구적"상태가 아닌 상태를 스레드하는 편리한 방법 인 것처럼 보이기 때문에 아마 상태 모나드에 대해 오해했을 것입니다.

상태 모나드는 정확하게 일부 범위를 통한 스레딩 상태입니다.

최상위 상태를 원하면 언어 외부에 있으며 전역 변수를 사용해야합니다. 이것이 어떻게 코드의 스레드 안전성을 복잡하게 할 것인가에 주목하십시오 - 그 상태는 어떻게 초기화됩니까? 그리고 언제? 그리고 스레드에 의해?

관련 문제