是否可以在我的计算表达式中提供 setter 函数?
Is it possible to provide a setter function in my computational expression?
我正在尝试编写一个 F# 计算表达式,它只允许从临界区内读取和写入线程安全变量。
我有一个类型 ThreadSafeVar<'t>
,它包含一个值,一个 CriticalSection<'t>
和一个计算表达式构建器,LockContext
,如下所示:
// wraps a value and restricts access to it
type ThreadSafeVar<'t> (value: 't) =
member val internal Value = value with get, set
// Encapsulates a critical section
type CriticalSection<'t> =
private
{ LockObj: obj
fn: unit -> 't }
static member Lock(lc: CriticalSection<'t>) = lock lc.LockObj lc.fn
// Expression builder for a locked context
type LockContext () =
member internal this.SyncRoot = obj()
member this.Return(value: 'v) = value
member this.ReturnFrom(value: ThreadSafeVar<'t>) = value.Value
member __.Bind(value: ThreadSafeVar<'t>, fn: 't -> 'u) = fn value.Value
// returns a CriticalSection
member this.Run(fn : unit -> 'u) = { LockObj = this.SyncRoot
fn=fn }
.
.
.
由于 Bind
,从锁定上下文中读取线程安全值非常简单。例如
let lockedInt = ThreadSafeVar(1) // create a thread-safe variable
let context = LockContext()
let wrapperVal = context {
let! i = lockedInt // get the wrapper value inside lockedInt
return i
} |> CriticalSection.Lock
但我很难理解如何实现一种从 LockContext
实例中设置值的方法。因此,我采用的方法是实现一个 custom operation,例如 setVal
。到目前为止,我已经包括了我的尝试,但恐怕他们只会把水搅浑。自定义操作似乎是在表达式中构建的计算上运行的,编码为元组,但我认为在我的情况下这不是必需的。
如有任何提示、资源指向或直接帮助,我们将不胜感激。
我完全不确定这是否明智,但我想出了一些基于 State monad 的东西,可能对你有用。首先,将“有状态”函数定义为接受 ThreadSafeVar
和 returns 某种类型结果的函数:
ThreadSafeVar<'state> -> 'result
然后我们将该签名放入表示有状态计算的类型中:
type Stateful<'state, 'result> =
MkStateful of (ThreadSafeVar<'state> -> 'result)
现在我们需要一种方法来 运行 使用给定的 TSV 安全地进行这样的计算:
let run (tsv : ThreadSafeVar<_>) (MkStateful f) =
lock tsv (fun () -> f tsv)
请注意,我已经摆脱了您的 CriticalSection
类型,而只是锁定了 TSV 本身。
接下来,我们需要一种将纯值提升为有状态计算的方法:
let lift value =
MkStateful (fun _ -> value)
以及将两个有状态计算绑定在一起的方法:
let bind binder stateful =
MkStateful (fun tsv ->
run tsv stateful
|> binder
|> run tsv)
然后定义构建器就很简单了:
type LockContext () =
member __.Return(value) = lift value
member __.Bind(stateful, binder) = bind binder stateful
let context = LockContext()
我们还需要辅助计算来安全地设置和获取值:
let getValue =
MkStateful (fun tsv ->
tsv.Value)
let setValue value =
MkStateful (fun tsv ->
tsv.Value <- value)
综合起来,我们可以定义一个增加 TSV 值的计算:
let comp =
context {
let! oldValue = getValue
let newValue = oldValue + 1
do! setValue newValue
return newValue
}
我们可以 运行 像这样:
let lockedInt = ThreadSafeVar(1)
let result = comp |> run lockedInt
printfn "%A" result // output is: 2
您可以查看完整的解决方案并自己尝试here。
我正在尝试编写一个 F# 计算表达式,它只允许从临界区内读取和写入线程安全变量。
我有一个类型 ThreadSafeVar<'t>
,它包含一个值,一个 CriticalSection<'t>
和一个计算表达式构建器,LockContext
,如下所示:
// wraps a value and restricts access to it
type ThreadSafeVar<'t> (value: 't) =
member val internal Value = value with get, set
// Encapsulates a critical section
type CriticalSection<'t> =
private
{ LockObj: obj
fn: unit -> 't }
static member Lock(lc: CriticalSection<'t>) = lock lc.LockObj lc.fn
// Expression builder for a locked context
type LockContext () =
member internal this.SyncRoot = obj()
member this.Return(value: 'v) = value
member this.ReturnFrom(value: ThreadSafeVar<'t>) = value.Value
member __.Bind(value: ThreadSafeVar<'t>, fn: 't -> 'u) = fn value.Value
// returns a CriticalSection
member this.Run(fn : unit -> 'u) = { LockObj = this.SyncRoot
fn=fn }
.
.
.
由于 Bind
,从锁定上下文中读取线程安全值非常简单。例如
let lockedInt = ThreadSafeVar(1) // create a thread-safe variable
let context = LockContext()
let wrapperVal = context {
let! i = lockedInt // get the wrapper value inside lockedInt
return i
} |> CriticalSection.Lock
但我很难理解如何实现一种从 LockContext
实例中设置值的方法。因此,我采用的方法是实现一个 custom operation,例如 setVal
。到目前为止,我已经包括了我的尝试,但恐怕他们只会把水搅浑。自定义操作似乎是在表达式中构建的计算上运行的,编码为元组,但我认为在我的情况下这不是必需的。
如有任何提示、资源指向或直接帮助,我们将不胜感激。
我完全不确定这是否明智,但我想出了一些基于 State monad 的东西,可能对你有用。首先,将“有状态”函数定义为接受 ThreadSafeVar
和 returns 某种类型结果的函数:
ThreadSafeVar<'state> -> 'result
然后我们将该签名放入表示有状态计算的类型中:
type Stateful<'state, 'result> =
MkStateful of (ThreadSafeVar<'state> -> 'result)
现在我们需要一种方法来 运行 使用给定的 TSV 安全地进行这样的计算:
let run (tsv : ThreadSafeVar<_>) (MkStateful f) =
lock tsv (fun () -> f tsv)
请注意,我已经摆脱了您的 CriticalSection
类型,而只是锁定了 TSV 本身。
接下来,我们需要一种将纯值提升为有状态计算的方法:
let lift value =
MkStateful (fun _ -> value)
以及将两个有状态计算绑定在一起的方法:
let bind binder stateful =
MkStateful (fun tsv ->
run tsv stateful
|> binder
|> run tsv)
然后定义构建器就很简单了:
type LockContext () =
member __.Return(value) = lift value
member __.Bind(stateful, binder) = bind binder stateful
let context = LockContext()
我们还需要辅助计算来安全地设置和获取值:
let getValue =
MkStateful (fun tsv ->
tsv.Value)
let setValue value =
MkStateful (fun tsv ->
tsv.Value <- value)
综合起来,我们可以定义一个增加 TSV 值的计算:
let comp =
context {
let! oldValue = getValue
let newValue = oldValue + 1
do! setValue newValue
return newValue
}
我们可以 运行 像这样:
let lockedInt = ThreadSafeVar(1)
let result = comp |> run lockedInt
printfn "%A" result // output is: 2
您可以查看完整的解决方案并自己尝试here。