2013-04-25 122 views
2

我只是想知道如何缩短这些代码,我怀疑这是过于冗余如何缩短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之间唯一的区别就是类型本身。

我该如何缩短这段代码?

回答

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) 
+0

可以,我们甚至去除多余'提高Wrong_bson_type' – 2013-04-25 13:38:18

+0

也在做,我们需要()为双V?喜欢的乐趣(Double v) - > ...' – 2013-04-25 13:41:21

+0

为什么它很丑? – 2013-04-25 13:42:15

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 
+0

对不起,我的意思是使用get_type样式,因为我希望用户明确地确定他想要的内容。 – 2013-04-25 13:56:45

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 

然后你可以使用单一产品风格的高阶功能,允许你保留你原来的定义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 

减少红色undant并将副作用抽象为泛型绑定和返回操作。

1

随着GADTs:

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