F#自定义二元运算符编译报错:期望支持'-'的类型却得到函数类型
F#自定义运算符相关问题解答
1. 报错原因
你遇到的Expecting a type supporting the operator '-' but given a function type报错是编译器无法正确识别运算符导致的:
定义在ListA<'T>类型内部的.<<<.和.>>>.两个运算符,所有参数都是函数类型,不符合F#对类型内部静态二元运算符的要求:定义在类型内的二元运算符必须至少有一个参数是当前类型的实例。因此编译器不会将这两个运算符纳入ListA的重载解析范围,调用时会尝试把符号拆分为内置运算符解析,拆分后的运算符要求数值类型支持,传入函数就触发了类型不匹配报错。
2. F#自定义二元运算符的正确规范
- 符号限制:运算符只能由
! $ % & * + - . / < = > ? @ ^ | ~范围内的字符组成,不能使用其他特殊字符。 - 优先级与结合性:运算符的优先级、结合性和首字符对应的内置运算符完全一致,比如
*开头的运算符优先级高于+开头的运算符,默认左结合,=类赋值运算符为右结合。 - 定义位置要求:
- 运算符至少有一个参数是自定义类型时,可定义为该类型的静态成员,泛型场景下建议标注
inline优化类型推导。 - 运算符所有参数都不是自定义类型时,必须定义为模块级别的
let绑定,不能放在某一类型的内部。
- 运算符至少有一个参数是自定义类型时,可定义为该类型的静态成员,泛型场景下建议标注
- 命名建议:避免定义过于生僻的运算符组合,降低代码可读性。
3. 运算符定义位置差异的原因
.>>=.的第一个参数是ListA<'T>类型实例,满足类型内部运算符的定义要求,调用时只要第一个参数是ListA类型,编译器就能匹配到内部定义的运算符,因此可以正常使用。.-->-.的两个参数都是函数类型,没有参数属于ListA<'T>类型,放在ListA内部定义时不会被编译器纳入重载解析范围,调用时找不到对应运算符,因此必须定义在模块级别才能正常使用。
原问题关联代码
type ListA<'T> = NIL | Cons of 'T * ListA<'T> with static member cat (a:ListA<'T>) (b:ListA<'T>) :ListA<'T>= match a with | NIL -> b | Cons (a, tail) -> Cons (a, (ListA<'T>.cat tail b)) static member join (a:ListA<ListA<'T>>) = match a with | NIL -> NIL | Cons (a, b) -> ListA<'T>.cat a (ListA<'T>.join b) static member fmap (fn: 'T -> 'U) (a:ListA<'T>) = match a with | NIL -> NIL | Cons (a, tail) -> Cons ((fn a), (ListA<'T>.fmap fn tail)) static member inline (.>>=.)(ma:ListA<'T> , k:'T -> ListA<'U>) = ListA<'T>.fmap k ma |> ListA<_>.join static member inline (.<<<.)(mbc:'B -> ListA<'C> , mab:'T -> ListA<'B>) = (fun x -> (mab x) .>>=. mbc) static member inline (.>>>.)(mab:'T -> ListA<'B> , mbc:'B -> ListA<'C>) = (fun x -> (mab x) .>>=. mbc) static member join2 (a:ListA<_>) = a .>>=. id let (.-->.)(mab:'T -> ListA<'B>) (mbc:'B -> ListA<'C>) = (fun x -> (mab x) .>>=. mbc) let (.<--.)(mab:'B -> ListA<'C>) (mbc:'T -> ListA<'B>) = (fun x -> (mbc x) .>>=. mab) let f = fun x -> Cons (x, Cons ((x + 1), NIL)) let g = fun x -> Cons (x * 2, NIL) // NOT Compiling: let fgA = fun x -> (f .<<<. g) // NOT Compiling: let fgB = fun x -> (f .>>>. g) let fg = fun x -> (f x) .>>=. g let fg1 = (f .-->. g .-->. g) let fg2 = (f .<--. g .-->. f) printfn "%A" (fg1 3)
内容的提问来源于stack exchange,提问作者Marc Engher
相关产品推荐
相关产品推荐

