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

pia*_*ste 8 f#

我正在使用相当流行的FSharpx.Collections包,特别是NonEmptyList类型.

这种类型提供了这个NonEmptyList.cons功能,但是我想像::常规那样使用运算符List,即head :: tail.由于tail必须已经是NonEmptyList<'a>,不应该有任何冲突List::运营商.

但是,似乎我无法定义运算符.这个:

let ( :: ) h t = NonEmptyList.cons h t
Run Code Online (Sandbox Code Playgroud)

导致编译错误:

Unexpected symbol '::' in pattern. Expected ')' or other token.
Run Code Online (Sandbox Code Playgroud)

我知道这::与其他运营商并不是同一类别,但我不完全了解如何.于是,我尝试了几件事情或多或少随机,如更换::op_cons之类的,都没有成功.

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

Fyo*_*kin 8

根据MSDN,冒号实际上不能在运营商名称中使用.这似乎与FSharp.org 的F#规范相矛盾,我不确定那里发生了什么.但我们可以在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
Run Code Online (Sandbox Code Playgroud)

如果你看一下如何List<'T>定义,你会发现它(::)实际上不是一个运算符,而是一个case构造函数:

type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
Run Code Online (Sandbox Code Playgroud)

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

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

val a : A = Cons ("abc",5)
Run Code Online (Sandbox Code Playgroud)

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

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

这意味着它(::)在某种程度上是特殊的(([])顺便说一下).

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