2012-12-31 22 views
7

Tôi hiện đang làm việc với các tính toán trong ReaderT r (Rand StdGen) a mà tôi muốn chạy song song. Tôi đã đi qua Monad Parallel mà có vẻ như nó sẽ làm những gì tôi muốn.MonadParallel Instance cho Rand

Đã có bản sao của MonadParallel cho ReaderT nhưng tôi phải tạo của riêng mình cho Rand từ monad-random. Tuy nhiên, tôi không chắc là tôi đã làm đúng. Tôi không quá quen thuộc với lập trình song song trong Haskell, nhưng tôi tin rằng có một kỳ vọng rằng việc chạy các định dạng trong song song sẽ cho cùng một giá trị như khi chúng chạy bình thường. Bởi vì thể hiện của tôi về bindM2 cho Rand sử dụng split (và do đó nhận được một tập hợp các số ngẫu nhiên khác từ cùng một trình tạo ban đầu), đây không phải là trường hợp của cá thể của tôi.

instance P.MonadParallel (Rand StdGen) where 
    bindM2 f ma mb = do 
     split1 <- getSplit 
     split2 <- getSplit 
     let a = evalRand ma split1 
     let b = evalRand mb split2 
     a `par` b `pseq` f a b 

Trong khi tôi cảm thấy có trường hợp bỏ qua điều này (các con số vẫn ngẫu nhiên, phải không?) Tôi cũng không thể cảm thấy rằng mình đang thiếu thứ gì đó. Điều này có ổn không hoặc có giải pháp tốt hơn không?

Trả lời

2

mối quan tâm chính của tôi là điều này vi phạm đảm bảo rằng:

Ngoài trật tự có thể xảy ra phản ứng phụ, chức năng này tương đương với \f ma mb-> do {a <- ma; b <- mb; f a b}

Những điều này sẽ có kết quả khác nhau:

let g = mkStdGen 0 
    r = evalRand (do x <- getRandom 
        y <- getRandom 
        return (x, y)) g 

vs

let g = mkStdGen 0 
    r = evalRand (P.bindM2 (\x y -> return (x,y)) getRandom getRandom) g 

Điều này làm tăng các câu hỏi thú vị về split, số giả ngẫu nhiên và bản chất của các số ngẫu nhiên liên quan đến độ tinh khiết. Thật khó cho tôi để tưởng tượng một trường hợp mà trường hợp của bạn sẽ có ảnh hưởng xấu, nhưng sự phức tạp của các hệ thống phần mềm chưa bao giờ làm tôi ngạc nhiên. Và vì lý do đó, tôi sẽ không vi phạm một kỳ vọng về bindM2, ngay cả khi nó có số ngẫu nhiên.

+0

Đây là một câu hỏi thú vị - có thể không giải quyết được mà không suy nghĩ lại cách phân chia hoạt động. Như bạn đã nói, trong trường hợp của tôi nó không chính xác là một mối quan tâm nhưng nó chắc chắn làm việc giữ một mắt trên. –

1

Có vấn đề cố hữu trong MonadParallel 's bindM2. Tài liệu của nó cho biết:

Thực hiện hai phép tính đơn điệu song song; khi cả hai kết thúc, chuyển kết quả đến hàm. Ngoài trật tự có thể xảy ra phản ứng phụ, chức năng này là tương đương với \f ma mb-> do {a <- ma; b <- mb; f a b}

Vấn đề là trong tính toán monadic (không giống như functors applicative) giá trị có thể phụ thuộc vào hiệu ứng, và trên đặt hàng của họ quá. Vì vậy, yêu cầu này không có ý nghĩa nhiều. Cân nhắc

do 
    a <- getCurrentTime -- from Date.Time 
    b <- getCurrentTime 
    return (a <= b) 

này trả luôn True, nhưng nếu bạn sắp xếp lại các hiệu ứng, nó sẽ bắt đầu quay trở lại False. Rõ ràng bằng cách song song hai phép tính, bạn sẽ nhận được một kết quả rất không xác định.

Vì vậy, thật khó để diễn giải ý định của bindM2.Tôi muốn nói chúng ta có thể chia các trường hợp MonadParallel thành hai loại:

  1. Những người đang thực sự xác định và mà bindM2 luôn bằng \f ma mb-> do {a <- ma; b <- mb; f a b}. Đó là, thứ tự của các hiệu ứng không thay đổi. Những triển khai thường được định nghĩa bằng cách sử dụng par, không thay đổi ngữ nghĩa của chương trình, chỉ chạy một số phần song song.
  2. Những điều đó phụ thuộc vào yêu cầu có hiệu lực, do đó, bindM2 có thể khác nhau tùy ý từ \f ma mb-> do {a <- ma; b <- mb; f a b}. Dường như hiện tại trường hợp duy nhất là IO, sử dụng forkIO để sinh ra một luồng mới cho một trong các tính toán.

Vì vậy, cho dù bạn chấp nhận bindM2 làm trường hợp hợp lệ hay không phụ thuộc vào cách bạn giải thích tài liệu. Nếu bạn xem xét (2.) là không hợp lệ thì việc triển khai của bạn cũng không hợp lệ (và bạn cũng nên từ chối IO). Nếu bạn giải thích (2.) là hợp lệ, thì bạn không phải lo lắng.

+0

Vấn đề chính ở đây thực sự là sử dụng 'split' để xác định cá thể của tôi cho Rand. Nó vẫn còn xác định (theo như tôi có thể nói) _but_ nó tạo ra một kết quả khác so với phiên bản tuần tự vì cách 'split' hoạt động. Tuy nhiên, xem xét trường hợp mặc định cho IO, bạn có xem xét việc sử dụng MonadParallel thường không an toàn không? –

+0

@TomSavage Tôi hiểu. An toàn hoặc không an toàn phụ thuộc vào những gì bạn mong đợi (hoặc những người sử dụng mã của bạn) có. Tôi không nghĩ rằng 'MonadRandom' là không an toàn nói chung, nó chỉ là id không rõ ràng những gì 'bindM2' nên tuân theo. Nó bắt đầu không an toàn khi ai đó hy vọng điều gì đó không đúng. Có lẽ cách tiếp cận an toàn nhất là tạo ra đơn nguyên của riêng bạn (hoặc thậm chí là biến đổi đơn nguyên) phù hợp đặc biệt cho các phép tính ngẫu nhiên song song và ghi lại hành vi chính xác của nó. IMHO này sẽ là một thư viện tốt đẹp và hữu ích. (Bạn có thể sử dụng 'MonadRandom' trong nội bộ để thực hiện nó.) –

+0

@TomSavage Nếu bạn sử dụng' IO' của 'MonadRandom', gọi' bindM2' sẽ có kết quả khác thường hơn gọi các hành động tuần tự. Vì vậy, ví dụ của 'Rand StdGen' về cơ bản là chính xác như' IO'. –