我可以在单例区分联合中调用 "aliased" 类型的方法吗?

Can I call methods of "aliased" types in single-case discriminated unions?

我在优秀的 F# for Fun and Profit 中读到,我可以使用单案例区分联合来获得类型安全等。所以我试着这样做。但是我很快发现我无法调用被(有点)别名或被 DU 查看的类型的方法。

这是一个例子:

type MyList = MyList of List<int>
let list' = [1; 2; 3]
printfn "The list length is %i" list'.Length
let myList = MyList([1; 2; 3])
// The list length is 3
// type MyList = | MyList of List<int>
// val list' : int list = [1; 2; 3]
// val myList : MyList = MyList [1; 2; 3]

到目前为止一切顺利。然后我试了

myList.Length
// Program.fs(12,8): error FS0039: The field, constructor or member 'Length' is not defined.

在使用单例 DU 时,是否有一种简单的方法来访问 "aliased" 或 "viewed" 类型的方法?

你可以这样做,它封装了你对内容所做的事情:

let length (MyList l) = l.Length
length myList

这不是你所说的"alias"。您已经声明了一个全新的类型,其中包含一个列表。您的类型不仅仅是列表的另一个名称,它是一个包装器。

如果你想声明一个真正的别名,你应该使用语法type A = B,例如:

type MyList = List<int> 
let myList : MyList = [1; 2; 3]
printf "Length is %d" myList.Length

有了这样的别名,MyList 将 100% 替代 List<int>,反之亦然。这就是通常所说的"alias"。同一事物的不同名称。

另一方面,包装器将为您提供额外的类型安全性:如果您尝试在预期 MyList 的地方使用裸 List<int>,编译器将捕获。这就是通常使用此类包装器的方式。但是这种额外的保护是有代价的:你不能把 MyList 当作一个列表来使用。因为那会扼杀整个想法。不能两全其美。

问:等等,你是说我必须重新实现所有列表功能才能获得这个包装器吗?

嗯,不。如果你从方法转向函数,你可以通过提供 map:

来使这个 wrapping/unwrapping 通用
type MyList = MyList of List<int>
   with static member map f (MyList l) = f l

let myList = MyList [ 1; 2; 3 ]
let len = MyList.map List.length myList

如果您发现自己经常使用某个特定功能,您甚至可以给它起一个自己的名字:

let myLen = MyList.map List.length
let len = myLen myList

Here 是 Wlaschin 先生关于该主题的另一篇文章。

另请注意:这是方法不如函数的另一种方式。人们通常会尽可能避免使用方法。

使用单例区分联合创建了一个新类型——无论是在技术上还是逻辑上——而且它还隐藏了该类型的实现细节。

如果您正在定义一个新类型(通过包装其他类型),则没有理由对包装类型 list<int> 起作用的操作也应该对类型 MyList 起作用(或者为什么让它们在 MyList 上工作应该很容易),因为您可以决定随时更改 MyList 中使用的表示。

我认为带有列表的示例令人困惑 - 定义 MyList 没有太多实际用途(并且类型别名可能会更好)。但是假设你有类似 Password:

type Password = Password of string

现在,您不希望能够对 Password 进行 运行 任意 string 操作,因为这可能会破坏您希望持有的有关密码的限制(例如,它包含小写和大写字符的混合)。如果您可以写 pass.Substring(0, 1),您将创建无效密码!

因此,如果您想隐藏底层表示,我认为使用单例 DU 是有意义的。然后您还想定义适用于类型 Password 的操作,并且不要使用 string 的任何操作。如果类型确实相同(并且您的别名没有其他限制),则类型别名会更好。