Xin chào folks,Tạo monads analoguous cho IO đơn nguyên với nhà nước xích
Tôi khá mới để Haskell một lần nữa trong năm nay (sau khi sử dụng nó trong đầu những năm 1990 và sau đó một lần nữa vào đầu những năm 00). Tôi đang cố gắng để viết một số mã có sử dụng một mô hình được analoguous gần như trực tiếp đến example IO monad shown on the Haskell Wiki:
type IO a = RealWorld -> (a, RealWorld)
(Vâng, tôi biết đây không phải là việc thực hiện GHC của IO, nhưng chỉ đơn thuần là một phương tiện để hiểu biết nó .) Lý do là, trong ứng dụng của tôi (một trò chơi), bây giờ tôi có hai mẫu làm điều này với hai thay thế khác nhau của RealWorld
tại đây. Trong một, đó là trạng thái của trò chơi, và mặt khác, nó chỉ là một hạt giống số ngẫu nhiên StdGen
. Tôi tất nhiên bây giờ có hai cặp loại như thế này:
-- | Easily return a specified value as well as the new random number generator
type ReturnRNG a = (a, StdGen)
-- | Take an RNG and return it and another value.
-- (This is basically like the IO type but with the StdGen instead of RealWorld.)
type WithRNG a = StdGen -> ReturnRNG a
-- | Easily return a specified value as well as the new GameState
type ReturnGS a = (a, GameState)
-- | Use a GameState and return a value with the updated GameState.
-- (This is like IO.)
type WithGS a = GameState -> ReturnGS a
(Vâng, tôi có thể tóm tắt chúng thành một cặp với hai tham số nhưng tôi đã không nhận xung quanh nó.) Bạn có thể thấy, tất nhiên, rằng các loại WithGS a
và WithRNG a
của tôi (loại từ đồng nghĩa) chính xác tương tự như IO a
ở trên.
Vì vậy, đây là một ví dụ đơn giản của mã làm việc thực tế mà tôi bây giờ có:
-- | Returns a random position for the given size.
randomPos :: (Int, Int) --^The size
-> WithRNG (Int, Int) --^The result (0 up to 1 less than the size) and new RNG seed
randomPos (w, h) r0 = ((x, y), r2)
where
(x, r1) = randomR (0, w - 1) r0
(y, r2) = randomR (0, h - 1) r1
Điều này tạo ra một cặp ngẫu nhiên trong một phạm vi nhất định, và trả về hạt RNG thức. Một tỷ lệ lớn các phương pháp của tôi là như thế này (sử dụng WithRNG
hoặc WithGS
), sử dụng trạng thái xích, đôi khi thậm chí nhận được tối đa r4
hoặc r6
(hoặc gs4
, v.v.). Tôi muốn viết ví dụ này giống như thế này ...
-- (Not working example)
randomPosSt (w, h) = do
x <- randomR (0, w - 1)
y <- randomR (0, h - 1)
return (x, y)
... nhưng có chữ ký và ngữ nghĩa chính xác giống nhau. Điều này có vẻ như nó nên có thể sau khi hướng dẫn nói trên mang đến cho ví dụ này:
(>>=) :: IO a -> (a -> IO b) -> IO b
(action1 >>= action2) world0 =
let (a, world1) = action1 world0
(b, world2) = action2 a world1
in (b, world2)
này, như bạn có thể thấy, gần như là chính xác những gì tôi đang làm ở trên (một khi bạn thay thế "let
" cho "where
" ký hiệu).
Tuy nhiên, tôi không thể tạo một Monad ra khỏi một từ đồng nghĩa loại. (Tôi đã thử TypeSynonymInstances nhưng nó không có vẻ làm việc với một trong hai "instance Monad WithRNG where
" hoặc sử dụng một tham số. Sử dụng một newtype
cũng dường như thêm cú pháp xấu xí vô dụng.) Tôi đã không thể tìm ra nhà nước Monad tốt đủ để thực hiện một phương pháp tương đương bằng cách sử dụng một trong hai. Mặc dù tôi đã thành công, tuy nhiên, việc triển khai Tiểu bang dường như sử dụng xấu xí "get
" và "put
" s (và "runState
" vv) và làm cho mã ít hơn có thể đọc được, không nhiều hơn.
-- THIS DOES NOT WORK
-- | Make a State Monad with random number generator - like WithRNG above
type RandomState = State StdGen
-- | Returns a random position for the given size.
randomPosSt :: (Int, Int) --^The size
-> RandomState (Int, Int) --^The result (0 up to 1 less than the size) and new RNG seed
Sau tất cả điều này, tôi đã kết luận rằng tôi đang làm điều gì đó sai, hiểu nhầm điều gì đó hoặc không thể làm những gì tôi muốn làm. Tôi sắp sửa nói "tốt, bạn không cần phải tìm ra cách sửa đổi mã của bạn để làm cho trạng thái được chuyển qua được xử lý tự động, vì nó hoạt động tốt" và bỏ cuộc rồi tôi nghĩ mình sẽ hỏi ở đây (lần đầu tiên tôi khám phá). Tôi thích một giải pháp thanh lịch hơn.
Tôi cũng tìm một giải pháp tinh tế hơn sẽ cho tôi chức năng này tôi sử dụng "miễn phí:"
-- | Maps the specified method, which must take a RNG as the last parameter,
-- over all the elements of the list, propagating the RNG and returning it.
-- TODO: Implement this without using recursion? Using a fold?
mapRandom :: (a -> WithRNG b) --^The function to map (that takes a RNG)
-> [a] --^The input list
-> WithRNG [b] --^The RNG to return
mapRandom func [] r0 = ([], r0)
mapRandom func (x:xs) r0 = (mapped : rest, r2)
where
(mapped, r1) = func x r0
(rest, r2) = mapRandom func xs r1
Thanks cho bất kỳ suy nghĩ, đề nghị, tài liệu tham khảo và thời gian của bạn!
Trước khi tôi trả lời, bạn có biết về đơn vị 'State' không? –
@GabrielGonzalez Thật vậy, tôi đã nghe nói về nhà nước Monad: "Tôi đã không thể tìm ra Monad nhà nước cũng đủ để thực hiện một phương pháp tương đương bằng cách sử dụng một trong hai. Ngay cả khi tôi đã thành công, tuy nhiên, việc thực hiện Monad Nhà nước có vẻ sử dụng xấu "lấy" và "đặt" s (và "runStates" vv) và làm cho mã ít đọc được hơn, không nhiều hơn. " –
Trước khi tôi trả lời, bạn có biết về biến thể đơn nguyên ['RandT'] (http://hackage.haskell.org/packages/archive/MonadRandom/0.1.8/doc/html/Control-Monad-Random.html) chưa ? –