为自定义集合定义cons(::)运算符

时间:2015-08-30 14:24:21

标签: f#

我使用的是相当受欢迎的FSharpx.Collections软件包,特别是NonEmptyList类型。

此类型提供NonEmptyList.cons功能,但我希望::运算符与常规List一样使用,即head :: tail。由于tail必须已经是NonEmptyList<'a>,因此不应与List的{​​{1}}运营商发生任何冲突。

然而,似乎我无法定义运算符。这样:

::

导致编译错误:

let ( :: ) h t = NonEmptyList.cons h t

我知道Unexpected symbol '::' in pattern. Expected ')' or other token. 与其他运营商不在同一类别,但我不完全理解如何。所以我或多或少地随意尝试了一些事情,例如用::等取代::,但没有成功。

我错过了什么,有办法做我想做的事吗?

1 个答案:

答案 0 :(得分:8)

According to MSDN,冒号实际上不能在运营商名称中使用。这似乎与FSharp.org的the F# specification相矛盾,我不确定那里发生了什么。但我们可以在FSI验证:

> let ( >:> ) a b = a+b
Script.fsx(1,7): error FS0035: This construct is deprecated: ':' is not permitted as a character in operator names and is reserved for future use

如果您查看how List<'T> is defined,您会发现(::)实际上不是运算符,而是案例构造函数

type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list

果然,您可以使用构造函数名称定义自己的DU类型:

> type A = 
>   | ( :: ) of string * int
>   | A of int
> 
> let a = "abc" :: 5

val a : A = Cons ("abc",5)

现在,奇怪的是,如果我尝试使用另一个看起来像运算符的名称作为案例构造函数,我会收到此错误:

> type A = | ( |> ) of string * int
Script.fsx(1,14): error FS0053: Discriminated union cases and exception labels must be uppercase identifiers

这意味着(::)在某种程度上特殊(顺便说一下,([])也是如此。

所以底线似乎是 - 不,你不能这样做 但为什么你甚至需要?也许你可以选择一个更可接受的运算符名称,它仍然可以表达&#34; const&#34;的语义。 - 比方说,(<+>)