从现有案例创建新的联合案例

时间:2017-02-08 19:57:20

标签: f#

有没有更好的方法来编写下面的addValues函数?似乎应该可以使用模式匹配而不是FSharp.Reflection但我看不到它。

open System
open FSharp.Reflection

type Value =
    | Tag1 of decimal
    | Tag2 of decimal
    | Error of string

let addValues v1 v2 =
    let c1, f1 = FSharpValue.GetUnionFields(v1, v1.GetType())
    let c2, f2 = FSharpValue.GetUnionFields(v2, v2.GetType())
    let amt1 = (f1.[0]) :?> decimal
    let amt2 = (f2.[0]) :?> decimal

    if c1 = c2
        then ((FSharpValue.MakeUnion(c1, [|box (amt1 + amt2)|]))) :?> Value
        else Error "Mixed Tags"

这可以这样使用:

addValues (Tag1 22m) (Tag1 10m) //Value = Tag1 32M
addValues (Tag1 22m) (Tag2 10m) //Value = Error "Mixed Tags"

3 个答案:

答案 0 :(得分:5)

不清楚应该如何处理addValues (Error "e1") (Error "e2),但对于其他情况你可以这样做:

let addValues v1 v2 =
    match v1, v2 with
    | Tag1 d1, Tag1 d2 -> Tag1 (d1 + d2)
    | Tag2 d1, Tag2 d2 -> Tag2 (d1 + d2)
    | Error e1, Error e2 -> //???
    | _ -> Error "Mixed Tags"

答案 1 :(得分:4)

这不正是原始问题的内容,但是 - 正如其他一些评论者所说的那样 - 我预感到您的数据类型并没有被很好地选择。拥有Application.Run个案意味着,当您拥有更多标签时,您的Error功能会变得非常尴尬。如果我假设你的所有值都是十进制的,你可以重新定义如下:

addValues

(或使用其中一种type Tag = | Tag1 | Tag2 type ResultOrError = | Result of Tag * decimal | Error of string 类型)。 Choice然后成为:

addValues

当您将代码类型扩展为let addValues t1 t2 = match t1, t2 with | Result (tag1, v1), Result (tag2, v2) when tag1 = tag2 -> Result (tag1, v1 + v2) | Result _, Result _ -> Error "Tag mismatch" | Result _, Error _ -> failwith "not implemented" | Error _, _ -> failwith "not implemented" 时,type Tag = | Tag1 | Tag2 | Tag3仍然有效。

答案 2 :(得分:1)

我想和Anton Schwaighofer in his answer一样吹小号。关键信息必须在数据结构中,因为这将利用monad技术来做the lifting;此外,您还需要单独调用案例Tag1Tag2的构造函数。

type Tag = Tag1 | Tag2
type ResultOrError = 
| Result of Tag * decimal
| Error of string

let bind2 f = function
| Result(tag, value) -> f tag value
| error -> error
let lift2EqualTag op mx my =
    bind2 (fun tagX x ->
        bind2 (fun tagY y ->
            if tagX = tagY then Result(tagX, op x y)
            else Error "Mixed tags" ) my ) mx
let add = lift2EqualTag (+)

add (Result(Tag1, 22m)) (Result(Tag1, 10m))
// val it : ResultOrError = Result (Tag1,32M)
add (Result(Tag1, 22m)) (Result(Tag2, 10m))
// val it : ResultOrError = Error "Mixed tags"