0

タイトルの「排他的状態管理」についてはよくわかりませんが、問題を簡潔に表現するために最善を尽くして作成しました。

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 Manager の新しいコピーが作成されると、古いコピーの使用を止めて同じ ID を再び取得する力がなくなることです。

では、F# で排他的な状態管理を行うにはどうすればよいでしょうか。

4

2 に答える 2

0

状態モナド(計算式)を使用できます。

最初に 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
于 2014-02-01T19:37:42.653 に答える
0

ID のセットを 1 回だけ初期化する必要がある場合は、次のように、ローカル関数スコープ内のリストへの変更可能な参照を単純に非表示にすることができます。

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

let id1 = nextId ()
let id2 = nextId ()
于 2013-09-29T17:03:27.733 に答える