在可区分的联合中共享元组类型

sharing a tuple type in a discriminated union

提问人:rasx 提问时间:12/31/2021 更新时间:1/1/2022 访问量:120

问:

假设我们有

type MyTuples =
    | One of a: string * b: string * c: string
    | Two of a: string * b: string * c: string * d: string
    | Three of a: string * b: string * c: string * e: string * f: int

难道不能在DU的案例中分享吗?a: string * b: string * c: string

也许是这样的:

type MyOne = One of a: string * b: string * c: string // the single-case DU

type MySharingTuples =
    | One of MyOne
    | Two of MyOne * d: string
    | Three of MyOne * e: string * f: int

let myTwo = MyTuples.Two (a = "1", b = "2", c = "3", d = "4")

let mySharingTwo = MySharingTuples.Two (MyOne.One (a = "1", b = "2", c = "3"), d = "4")

myTwo |> printfn "%A"
mySharingTwo |> printfn "%A"

哪些打印

Two ("1", "2", "3", "4")
Two (One ("1", "2", "3"), "4")

从类型论的角度来看,并且是不相等的,编译器甚至不允许我们在运行时证明这一点。但从集合论的角度来看,看起来像:myTwomySharingTwoTwo ("1", "2", "3", "4")Two (One ("1", "2", "3"), "4")

("1", "2", "3", "4") = (("1", "2", "3"), "4")

我的旧数学书告诉我那里有等价性。

是否违反 DRY pinciple?有没有某种方式表明,比如说,等价于?这项调查有什么意义吗?我是不是把类型论和集合论混为一谈,把自己弄糊涂了?MyTuplesMyOneMyTuples.One

F# 设置 序列 Dry

评论

2赞 Fyodor Soikin 12/31/2021
不可以,不能为元组的一部分添加别名。考虑使用记录对数据进行建模。

答:

1赞 Brian Berns 1/1/2022 #1

我确实认为您的原始记录违反了 DRY 原则,而 Fyodor 建议使用通用记录类型代替是最好的方法:MyTuples

type Shared = { a: string; b: string; c: string }

type MyTuples =
    | One of Shared
    | Two of Shared * d: string
    | Three of Shared * e: string * f: int

    with
    member this.Shared =
        match this with
            | One shared -> shared
            | Two (shared, _) -> shared
            | Three (shared, _, _) -> shared

然后,您可以共享公共组件,如下所示:

let myTwo = MyTuples.Two ({ a = "1"; b = "2"; c = "3" }, d = "4")
let myThree = Three (myTwo.Shared, e = "5", f = 6)

评论

0赞 rasx 1/1/2022
我接受这个答案,因为 F# 不支持用另一种记录类型扩展记录类型(我们可以在 Typescript 中像这样相交);我们必须使用这种记录和元组的出色组合,以便像(模式匹配)这样的东西在 F 中工作#&match
2赞 bmitc 1/1/2022 #2

MyTuples 是否违反了 DRY pinciple?

我认为如果没有更多的背景,这个问题就没有明确的答案。

不要重复自己 (DRY) 原则仅适用于代码和上下文都重复的情况。在这里,我们没有上下文。在你的联合子句中,在上下文中重复的东西,是重复的东西,是,还是没有?如果没有更多的上下文,我认为使用记录的建议是一个很好的建议。但是,如果这些东西确实相关(例如,它们可能表示 3、4、5 维向量),那么您可以在有区别的联合中使用匿名记录。例如:aa, ba, b, c

type MyTuples =
    | One of {| a: string; b: string; c: string|}
    | Two of {| a: string; b: string; c: string; d: string |}
    | Three of {| a: string; b: string; c: string; e: string; f: int |}

let one = One {|a="a"; b="b"; c="c"|}

有没有办法证明,比如说,MyOne 等同于 MyTuples.One?

是的,您可以定义一个或多个比较函数甚至运算符。

这项调查有什么意义吗?我是不是把类型论和集合论混为一谈,把自己弄糊涂了?

我不认为这是一项调查。类型旨在帮助您对某些域进行建模。如果没有域的上下文,这里唯一的问题就是一个技术问题,即 F# 是否允许扩展现有的记录定义,答案是否定的。

我不清楚集合和类型理论是如何相关的。

评论

0赞 Romain Deneau 1/2/2022
您能为大家解释一下为什么使用匿名记录吗?我想这是因为我们可以编写一些(伪?)类型扩展名。{| abc with d="d" |}let abc = {|a="a"; b="b"; c="c"|}
0赞 rasx 1/3/2022
你的回答“不”对我来说是最充分的:);我可以写的最多上下文是在我的 Jupyter Notebook
0赞 bmitc 7/3/2022
@RomainDeneau 没有确切的原因。在本例中,这样我就不必使用自己的类型定义来命名记录。不过,一个特别之处是有重复的名字。例如,如果我有 a 和 a ,然后我想定义一个向量类型,那么我必须执行类似 .这是有效的 F#,但具有相同名称的类型和构造函数。不过,我通常会这样做,因为没有其他方法。type TwoVector = {x: float; y: float}type ThreeVector = {x: float; y: float; z: float}type Vector = | TwoVector of TwoVector | ThreeVector of ThreeVector