2012-09-02 33 views
5
class Monad m => MonadState s m | m -> s where 
    -- | Return the state from the internals of the monad. 
    get :: m s 
    get = state (\s -> (s, s)) 

    -- | Replace the state inside the monad. 
    put :: s -> m() 
    put s = state (\_ -> ((), s)) 

    -- | Embed a simple state action into the monad. 
    state :: (s -> (a, s)) -> m a 
    state f = do 
     s <- get 
     let ~(a, s') = f s 
     put s' 
     return a 

instance MonadState s m => MonadState s (MaybeT m) where... 

Tại sao một thể hiện của MonadState cần một trạng thái và một đơn nguyên, tại sao không tạo một lớp Trạng thái thông số duy nhất?Tại sao sử dụng MultiParamTypeClasses trong MonadState

+4

Tôi không chắc chắn tôi hiểu thay thế bạn đang đề xuất. Làm thế nào bạn sẽ viết kiểu 'state :: (s -> (a, s)) -> m a' mà không có' m' và 's'? – Owen

+0

giả sử chúng ta sẽ bắt đầu từ 'lớp MonadState s ở đâu ...' và chúng ta chỉ làm 'get :: s' và' put :: s ->() 'mà không đặt s trong một đơn nguyên? Nó có thể đạt được một thực hiện trạng thái đơn giản hơn, nơi chúng ta không phải lo lắng về nếu nó là một trạng thái Có thể hoặc một trạng thái hoặc trạng thái IO? –

Trả lời

6

Hãy để tôi thử và trả lời câu hỏi của Gert trong các nhận xét vì đó là một câu hỏi khá khác.

Câu hỏi đặt ra là, tại sao chúng ta không chỉ cần viết

class State s where 
    get :: s 
    put :: s ->() 

Vâng, chúng ta có thể viết những dòng này. Nhưng bây giờ câu hỏi là, chúng ta có thể làm gì với nó? Và phần khó khăn là, nếu chúng ta có một số mã với put x và sau đó get, làm thế nào để chúng ta liên kết các get đến put sao cho cùng một giá trị được trả về như một giá trị được đưa vào?

Và vấn đề là, chỉ với các loại ()s, không có cách nào để liên kết một với nhau. Bạn có thể thử triển khai nó theo nhiều cách khác nhau, nhưng nó sẽ không hoạt động. Không có cách nào để mang dữ liệu từ số put đến số get (có thể ai đó có thể giải thích điều này tốt hơn, nhưng cách tốt nhất để hiểu là thử viết nó).

Một đơn nguyên không nhất thiết phải là cách duy nhất để thực hiện các hoạt động có thể kết nối, nhưng nó là một cách, bởi vì nó có >> điều hành để liên kết hai câu với nhau:

(>>) :: m a -> m b -> m b 

vì vậy chúng tôi có thể viết

(put x) >> get 

EDIT: Dưới đây là một ví dụ sử dụng the StateT instance defined in the package

foo :: StateT Int IO() 
foo = do 
    put 3 
    x <- get 
    lift $ print x 

main = evalStateT foo 0 
+0

bạn có thể thêm một ví dụ IO vào câu trả lời của bạn không? Giống như '(put x) >> get >> = (\ x -> print x)' hay gì đó? –

+1

@GertCuykens Tôi không thể nghĩ làm thế nào để làm điều đó với 'IO'; 'IO' không có chỗ để đặt trạng thái như' State'. Nhưng tôi có thể thêm một ví dụ bằng cách sử dụng 'StateT' xung quanh' IO'. – Owen

+0

@Owen - bạn có thể tạo một ví dụ bằng cách sử dụng 'IO' với' IORef'. –

4

Bạn cần một số cách liên kết loại trạng thái với loại đơn nguyên. MultiParamTypeClasses với FunctionalDependencies là một cách. Tuy nhiên, bạn cũng có thể làm điều đó bằng cách sử dụng TypeFamilies.

class (Monad m) => MonadState m where 
    type StateType m 

    -- | Return the state from the internals of the monad. 
    get :: m (StateType m) 
    get = state (\s -> (s, s)) 

    -- | Replace the state inside the monad. 
    put :: StateType m -> m() 
    put s = state (\_ -> ((), s)) 

    -- | Embed a simple state action into the monad. 
    state :: (StateType m -> (a, StateType m)) -> m a 
    state f = do 
     s <- get 
     let ~(a, s') = f s 
     put s' 
     return a 

instance MonadState m => MonadState (MaybeT m) where 
    type StateType (MaybeT m) = StateType m 

    ... 

Đây là phương pháp được thực hiện bởi monads-tf package.

+1

Lưu ý: 'monads-tf' về cơ bản đã được gỡ bỏ. Ban đầu, Ross tách 'mtl' thành' máy biến áp' là Haskell 98 và hai gói 'monads-tf' và' monads-fd' để mọi người có thể chọn kiểu mà họ ưa thích. Tuy nhiên, điều này là khủng khiếp vì nó chia cộng đồng 3 cách, như 'mtl',' monads-tf' và 'monads-fd' tất cả đều sử dụng cùng một tên mô-đun! Bản sửa lỗi là để gỡ bỏ 'mtl' cũ và tạo' monads-fd' thành 'mtl' và để' monads-tf' chết. Tôi sẽ không phản đối một gói với các lớp từ 'monads-tf' đã được tắt sang một bên bằng cách sử dụng các tên module khác nhau nhưng, vì nó đứng, nó là hoàn toàn bất lợi. –

Các vấn đề liên quan