2013-03-25 21 views
9

Hãy nói rằng tôi có một monadT:Tại sao bạn không cần sử dụng 'lift' khi tương tác với một monadT lồng nhau StateT trong trường hợp này?

type Wrap a = ReaderT Env (StateT Int (StateT Int Identity)) a 

Điều quan trọng cần lưu ý ở đây là một StateT được gói khác, và cả hai đều được bao bọc bên trong một MonadT thứ ba, cụ thể là ReaderT.

và chức năng runWrap tương ứng cho tiện theo dõi:

type Env = Map.Map Char Integer 

runWrap :: Env -> Int -> Int -> Wrap a -> a 
runWrap env st1 st2 m = runIdentity $ evalStateT (evalStateT (runReaderT m env) st2) st1 

Và một đơn nguyên nhà nước tock generic:

tock :: (Num s, MonadState s m) => m() 
tock = do modify (+1) 

bây giờ tôi tạo ra một monadT bọc nơi bên tôi sử dụng tock:

aWrap :: Wrap (Int, Int) 
aWrap = do 
    lift tock 
    lift . lift $ tock 
    x <- get 
    y <- lift . lift $ get 
    return (x, y) 

Và chạy nó:

env = Map.fromList [('x', 1)] 
runWrap env 1 200 aWrap 
// answer: (201,2) 

Việc sử dụng lift ở đây có ý nghĩa với tôi về mặt hiểu biết của tôi về cách tương tác với các lớp MonadT lồng nhau.

Tuy nhiên, điều này cũng làm việc và cung cấp cho tôi câu trả lời giống nhau: (201,2):

aWrap :: Wrap (Int, Int) 
aWrap = do 
    tock 
    lift . lift $ tock 
    x <- get 
    y <- lift . lift $ get 
    return (x, y) 

Tôi nghĩ rằng bằng cách gọi tock w/o lift, nó đọc như thể tock được áp dụng cho MonadT bên ngoài, cụ thể là ReaderT , không có ý nghĩa gì cả. Nhưng tại sao điều này lại hiệu quả?

P.S. Xin vui lòng bỏ qua sự hiện diện của Env ở đây, nó không có gì để làm w/câu hỏi, chỉ là sự lựa chọn của MonadT bên ngoài tôi đang sử dụng.

Trả lời

8

Có thể bạn đang sử dụng kiểu chữ MonadState mà không cần biết. Kiểu chữ này được định nghĩa trong gói mtl (và cả trong số monads-fd).

MonadState cho phép bạn sử dụng các phương thức của đơn vị State, trực tiếp và không nâng cấp rõ ràng, trong nhiều ngăn xếp đơn nguyên dựa trên State.

Nhìn vào hai dòng sau trong haddocks:

Monad m => MonadState s (StateT s m) 
MonadState s m => MonadState s (ReaderT r m) 

Người đầu tiên nói rằng bất kỳ StateT là một thể hiện của MonadState (như chúng ta nên mong đợi!). Điều thứ hai nói rằng bất kỳ ReaderT mà đơn nguyên cơ sở của bạn là một instace của MonadState, cũng là một ví dụ của MonadState. Điều đó xảy ra là trường hợp của bạn.

Nhìn vào source code cho MonadState, chúng ta thấy:

instance MonadState s m => MonadState s (ReaderT r m) where 
    get = lift get 
    put = lift . put 
    state = lift . state 

modify :: MonadState s m => (s -> s) -> m() 
modify f = state (\s -> ((), f s)) 

Như bạn thấy, các máy móc bên trong của typeclass sẽ chăm sóc của việc dỡ bỏ.

Có các loại máy chữ khác cung cấp chức năng tương tự, như MonadReader, MonadWriterMonadRWS.