如何缩短此OCaml代码?

时间:2013-04-25 13:30:32

标签: functional-programming ocaml

我只是想知道如何缩短这些代码,因为我怀疑它太冗余了

let get ename doc = 
  try Some (StringMap.find ename doc) with Not_found -> None;;

let get_double ename doc = 
  let element = get ename doc in
  match element with
    | None -> None
    | Some (Double v) -> Some v
    | _ -> raise Wrong_bson_type;;

let get_string ename doc = 
  let element = get ename doc in
  match element with
    | None -> None
    | Some (String v) -> Some v
    | _ -> raise Wrong_bson_type;;

let get_doc ename doc = 
  let element = get ename doc in
  match element with
    | None -> None
    | Some (Document v) -> Some v
    | _ -> raise Wrong_bson_type;;

所以,基本上,我有不同类型的值,并将所有这些值放入地图中。

上面的代码用于从地图中获取相应的值类型。我所做的是,对于每种类型,我都得到了。要获得一种类型的价值,我必须看到a)。是否存在; B)。是否确实是这种类型,如果没有,则提出异常。

但是你可以看到上面的代码似乎是多余的。每种类型的get之间唯一的区别就是类型本身。

如何缩短此代码?

4 个答案:

答案 0 :(得分:2)

你可以这样做:

let get_generic extract ename doc =
  let element = get ename doc in
  match element with
    | None -> None
    | Some v -> Some (extract v)

let get_double = get_generic (function Double v -> v | _ -> raise Wrong_bson_type)
let get_string = get_generic (function String v -> v | _ -> raise Wrong_bson_type)
let get_doc = get_generic (function Document v -> v | _ -> raise Wrong_bson_type)

编辑: 要删除多余的raise Wrong_bson_type(但它很难看):

let get_generic extract ename doc = try
  let element = get ename doc in
  match element with
    | None -> None
    | Some v -> Some (extract v)
with Match_failure _ -> raise Wrong_bson_type

let get_double = get_generic (fun (Double v) -> v)
let get_string = get_generic (fun (String v) -> v)
let get_doc = get_generic (fun (Document v)-> v)

答案 1 :(得分:2)

您可以使用GADT执行此操作:

如果您定义类似expr的类型:

type _ expr =
  | Document: document -> document expr
  | String: string -> string expr
  | Double: float -> float expr

您可以像这样写一个函数get

let get : type v. v expr -> v = function
   Document doc -> doc
 | String s -> s
 | Double d -> d

答案 2 :(得分:1)

使用GADT:

type _ asked = 
 | TDouble : float asked
 | TString : string asked
 | TDocument : document asked

let get : type v. v asked -> string -> doc StringMap.t -> v option = 
  fun asked ename doc ->
  try
    Some (match asked, StringMap.find ename doc with
         | TDouble, Double f -> f
         | TString, String s -> s
         | TDocument, Document d -> d)
  with Not_found -> None

let get_double = get TDouble
let get_string = get TString
let get_document = get TDocument

答案 3 :(得分:0)

如果您可以使用这些提取器功能:

let extract_double = function
  | Double v -> v
  | _ -> raise Wrong_bson_type

let extract_string = function
  | String v -> v
  | _ -> raise Wrong_bson_type

let extract_doc = function
  | Document v -> v
  | _ -> raise Wrong_bson_type

然后你可以使用monadic样式作为高阶函数,这样你就可以保留get的原始定义:

let return x = Some x

let (>>=) mx f = 
  match mx with
    | Some x -> f x
    | None -> None

let get_with exf ename doc =
  (get ename doc) >>= fun v -> 
  return (exf v)

let get_double = get_with extract_double
let get_string = get_with extract_string
let get_doc = get_with extract_doc

减少冗余并将副作用抽象为通用绑定和返回操作。