F#:嵌套的区分联合和匹配

时间:2012-02-03 22:45:07

标签: f# pattern-matching discriminated-union

我有2个嵌套的歧视联盟:

type ServiceTypes =
    | Contexts
    | Context of int
    | Producers

type ServiceActions =
    | Get of ServiceTypes
    | Update of ServiceTypes

一个嵌套的匹配语句:

let s_action = match action with
               | Get(stype) -> sprintf "Get%s" (match stype with
                                                | Contexts -> sprintf "Contexts"
                                                | Context(id)  -> (sprintf "Context/%d" id))
                                                | _ -> raise (RequestException("get"))
               | Update(stype) -> sprintf "Update%s" (match stype with
                                                      | Producers -> (sprintf "Producers")
                                                      | _ -> raise (RequestException("update")))

目标是使用类似req.Send(Update Producers)的调用构建请求字符串。

无论如何,由于我不理解的原因,编译器给了我2个警告:

  1. Update(stype)我得到此规则永远不会匹配
  2. 在第一个match stype上我得到一个不完整模式匹配此表达式。例如,值“生产者”可能表示模式未涵盖的案例。
  3. 所以问题是为什么我会收到这两个警告?我是否错过了匹配工作的方式?

3 个答案:

答案 0 :(得分:11)

虽然嵌套匹配表达式有时是有保证的,但在这种特殊情况下,如果我是你,我会写一个更易读的单级匹配:

let s_action = 
   match action with
   | Get Contexts     -> "GetContexts"
   | Get (Context id) -> sprintf "GetContext/%d" id
   | Update Producers -> "UpdateProducers"
   | Get _    -> raise (RequestException "get")
   | Update _ -> raise (RequestException "update")

实现与您的代码完全相同的效果。

答案 1 :(得分:6)

您的右括号位于错误的位置。

| Context(id)  -> (sprintf "Context/%d" id))
| _ -> raise (RequestException("get"))

应该是

| Context(id)  -> (sprintf "Context/%d" id)
| _ -> raise (RequestException("get")))

实际上,为了清楚起见,我将摆脱所有无关的括号(在这种情况下实际上是每个括号):

let s_action =
    match action with
    | Get stype    -> match stype with
                        | Contexts   -> "Contexts"
                        | Context id -> sprintf "Context/%d" id
                        | _          -> RequestException "get" |> raise
                      |> sprintf "Get%s"
    | Update stype -> match stype with
                        | Producers -> "Producers"
                        | _         -> RequestException "update" |> raise
                      |> sprintf "Update%s"

就我个人而言,我觉得这更具可读性,但当然是主观的YMMV。

答案 2 :(得分:2)

由于您在错误的位置关闭了副作用,因此您的代码实际上变为:

let s_action =
  match action with
  | Get(stype) -> sprintf "Get%s" (match stype with
                                   | Contexts -> sprintf "Contexts"
                                   | Context(id)  -> (sprintf "Context/%d" id))
  | _ -> raise (RequestException("get")) (* Closing parenthesis should be here *)
  | Update(stype) -> sprintf "Update%s" (match stype with
                                         | Producers -> (sprintf "Producers")
                                         | _ -> raise (RequestException("update")))

显然,由于之前的match stype with模式,您可以看到第一个Producers未涵盖Update(stype)且最后一个模式_从未匹配。因此,所有编译器警告都是合理的。

你似乎过度使用了paratheses;这是一个清理版本:

let s_action =
 match action with
 | Get stype -> sprintf "Get%s" <| match stype with
                                   | Contexts -> sprintf "Contexts"
                                   | Context id -> sprintf "Context/%d" id
                                   | _ -> raise <| RequestException "get"
 | Update stype -> sprintf "Update%s" <| match stype with
                                         | Producers -> sprintf "Producers"
                                         | _ -> raise <| RequestException "update"