如何在F#中管理独占状态?

时间:2013-09-29 16:12:41

标签: f#

我不确定标题中的“独家国家管理”事情,我尽力做到最好,试图简明扼要地解决问题。

我正在将一些我的C#代码移植到F#,试图尽可能地将其作为惯用语。我有一个实体从我的数据库中的序列请求许多ID,然后将这些ID分发给任何有需要的人。一旦id被发出,它就不再适用于任何其他人。因此,必须存在与该实体相关联的某种状态,以跟踪剩余的ID数量。由于使用可变状态不是惯用语,我能做的就是写这样的东西:

let createIdManager = 
  let idToStartWith = 127
  let allowed = 10
  let givenOut = 0
  (idToStartWith, allowed, givenOut)
-
let getNextAvailableId (idToStartWith, allowed, givenOut) = 
  if givenOut< allowed
  then ((idToStartWith, allowed, givenOut+ 1), Some(idToStartWith + givenOut))
  else ((idToStartWith, allowed, givenOut), None)

let (idManager, idOpt) = getNextAvailableId createIdManager()
match idOpt with
| Some(id) -> printf "Yay!"
| None -> reloadIdManager idManager |> getNextAvailableId

这种方法是惯用的(据我所知)但非常脆弱。有很多方法可以搞砸它。我最关心的是,一旦id被提升并且创建了一个更新的id管理器副本,就没有任何力量可以阻止你使用旧版本并再次获得相同的id。

那么如何在F#中进行独占状态管理?

2 个答案:

答案 0 :(得分:0)

如果您只需要初始化一组id,那么您可以简单地隐藏对本地函数范围内的列表的可变引用,如:

let nextId =
    let idsRef = ref <| loadIdsFromDatabase()
    fun () ->
        match idsRef.Value with
        | []        -> 
            None
        | id::ids   ->
            idsRef := ids
            Some id

let id1 = nextId ()
let id2 = nextId ()

答案 1 :(得分:0)

你可以使用state-monad(Computational Expression)。

首先我们声明state-monad

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

type StateBuilder<'s>() =
  member x.Return v : State<'s,_> = State(fun s -> v,s)
  member x.Bind(State v, f) : State<'s,_> =
    State(fun s ->
      let (a,s) = v s
      let (State v') = f a
      v' s)

let withState<'s> = StateBuilder<'s>()
let runState (State f) init = f init

然后我们定义你的'IdManager'和一个函数来获得下一个可用的id以及执行函数后的新状态。

type IdManager = {
  IdToStartWith : int
  Allowed : int
  GivenOut : int
}

let getNextId state = 
  if state.Allowed > state.GivenOut then
    Some (state.IdToStartWith + state.GivenOut), { state with GivenOut = state.GivenOut + 1 }
  else
    None, state

最后,我们定义了请求id并执行state-monad的逻辑。

let idStateProcess =
  withState {
    let! id1 = State(getNextId)
    printfn "Got id %A" id1

    let! id2 = State(getNextId)
    printfn "Got id %A" id2

    //...

    return ()
  }

let initState = { IdToStartWith = 127; Allowed = 10; GivenOut = 0  }

let (_, postState) = 
  runState 
    idStateProcess 
    initState //This should be loaded from database in your case

输出:

Got id Some 127
Got id Some 128