2011-05-05 5 views
24

여기는 생각하기에 좋은 음식입니다.모나드 계산에서 주문 제약 조건을 풀어 라

모나드 코드를 작성할 때 모나드는 수행 된 작업에 대해 순서를 지정합니다. 예를 들어, 나는 IO 모나드에서 작성하는 경우 :

do a <- doSomething 
    b <- doSomethingElse 
    return (a + b) 

나는 doSomethingdoSomethingElse 전에 실행됩니다 알고있다.

지금, C와 같은 언어에 해당하는 코드를 살펴

return (doSomething() + doSomethingElse()); 

C의 의미는 실제로이 두 함수 호출이 평가됩니다 주문을 지정하지 않기 때문에 컴파일러는 일을 자유롭게 이동할 수 주위에 기쁘게.

내 질문은 하스켈에이 평가 순서를 정의하지 않은 모나 딕 코드를 쓰려면 어떻게해야합니까? 이상적으로, 필자는 컴파일러의 옵티마이 저가 코드를보고 주위를 움직이기 시작할 때 몇 가지 이점을 얻을 것입니다. 여기

작업이 수행되지 않는 몇 가지 가능한 기술이다,하지만 바로 "정신"에 있습니다

  • functorial 스타일의 코드를 쓰기 즉, plus doSomething doSomethingElse를 작성하고 plus을 할 수있다 모나드 호출을 예약하십시오. 단점 : 모나드 작업 결과에 대한 공유를 잃게되고 plus은 평가가 끝나는 시점에 대한 결정을 내립니다.
  • 지연된 메시지, 즉 unsafeInterleaveIO을 사용하면 느린 평가 요구에 따라 일정을 지연시킬 수 있습니다. 그러나 게으른 것은 정의되지 않은 명령과 엄격한 차이가 있습니다. 특히 모든 모나드 액션이 실행되기를 바랍니다.
  • 지연 인수 (Lazy IO)를 결합하여 모든 인수를 즉시 확인합니다. 특히 seq은 순서 지정 제한을 부과하지 않습니다.

이 의미에서 나는 모나드 순서보다 유연하지만 풀 아웃 게으름보다 유연함을 원합니다.

+0

왜 모나드가되어야합니까? 왜 정의되지 않은 주문을 시행해야합니까? – x13n

+0

그렇지 않습니다. 정의되지 않은 순서는 최적화 관점에서 가장 바람직합니다. –

+0

게으른 주 또는 ST 모나드를 사용하면 대략 어떻게됩니까? – hammar

답변

16

오버 sequentializing 모나드 코드의이 문제는 "commutative monads problem"로 알려져 있습니다.

do b <- g y 
    a <- f x 
    m a b 
:

do a <- f x 
    b <- g y 
    m a b 

과 같이 동일합니다 : 다음 코드 경우

가환 모나드는 행동의 순서 (그들은 통근) 차이가 없습니다하는 모나드이다, 즉

통근하는 모나드가 많습니다 (예 : Maybe, Random). 모나드가 교환 가능하다면, 모나드 내에 캡처 된 연산을 병렬로 계산할 수 있습니다. 그들은 매우 유용합니다!

그러나, 우리는 a lot of people have asked하지만 그런 일을 위해, 통근 모나드에 대한 좋은 구문이없는 - 그것은 여전히 ​​open research problem입니다.

펑 터터는 계산 순서를 자유롭게 지정할 수 있지만, 이라는 개념을 포기해야합니다 (예 : liftM2과 같은 제안 사항).

+1

'어쩌면'은 내게 쓸데없는 것 같지 않습니다.'okay = do x <- Nothing; y <- 정의되지 않음; x'를 반환하고,'bad = do y <- undefined; x <- 아무것도; return x'. 아마 그것은 더 제한적인 의미로 통근합니까? – acfoltzer

+2

그들은 통근 통학합니다. 그들이 설명하는 효과. 예외가 있거나 비 종결 또는 모나드에 의해 추적되지 않는 기타 관찰 가능한 효과가있는 경우 운이 없게됩니다. –

+1

@DonStewart Tomas Petricek의 블로그 포스트에 대한 링크가 죽었습니다. – Jubobs

3

C의 의미는이 두 함수 호출이 평가되는 순서를 실제로 지정하지 않으므로 컴파일러는 원하는대로 주변을 자유롭게 이동할 수 있습니다.

그러나 doSomething() 경우

doSomethingElse()의 동작을 변경하는 부작용의 원인은? 컴파일러가 명령을 엉망으로 만들고 싶습니까? (힌트 : 아니오) 당신이 모나드에 있다는 사실은 그러한 경우가 있음을 시사합니다. 귀하가 "결과에 대한 공유를 잃어 버렸다"는 사실은 또한 이것을 제안합니다.

그러나 모나 딕은 항상 시퀀스 된 것은 아닙니다. 그것은 정확히 당신이 묘사하는 것이 아니지만, 당신은 당신의 행동을 병렬로 실행할 수있는 Par monad에 관심이있을 것입니다.

주문을 정의하지 않은 상태로두면 컴파일러가 마술처럼 최적화 할 수 있습니다. 아마도 대신에 당신은 (일부 필연적으로 다른 것들보다 먼저 일어날 필요가있는) 의존성을 나타 내기 위해 Par 모나드와 같은 것을 사용하고 나머지는 병렬로 실행해야합니다.

사이드 노트 : 하스켈의 return이 같은 것을 할 혼동하지 마십시오 C의 return

9

이것은 심하게 더러운 해킹이지만 내게 속임수를 쓰는 것처럼 보입니다. 흐름 문제 (즉, 예외)뿐만 아니라 제어에 관해서

{-# OPTIONS_GHC -fglasgow-exts #-} 
{-# LANGUAGE MagicHash #-} 
module Unorder where 
import GHC.Types 

unorder :: IO a -> IO b -> IO (a, b) 
unorder (IO f) (IO g) = IO $ \rw# -> 
      let (# _, x #) = f rw# 
       (# _, y #) = g rw# 
      in (# rw# , (x,y) #) 

이 컴파일러의 손에 비 결정 성을두고 있기 때문에,이 "제대로"행동해야 (즉 nondeterministically). 한편

, 우리는 우리가 정말 RealWorld 토큰과 장난을 통해 이용할 수있는 거리에 무시 무시한 행동에 의존하고 있기 때문에 같은 StateEither a 등 대부분의 표준 모나드 같은 트릭을 당겨 수 없습니다. 올바른 동작을 얻으려면 최적화 도구에서 사용할 수있는 주석이 필요합니다.이 주석은 비 동등한 두 가지 옵션 중 비 결정적 선택을 통해 문제가 없다고 나타냅니다.

+3

ㅎ, 꽤 환상적입니다. 자, 안전하게 사용할 수있을 때 특성을 정하십시오. –

+0

여기서는 전문적인 지식을 주장하지 않겠지 만 이것은 상당히 안전하며 본질적으로 원하는 의미를 부여한다고 생각합니다. 엄격한 튜플의 조건은 우리가 원하는 순서 보장을 제공해야하지만 let 절의 구조는 우리가하지 않는 순서 조건을 피해야합니다. – sclv

+1

나는 이것을 얻지 못한다. 이 코드가하는 일에 대한 자세한 설명은 많은 도움이 될 것입니다. – user239558