ocaml中的评价监视器

ocaml中的评价监视器,ocaml,functor,Ocaml,Functor,我试图实现的功能类似于日志记录功能,但用于监控和流式传输运行模拟中的任意数据。以下是简化的情况: module Sim (V:VEC) = struct module V = V module M = struct type data = V.t end let loop n init_data = let running_data = ref init_data in for _i = 1 to n do (*?*) (* monitor here:

我试图实现的功能类似于日志记录功能,但用于监控和流式传输运行模拟中的任意数据。以下是简化的情况:

module Sim (V:VEC) = struct
  module V = V
  module M = struct type data = V.t end
  let loop n init_data = 
    let running_data = ref init_data in
    for _i = 1 to n do 
      (*?*) (* monitor here: data => outside world *)
      rdata := process_data !rdata
    done
end
模拟循环时,在
处,我可能希望“点击”数据并将其累积。其他时候,我只想让它运行并以最小的开销禁用数据流——
处于一个紧密的循环中。因此,我希望流媒体能够以低成本进行配置

我现在得到的是:

module Sim (V:VEC) = struct
  module V = V
  module M = struct type data = V.t end
  let data_monitor : (M.data -> unit) ref = ref (fun d -> ())
  let loop n init_data = 
    let running_data = ref init_data in
    for _i = 1 to n do 
      !data_monitor !rdata; (* monitor here *)
      rdata := process_data !rdata
    done
end
我在那里放了一个存根监控函数引用。在实际的应用程序脚本中,我可以分配一个函数,例如,将数据值累加到一个列表或类似的列表中。它起作用了

所以问题是:这是实现我想要的最好/最低开销/最好的方法吗

这种方法似乎有点老土,我宁愿使用模块系统而不是函数指针。但是,要传输的数据类型仅在functor
Sim
中定义。因此,在
Sim
之外的另一个模块
Sampler
中实现监控功能,并由此对
Sim
进行参数化,似乎不方便,并且/或者需要重复代码或递归模块。我试过了,但我不能使所有类型都相等

编辑:下面是它在没有函数引用的情况下的大致尝试:

module Sampler (V:VEC) : sig
  module V : VEC
  type data = V.t
  val monitor_data : data -> unit
end 
with type data = V.t = struct
  module V = V
  type data = V.t
  let monitor_data data = store_away_the data
end

module Sim (V:VEC) (Sampler:??) : sig
  ...
end with type M.data = V.t

我不知道如何指定采样器的输出签名,因为输入签名
VEC
仍然是免费的;另外,我也不确定如何使类型相等工作。也许我在这里做错了。

如评论中所述,您可以使用高阶函数(而不必求助于高阶函子)执行类似操作:


用法:

module MySampler (Data : DATA) =
struct
  let monitor data = data |> Data.show |> print_endline
  let monitor' data =
    data
    |> List.map Data.show
    |> String.concat " "
    |> print_endline
end

module MySim = Sim (MySampler)

let () = MySim.simulate ()
这张照片

hi!
hello world
完整性:

基于的函子部分,这是我目前正在使用的。它仍然有点复杂,也许可以更简洁,但它有一些很好的优点。即:可以在集中的位置(类型为
SAMPLER
的模块)打开和关闭各个方面的监控,并且可以导出任意类型,即使它们仅在模拟器模块内的某个位置定义

我定义了监控(=采样)模块和模块类型,如下所示:

module type STYPE = sig type t end

module type SSAMPLER = sig
  type t
  val ev : t React.event
  val mon : t -> unit
end

module type SAMPLER_FN = functor (Data : STYPE) -> SSAMPLER
  with type t := Data.t

(* stub sampler function for a single one *)
module Never : SAMPLER_FN = functor (Data : STYPE) -> struct
  let ev = React.E.never
  let mon = ignore
end

(* event primitive generating sampling function *)
module Event : SAMPLER_FN = functor (Data : STYPE) -> struct
  let (ev : Data.t React.event), mon' = React.E.create ()
  let mon = mon' ?step:None
end
这里,我使用React库生成数据的输出流。
React.E.never
事件不执行任何操作,并对应于关闭采样。然后指定完全采样配置,如下所示:

(* the full sampling config *)
module type SAMPLER = sig
  val sampler_pos : (module SAMPLER_FN)
  val sampler_step : (module SAMPLER_FN)
  (* and several more... *)
end

module NoSampling : SAMPLER = struct
  let sampler_pos = (module Never: SAMPLER_FN)
  let sampler_step = (module Never: SAMPLER_FN)
  (* ... *)
end

(* default sampling config *)
module DefaultSampling : SAMPLER = struct
  include NoSampling
  (* this is only possible when using first class modules *)
  let sampler_pos = (module Event : SAMPLER_FN)
end
可以避免使用一级模块,但不允许在
DefaultSampling
中方便地包含和重写

在模拟库代码中,它的用法如下:

module type VEC = sig
  type t
  val zeropos : t
  val wiggle : t -> t
end

module Sim (V:VEC) (Sampler:SAMPLER) = struct
  module V = V                  

  module M = struct
    type t = { mutable pos : V.t }
    val create () = { pos=V.zeropos }
    module Sampler_pos = (val Sampler.sampler_pos) (struct type nonrec t = t end)
    let update f m = m.pos <- f m.pos
  end

  module Sampler_b = (val Sampler.sampler_b) (struct type t = int end)

  let loop n (running_data:M.t) = 
    for i = 1 to n do 
      (* monitor step number: *)
      Sampler_b.mon i;
      (* monitor current pos: *)
      Sampler_pos.mon running_data;
      M.update V.wiggle running_data
    done 

end

最后一行包含循环期间发生的
Simulator.M.t
类型的累积值列表。步进计数器的监控(一个愚蠢的例子)被关闭。通过创建另一个类型为
SAMPLER
的采样函子,并以此参数化
Sim
,如果需要,可以进一步自定义监控。

当尝试使类型相等时,您是否将
与类型约束一起使用?是,请参阅问题末尾的编辑。在您的问题中,它表示要流式传输的类型仅在
Sim
中定义。但是,该类型(
M.data
)等于
V.t
。这是一种简化,还是仅仅是关于使
M.data
等于
V.t
Sampler
中的问题?现在,如果
Sampler
定义为is,则它无法处理(参数化除外)除
V
中的类型以外的任何类型。同时,您是否考虑过将“Sampler”函数简单地传递给
循环
,可能作为可选参数?并且将循环转换为类似于前面的注释的
折叠:这是一个(n过)简化。事实上,在Sim.M中,一个新的记录类型由一个等于V.t的字段构成。所以,让采样器只依赖于V的天真想法应该注定要失败。但是递归模块很可怕,或者更准确地说,我会发现手工定义Sim的完整输出sig非常麻烦。Sim卡在实际代码中非常长。感谢您制作自我包含的示例!我不确定我是否理解高阶函子解。以下是我的想法:SAMPLER不知道任何依赖于VEC的类型。但这不是一个问题,因为监视器可能会产生副作用,例如,将向量t存储到全局列表中;采样器允许这样做。VEC.t需要从关闭中恢复。现在Sim是一个高阶函子;这是否意味着Sim卡的主体中有V?如果是,怎么做?我想我不知道如何编写测试,以便monitor实际将一些VEC.t(list)导出到外部。无论如何,通过monitor函数的解决方案可能是可行的。唯一缺少的是,没有一个地方可以将所有“监控设置”很好地捆绑在一起——在实际问题中,有许多不同的监控功能。这将需要在应用程序脚本中处理。上面的高阶函子示意图只关注声明高阶函子的语法。我将编辑它(大约一两个小时后),使其更接近我认为您的情景,也许它将回答您的一些问题。
Sim
是一个高阶函子,这意味着它有另一个函子
Sampler\u fn
,作为参数,因此它可以将它应用于其嵌套模块
M
–然后,
Sampler\u fn
的主体可以依赖于这些模块,包括声明依赖于
M
中类型的监视函数。至于高阶函数的捆绑,我仍然不确定是什么阻止了您这样做,或者使用高阶函子在这方面会获得什么好处。您仍然可以声明您的监视乐趣
(* the full sampling config *)
module type SAMPLER = sig
  val sampler_pos : (module SAMPLER_FN)
  val sampler_step : (module SAMPLER_FN)
  (* and several more... *)
end

module NoSampling : SAMPLER = struct
  let sampler_pos = (module Never: SAMPLER_FN)
  let sampler_step = (module Never: SAMPLER_FN)
  (* ... *)
end

(* default sampling config *)
module DefaultSampling : SAMPLER = struct
  include NoSampling
  (* this is only possible when using first class modules *)
  let sampler_pos = (module Event : SAMPLER_FN)
end
module type VEC = sig
  type t
  val zeropos : t
  val wiggle : t -> t
end

module Sim (V:VEC) (Sampler:SAMPLER) = struct
  module V = V                  

  module M = struct
    type t = { mutable pos : V.t }
    val create () = { pos=V.zeropos }
    module Sampler_pos = (val Sampler.sampler_pos) (struct type nonrec t = t end)
    let update f m = m.pos <- f m.pos
  end

  module Sampler_b = (val Sampler.sampler_b) (struct type t = int end)

  let loop n (running_data:M.t) = 
    for i = 1 to n do 
      (* monitor step number: *)
      Sampler_b.mon i;
      (* monitor current pos: *)
      Sampler_pos.mon running_data;
      M.update V.wiggle running_data
    done 

end
module Simulator = Sim (V) (DefaultSampling);;

let trace = Simulator.M.Sampler_pos.ev
          |> React.E.fold (fun l h -> h :: l) []
          |> React.S.hold [];;

let init_m = Simulator.M.create () in
Simulator.loop 100 init_m;;

React.S.value trace;;