2010-04-07 24 views
9

Tôi chỉ quấn đầu quanh các monads (ít nhất tôi muốn nghĩ rằng mình có) và cụ thể hơn là đơn nguyên tiểu bang, một số người thông minh hơn tôi đã tìm ra, vì vậy tôi có thể câu hỏi này.State Monad, tại sao không phải là một bộ dữ liệu?

Dù sao, đơn nguyên nhà nước thường được thực hiện với một M < 'a> như một cái gì đó như thế này (F #):

type State<'a, 'state> = State of ('state -> 'a * 'state) 

Bây giờ câu hỏi của tôi: Có bất kỳ lý do tại sao bạn không thể sử dụng một tuple đây? Khác thì sự mơ hồ có thể có giữa MonadA<'a, 'b>MonadB<'a, 'b> mà cả hai đều trở thành bộ tóan tương đương ('a * 'b).

Edit: thêm ví dụ cho rõ ràng

type StateMonad() = 
    member m.Return a = (fun s -> a, s) 
    member m.Bind(x, f) = (fun s -> let a, s_ = x s in f a s_) 

let state = new StateMonad() 
let getState = (fun s -> s, s) 
let setState s = (fun _ ->(), s) 
let execute m s = m s |> fst 
+0

Câu hỏi là gì? Sử dụng một tuple ở đâu? Kiểu trả về của hàm là một tuple. – Brian

+0

Không sử dụng tuple thay vì loại State và chỉ trả về một hàm thay vì State. – thr

Trả lời

12

Các đơn nguyên Nhà nước chủ yếu làm việc với một loại 'state -> 'res * 'state, đại diện cho một tính toán rằng có một số trạng thái ban đầu và tạo ra kết quả (cùng với một giá trị mới của nhà nước).

Nếu bạn đang hỏi liệu nó có tạo ra bất kỳ sự khác biệt nào cho dù chúng tôi cung cấp một số tên đặc biệt cho loại này (ví dụ: State<'state, 'res>) thì câu trả lời là không thực sự quan trọng. Mục đích duy nhất của việc đưa ra một số tên đặc biệt cho loại đó là nó làm cho mã dễ đọc hơn. Ví dụ, chúng ta hãy nhìn vào hai chữ ký kiểu có thể xảy ra ví dụ sau:

let foo n = state { 
    let! m = getState() 
    do! setState(m + 1) 
    return sprintf "Result: %d" (n * m) } 

// Using State<'state, 'res> type: 
val foo : int -> State<int, string> 

// Using the underlying representation: 
val foo : int -> int -> int * state 

Loại thứ nhất chữ ký rõ ràng hơn nói rằng chúng ta đang viết hàm trong một số đơn nguyên. Ví dụ thứ hai chỉ là một hàm có hai giá trị int. Tôi nghĩ rằng lợi ích chính của việc đầu tiên là bạn có thể dễ dàng nhận ra rằng loại có thể được sử dụng từ tính toán monadic khác (viết bằng cách sử dụng state { ... }).

Tuy nhiên, như tôi đã lưu ý, đây không phải là yêu cầu kỹ thuật. Mọi người có thể sử dụng phong cách này, bởi vì nhiều monads đến từ Haskell, nơi monads được liên kết với loại (chẳng hạn như State<'state, 'res>) và không phải là một người xây dựng tính toán (chẳng hạn như state), vì vậy có vẻ như một ý tưởng hay để xác định loại mới cho mọi đơn vị trong Haskell.

+0

Cảm ơn, Có, tôi không có ý định sử dụng phiên bản tuple, chúng tôi chỉ đang thảo luận về IRC (## fsharp @ freenode) và không ai trong chúng tôi có thể thấy lý do kỹ thuật cho loại trạng thái. – thr

5

Ah, vâng, nếu câu hỏi là: Tôi nên sử dụng một đơn thẻ phân biệt đối xử liên minh mang một giá trị dữ liệu của loại T, hay tôi nên chỉ sử dụng T, sau đó bạn có thể sử dụng một trong hai. Trong Haskell, bạn cần phải sử dụng một thẻ dữ liệu với monads, vì cú pháp Haskell do infers loại monad dựa trên các loại giá trị (một đại diện tuple có thể là một thể hiện của nhiều nhất một Monad). Trong khi F #, biểu thức tính toán rõ ràng về loại đơn nguyên (ví dụ: state { ... } hoặc async { ... } hoặc bất kỳ thứ gì) để hạn chế này là không cần thiết, cùng loại biểu diễn có thể được sử dụng cho nhiều đơn vị.

6

Các loại giá trị monadic trong ví dụ của bạn không chỉ là một tuple - đó là một hàm trả về tuple:

'state -> 'res * 'state 

Nếu bạn đang hỏi bạn có thể sử dụng chỉ 'state * 'res như kiểu của monadic tính toán, sau đó câu trả lời là không.Điều đó sẽ không hoạt động, bởi vì không có cách nào (an toàn) thực hiện thao tác trả về, phải có chữ ký sau:

// how would we get a value of type 'state in the implementation? 
val return : 'a -> 'state * 'a 
Các vấn đề liên quan