F#计算表达式以建立状态并推迟执行

时间:2018-12-23 10:47:52

标签: f# computation-expression

我正在寻找一个可以表达以下内容的计算表达式:

let x = someComputationExpression {
    do! "Message 1"
    printfn "something 1"
    do! "Message 2"
    printfn "something 2"
    do! "Message 3"
    printfn "something 3"
    let lastValue = 4
    do! "Message 4"
    // need to reference values across `do!`
    printfn "something %s" lastValue
}

并能够从x中获取一个列表:

[| "Message 1"
   "Message 2"
   "Message 3"
   "Message 4" |]

从未调用过printfn,但具有以后执行它的能力(如果有意义)。

它不需要与do!关键字一起使用,可以是yieldreturn,无论它需要工作什么。

换句话说,我希望能够在计算Express中收集某些状态,并将可以稍后执行的工作(printfn)排入队列。

我已经尝试了一些方法,但是不确定是否有可能。

1 个答案:

答案 0 :(得分:2)

从OP问题中找出精确的解决方案有点困难。相反,我将发布一些OP可能可以适应需求的代码。

我定义Result和ResultGenerator

type Result =
  | Direct  of string
  | Delayed of (unit -> unit)

type ResultGenerator<'T> = G of (Result list -> 'T*Result list )

生成器生成一个值以及直接值和延迟值的列表,直接值是上面的字符串列表,但与它们混合在一起的是延迟值。我喜欢混合在一起返回,这样可以保留顺序。

请注意,这是有时称为State单子的版本。

除了bind和构建器之类的CE类组件之外,我还创建了两个直接函数和延迟函数。

direct用于创建直接值,而delayed用于创建延迟值(具有函数)

let direct v : ResultGenerator<_> =
  G <| fun rs ->
    (), Direct v::rs

let delayed d : ResultGenerator<_> =
  G <| fun rs ->
    (), Delayed d::rs

为了提高可读性,我定义了延迟的trace函数:

let trace m : ResultGenerator<_> =
  G <| fun rs ->
    (), Delayed (fun () -> printfn "%s" m)::rs

let tracef fmt = kprintf trace fmt

通过示例生成器:

let test =
  builder {
    do! direct "Hello"
    do! tracef "A trace:%s" "!"
    do! direct "There"
    return 123
  }

获得了以下结果:

(123, [Direct "Hello"; Delayed <fun:trace@37-1>; Direct "There"])

(延迟执行时将打印跟踪)。

希望这可以为如何解决实际问题提供一些思路。

完整来源:

open FStharp.Core.Printf

type Result =
  | Direct  of string
  | Delayed of (unit -> unit)

type ResultGenerator<'T> = G of (Result list -> 'T*Result list )

let value v : ResultGenerator<_> =
  G <| fun rs ->
    v,  rs

let bind (G t) uf : ResultGenerator<_> =
  G <| fun rs ->
    let tv, trs = t rs
    let (G u) = uf tv
    u trs

let combine (G t) (G u) : ResultGenerator<_> =
  G <| fun rs ->
    let _, trs = t rs
    u trs

let direct v : ResultGenerator<_> =
  G <| fun rs ->
    (), Direct v::rs

let delayed d : ResultGenerator<_> =
  G <| fun rs ->
    (), Delayed d::rs

let trace m : ResultGenerator<_> =
  G <| fun rs ->
    (), Delayed (fun () -> printfn "%s" m)::rs

let tracef fmt = kprintf trace fmt

type Builder() =
  class
    member x.Bind       (t, uf) = bind t uf
    member x.Combine    (t, u)  = combine t u
    member x.Return     v       = value v
    member x.ReturnFrom t       = t : ResultGenerator<_>
  end

let run (G t) =
  let v, rs = t []
  v, List.rev rs

let builder = Builder ()

let test =
  builder {
    do! direct "Hello"
    do! tracef "A trace:%s" "!"
    do! direct "There"
    return 123
  }

[<EntryPoint>]
let main argv =
  run test |> printfn "%A"
  0