2013-04-30 14 views
6

Chủ đề chung: Trong khi tôi tìm thấy ý tưởng sắp xếp các monads với nhau rất hấp dẫn, tôi gặp rất nhiều rắc rối khi xem mã được thực hiện như thế nào và các lệnh thích hợp để chạy các lớp là gì. Dưới đây là một ví dụ về một ngăn xếp: Writer, State, State, và Error, không theo thứ tự cụ thể (hoặc là có?).Làm thế nào để bạn lý do về thứ tự thực hiện các chức năng trong một ngăn xếp monadT?

----------------------- 
-- Utility Functions -- 
----------------------- 

type Memory = Map String Int 
type Counter = Int 
type Log  = String 

tick :: (MonadState Counter m) => m() 
tick = modify (+1) 

record :: (MonadWriter Log m) => Log -> m() 
record msg = tell $ msg ++ "; " 

------------------ 
-- MonadT Stack -- 
------------------ 

mStack :: (MonadTrans t, MonadState Memory m, MonadState Counter (t m), MonadError ErrMsg (t m), MonadWriter Log (t m)) => t m Int 
mStack = do 
    tick 
    m <- lift get 
    let x = fromJust (M.lookup "x" m) in x 
    record "accessed memory" 
    case True of 
     True -> return 100 
     False -> throwError "false" 

Hãy lưu ý trong mStack, cho dù lỗi được ném hoặc không liên quan gì đến bất kỳ phần nào khác của hàm.

Bây giờ lý tưởng tôi muốn đầu ra trông như thế này:

(Right 100, 1, "accessed memory", fromList [...]) 

hoặc nói chung:

(output of errorT, output of stateT Counter, output of writerT, output of StateT Memory) 

Nhưng tôi không thể có được nó để làm việc. Cụ thể, tôi đã cố gắng chạy stack như nếu Lỗi là trên lớp ngoài cùng:

mem1 = M.fromList [("x",10),("y",5)] 
runIdentity $ runWriterT (runStateT (runStateT (runErrorT mStack) 0) mem1) "" 

Nhưng tôi nhận được thông báo lỗi này:

Couldn't match type `Int' with `Map [Char] Int' 

Các ví dụ ở trên sang một bên, nói chung, khi tôi đang gọi:

runMonadT_1 (runMonadT_2 expr param2) param1,

là các chức năng liên quan đến monadT_2 chạy trước, sau đó đầu ra đó được đưa vào các chức năng liên quan đến monadT_1? Vì vậy, nói cách khác, như bắt buộc như mã nhìn vào các chức năng trên mStack, là thứ tự thực hiện hoàn toàn phụ thuộc vào thứ tự mà monadT được chạy (ngoài bất kỳ cứng nhắc trong cấu trúc được giới thiệu bởi lift)?

Trả lời

6

Bạn đã có thể nhận được một lỗi thông tin mới hơn loại nếu bạn đã cố gắng để gõ tính toán bằng cách sử dụng một ngăn xếp đơn nguyên biến rõ ràng:

mStack :: ErrorT String (StateT (Map String Int) (StateT Int Writer)) Int 

Had bạn làm điều đó, ghc sẽ bắt gặp những loại lỗi trước đó. Nguyên nhân là do bạn sử dụng hai lệnh sau đây trong vòng mStack tại the-top nhất cấp:

modify (+1) -- i.e. from `tick` 
... 
yourMap <- lift get 

Nếu bạn đã cung cấp cho này một ngăn xếp rõ ràng, sau đó bạn muốn nắm bắt những sai lầm: cả hai modifylift get đang đi để nhắm mục tiêu lớp StateT đầu tiên mà chúng gặp phải, điều này xảy ra giống với lớp StateT.

modify bắt đầu từ lớp ErrorT và tiến xuống cho đến khi nó chạm bên ngoài StateT lớp, và kết luận rằng bên ngoài StateT phải đang sử dụng một nhà nước Int. get bắt đầu từ lớp StateT bên ngoài, thông báo rằng lớp này đã ở trong lớp StateT và bỏ qua lớp bên trong StateT hoàn toàn, do đó, kết luận rằng lớp bên ngoài StateT phải lưu trữ Map.

ghc sau đó nói "Điều gì mang lại?Lớp này không thể lưu trữ cả IntMap! ", Giải thích lỗi loại bạn nhận được. Tuy nhiên, vì bạn đã sử dụng các loại lớp thay vì ngăn xếp biến đổi đơn nguyên, không có cách nào mà ghc có thể biết rằng đây là một lỗi gõ chờ đợi cho đến khi bạn đã xác định một chồng bê tông

các giải pháp rất đơn giản:.. chỉ cần thêm lift khác để get của bạn và bây giờ nó sẽ nhắm mục tiêu các lớp bên trong StateT như bạn dự định

cá nhân tôi muốn tránh mtl các lớp học hoàn toàn và luôn hoạt động với ngăn xếp biến đổi đơn nguyên cố định chỉ sử dụng thư viện transformers. erbose vì bạn phải chính xác về lớp nào bạn muốn sử dụng lift, nhưng nó gây ra ít đau đầu hơn trên đường.