簡體   English   中英

如何在F#中管理獨占狀態?

[英]How can I manage an exclusive state in F#?

我不確定標題中是否包含“專有狀態管理”,我竭盡所能,力圖簡潔地解決問題。

我正在將我的一些C#代碼移植到F#,以盡我所能地做到這一點。 我有一個實體,該實體從數據庫中的序列中請求多個ID,然后將這些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#中進行排他狀態管理呢?

如果您只需要初始化一次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 ()

您可以使用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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM