F#:使用具有区别联合的对象表达式

时间:2014-09-12 21:59:43

标签: recursion f# discriminated-union

我有一个递归函数,它包含一系列匹配,可以使递归调用回函数,也可以调用failwith。

这基本上是Don Syme的专家F#书(第180页)中描述的递归下降解析器的混合实现,此处显示的解析示例:http://fsharpforfunandprofit.com/posts/pattern-matching-command-line/

以下是我自己代码的片段。

let rec parseTokenListRec tokenList optionsSoFar =
    match tokenList with
    | [] -> optionsSoFar
    | SOURCE::t -> 
        match t with
        | VALUE x::tt -> parseTokenListRec (returnNonValueTail t) {optionsSoFar with Source = (returnConcatHeadValues t)}
        | _ -> failwith "Expected a value after the source argument."
    | REGISTRY::t ->
...

可在http://fssnip.net/nU

找到完整的代码清单

当前编写代码的方式,当函数完成通过tokenList的工作时,它将返回通过对象表达式{optionsSoFar with Source = (returnConcatHeadValues t)}编译的optionsSoFar记录,否则它将抛出异常,如果找到了无效的参数。

我想重构这个,以便函数不依赖于异常,但总会返回一些可以由调用函数处理的值。我的想法是返回一个受歧视的联盟而不是记录。

这种受歧视的联盟就像是

type Result =
    |Success of Options
    |Failure of string

我尝试重构代码时遇到的问题是我无法弄清楚如何通过对象表达式来获取DU的成功值。这可能吗?

我在MSDN上查看过的示例(http://msdn.microsoft.com/en-us/library/vstudio/dd233237(v=vs.100).aspx),fsharpforfunandprofit(http://fsharpforfunandprofit.com/posts/discriminated-unions/)和其他地方并没有为我清除这一点。

我担心我在这里没有任何意义。如果需要,我很乐意澄清。

1 个答案:

答案 0 :(得分:2)

如果我理解正确,在您当前的解决方案中,optionsSoFar的类型为Options。如果您将optionsSoFar的类型更改为新定义的Result,则代码会变得更加棘手。

但是,我认为您不需要这样做 - 您可以保留optionsSoFar : Options并更改函数以返回Result。这是有效的,因为在失败后,你永远不需要递归地调用函数:

let rec parseTokenListRec tokenList optionsSoFar =
    match tokenList with
    | [] -> Success optionsSoFar
    | SOURCE::t -> 
        match t with
        | VALUE x::tt -> 
            {optionsSoFar with Source = (returnConcatHeadValues t)}
            |> parseTokenListRec (returnNonValueTail t) 
        | _ -> Failure "Expected a value after the source argument."
    | REGISTRY::t -> ...

如果您确实想要在Source值中更新Result,那么我可能会写下这样的内容:

module Result = 
  let map f = function
    | Success opt -> f opt
    | Failure msg -> Failure msg

然后您可以按如下方式编写转换:

resultSoFar
|> Result.map (fun opts -> {opts with Source = returnConcatHeadValues t})
|> parseTokenListRec (returnNonValueTail t)