2010-04-07 7 views
9

Właśnie owinąłem głowę monadami (a przynajmniej chciałbym myśleć, że mam), a dokładniej państwową monadą, którą niektórzy ludzie są mądrzejsi niż ja, więc prawdopodobnie jestem z to pytanie.Monada stanowa, dlaczego nie krotka?

Zresztą monada stan jest zazwyczaj realizowane z M < „a> jak coś tak (F #):

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

Teraz moje pytanie: Czy jest jakiś powód, dla którego nie można użyć krotki tutaj? Poza tym możliwa niejednoznaczność między MonadA<'a, 'b> i MonadB<'a, 'b>, które stałyby się odpowiednikiem krotki.

Edycja: Dodano przykład dla jasności

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

Na czym polega pytanie? Użyj krotki gdzie? Typ zwrotu funkcji to krotka. – Brian

+0

Nie używaj krotki zamiast typu State i po prostu zwróć funkcję zamiast stanu. – thr

Odpowiedz

12

Państwowa monada zasadniczo działa z typem 'state -> 'res * 'state, co oznacza, że ​​trwa trochę obliczeń stan początkowy i generuje wynik (wraz z nową wartością stanu).

Jeśli zastanawiasz się, czy nie ma znaczenia, czy nadajemy temu typowi specjalną nazwę (np. State<'state, 'res>), odpowiedź brzmi: tak naprawdę nie ma to znaczenia. Jedynym celem nadania temu typowi specjalnej nazwy jest uczynienie kodu bardziej czytelnym. Na przykład, spójrzmy na dwóch możliwych podpisów rodzaju poniższym przykładzie:

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 

Pierwszy typ podpis wyraźniej mówi, że mamy funkcję pisania w jakimś monady. Drugi przykład to tylko funkcja, która przyjmuje dwie wartości: int. Myślę, że główną korzyścią pierwszej z nich jest to, że łatwiej można zauważyć, że ten typ może być użyty z innych monadycznych obliczeń (napisanych przy użyciu state { ... }).

Jednak, jak już zauważyłem, nie jest to wymóg techniczny. Ludzie prawdopodobnie używają tego stylu, ponieważ wiele monad pochodzi z Haskell, gdzie monady są powiązane z typu (takiego jak State<'state, 'res>), a nie z konstruktorem obliczeniowym (takim jak state), więc brzmi to jak dobry pomysł, aby zdefiniować nowy typ dla każdej monady w Haskell.

+0

Dzięki, tak, nie mam zamiaru używać wersji krotki, tylko dyskutowaliśmy o tym na IRC (## fsharp @ freenode) i żaden z nas nie widział przyczyny technicznej dla typu State. – thr

5

Ach, tak, jeśli pytanie brzmi: należy użyć jednego tag dyskryminowanych Unii, które niesie ze sobą wartość danych typu T, czy mam po prostu użyj T, możesz użyć albo.

W Haskell, musisz użyć znacznika danych z monadami, ponieważ składnia Haskella do podaje typ monady na podstawie typów wartości (reprezentacja krotki może być instancją co najwyżej pojedynczej Monady). Podczas gdy w F # wyrażenia obliczeniowe są jednoznaczne na temat typu monady (np. state { ... } lub async { ... } lub cokolwiek innego), więc to ograniczenie nie jest konieczne, ten sam typ reprezentacji mógłby być stosowany dla wielu monad.

6

Rodzaj wartości monadycznej W przykładzie nie jest tylko krotka - jest to funkcja powrocie krotka:

'state -> 'res * 'state 

Jeśli pytasz, czy można użyć tylko 'state * 'res jako typ monadyczne obliczenia, wtedy odpowiedź brzmi: nie.To by nie działało, ponieważ nie ma sposobu, aby bezpiecznie przeprowadzić operację zwrotu, która musiałaby mieć następujący typ podpisu:

// how would we get a value of type 'state in the implementation? 
val return : 'a -> 'state * 'a