使用单子来简化重复的模式

3

我并不确定该用单子来实现这个功能,或者是否应该使用单子进行解决,如果单子不是最佳方案,我会寻找其他解决方法。

假设我有以下代码(经过简化,但思路在这里):

module IM = Map.Make (Int)

let f k v m =
  if IM.mem k m then (m, true)
  else (IM.add k v m, false)

let a =
  let m = IM.empty in
  let m, res = f 0 "Zero" m in
  if res then
    let m, res = f 1 "One" m in
    if res then f 2 "Two" m else (m, res)
  else (m, res)

我重复多次:

let value, boolean = function application to value and other values in 
  if boolean then another function application to the new value and other values (not the same as the first one)
  else the result

这意味着这些函数不一定具有相同的类型,但它们都返回一个a IM.t * boola 对于每个函数来说是相同的类型)。

我想知道是否可能创建一个内联运算符让我这样做。

我尝试了类似以下的内容:

module EqMonad = struct
  let ( let* ) (t, res) f2 = if res then f2 t else (t, false)
  let return t = t
end

但明显这样不起作用,因为f2需要接收t作为最后一个参数,但它需要多个参数。

我想我可以这样总结我的问题:

是否有可能使用单子包装可变参数函数?


为什么不直接使用 IM.mem 而是奇怪地使用 IM.find - jthulhu
@BlackBeans 简化我的示例后,我没有看到它,但在我的真实示例中,我需要 IM.find 的结果;-) 我将编辑我的问题。 - Lhooq
1个回答

6

你的解决方案可行(在将 return 更正为单子返回后),并且是错误单子的受限版本:

let return x = x, true
let a =
   let m = IM.empty in
   let* m = f 0 "Zero" m in
   let* m = f 1 "One" m in
   let* m = f 2 "Two" m in
   return m

然而,由于控制流程从未取决于m的值,这表明使用常规函数可能更简单:


let rec add_first_fresh m l = match l with
  | [] -> None
  | (k,v):: q ->
    if IM.mem k m then add_first_fresh m q
    else Some (IM.add k v m)

let a = add_first_fresh IM.empty
    [0, "Zero";
     1, "One";
     2, "Two"
    ]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接