openlang

私有联合类型 case、智能构造函数,remove 之后为什么还是 `Colors []`?

大家好,

我有一个小型色板编辑器,用了一个三种情形的调色板联合类型。全新的编辑是 NotEntered,主动清空是 Empty,Colors 则持有一个列表。我把这几个 case 设成了私有,只暴露 ofColors,其中 ofColors [] 会得到 Empty。我原本以为编译器会让 Colors [] 根本无法构造出来。

在一次脚本化的操作中(新建、添加蓝色、移除蓝色、Save),Save 打印的是 "0 colors",而不是 "empty (chosen)"。Reset 的表现是正确的。Rider 没有显示波浪线,构建也是干净的。

我先怀疑是 List.filter,然后怀疑 Rider 用的 SDK 和 global.json 里指定的不一样,于是我在终端里重新构建了一遍。结果还是一样。

我认为要负责的是下面这个函数。

let remove (name: string) (p: Palette) =
    match p with
    | Colors cs -> Colors(cs |> List.filter (fun c -> c.Name <> name))
    | other -> other

私有性是不是只在 Palette.fs 之外才检查,所以我自己的模块可以随意构造 Colors []?访问控制页面似乎是这个意思,但我不确定。如果是这样,让 add 和 remove 都经过 ofColors 是通常的修法吗,还是说这个类型本身就设计错了?我还在想,用一个普通列表加一个 entered 标志,会不会在这里更简单。

谢谢, Oskar

从英语翻译查看原文

后续讨论 1

顺着彼此的依据读下去
  1. 固定链接

    谢谢你写得这么清楚,Oskar。我觉得你对私有性的判断是对的,在定义它的模块内部构造器是可见的,所以 remove 可以随便构造 Colors []。Haskell 里隐藏构造器也是同样的做法,所以这不是 F# 特有的。

    在选修法之前,我想先确认一件事。Save 实际上是对什么做匹配的?如果它有一个打印数量的 Colors cs 分支,那么无论这个值是怎么构造出来的,任何 Colors [] 都会打印 "0 colors"。从新建到添加这一步是经过 ofColors,还是直接构造 Colors?我这么问,是因为步骤的顺序(先 add,再 remove,然后 Save)决定了最终的值由哪个函数产生。

    如果你让 remove 经过 ofColors,add 是不是也需要?我也好奇你为什么排除了让 Colors 携带非空列表的做法。

    从英语翻译查看原文

同一个问题的其他角度

  1. 只读的 List<Int> 算快照吗?

    List<Int> 是只读视图,不是快照。它只告诉使用方,通过这个引用可以调用哪些操作,并不保证它背后的集合不会变。 比如 val owned = mutableListOf(2, 4)。调用方把 owned 传给一个接收 List<Int> 的函数…

    回复 4最近活动 kotlin
  2. 用度量单位做低成本的 id

    对整数 id,我一直在试用度量单位来代替单分支联合类型,想在它扩散开之前听听别人的意见。CustomerId of int 除非标上 [<Struct>] 否则会分配内存,而且每次使用都要经过构造器或 match。用 [<Measure>] type…

    回复 1最近活动 fsharp
  3. 在判别联合类型之前先校验 JSON

    写了个小工具,读取备份主机的状态 dump,每台打印一行。每个文件应该是带 items 的 ready,或者带 message 的 failed。四台主机都正常输出。db-02 的文件是这样的(看起来是 cron 任务把 dump 截断了)。 然后运…

    回复 4最近活动 typescript