2011-04-30 85 views
13

我試圖在OCaml中實現狀態monad(作爲練習)。我的實現看起來是這樣的:OCaml中的狀態monad

module type MONAD_BUILDER = 
sig 
    type 'a t 
    val return : 'a -> 'a t 
    val bind : 'a t -> ('a -> 'b t) -> 'b t 
end;; 

module MonadBuilder = functor (M: MONAD_BUILDER) -> 
struct 
    let (>>=) = M.bind 
    let return = M.return 
end;; 

module StateM = 
struct 
    type 'a state = { state: 's . 's -> ('a * 's) } 
    type 'a t = 'a state 
    let return x = { state = fun s -> (x, s) } 
    let bind m f = 
    let aux s = 
     let (x, s') = m.state s in 
     (f x).state s' 
    in { state = aux } 
    let run m x = fst (m.state x) 
end;; 

我選擇的是生存型的記錄字段,因爲我不喜歡使用仿函數和包裝模塊中的狀態類型的想法。上面的實現工作,但我 遇到一個問題,同時實施getStatesetState。我試圖執行它們,如:

let getState = { state = fun s -> (s, s) } 

let setState s = { state = fun _ -> ((), s) } 

由於推斷的字段類型(例如, 'a -> ('a * 'a)'a -> (unit * 'a),不如聲明的類型's . 's -> ('a * 's)通用。我明白爲什麼會發生這種情況,但我想知道是否有另一種使用記錄方法使其工作的方式?

謝謝。

乾杯, 亞歷

回答

12

's. 's -> ('a * 's)是一個通用型的。你將很難實現一個具有通用類型的狀態......

沒有使用模塊封裝存在類型的乾淨方式(因爲存在類型是抽象類型的用途)。

你可以,當然,發佈狀態類型,而不是:

type ('a,'s) state = { state : 's -> ('a * 's) } 

或者更短,

type ('a,'s) state = 's -> 'a * 's 

通過附加參數,你的代碼運行。但是,您遇到了必須由monad處理您的參數的事實。因此,建立單子時,你可以將其隱藏:

module Monad = MonadBuilder(struct 
    include StateM 
    type 'a t = ('a,myStateType) state 
end) 

或改變你的單子設計以併入一個額外的類型參數是用於生存類型:

module type MONAD_BUILDER = 
sig 
    type ('a,'param) t 
    val return : 'a -> ('a,'param) t 
    val bind : ('a,'param) t -> ('a -> ('b,'param) t) -> ('b,'param) t 
end;; 
+0

感謝您的回答。 – Alex 2011-05-01 13:42:43