F#对象表达式中接口成员实现疑问:为何需函数类型直接赋值?
当在F#对象表达式里尝试直接给接口成员赋值函数时,会触发编译错误。示例代码如下:
type IMyInterface = abstract member Add: a:int -> b:int -> int module MyModule = let myImplementation = let add (a:int) (b:int) : int = a + b { new IMyInterface with member this.Add = add }
报错信息:
No implementation was given for 'abstract IMyInterface.Add: a: int ->
b: int -> int'. Note that all interface members must be implemented
and listed under an appropriate 'interface' declaration, e.g.
'interface ... with member ...'. At least one override did not
correctly implement its corresponding abstract member
通过显式传入参数可以解决这个问题:
module MyModule = let myImplementation = let add (a:int) (b:int) : int = a + b { new IMyInterface with member this.Add a b = add a b}
但如果先给函数定义命名类型签名,就能直接赋值函数且运行正常:
type Add = int -> int -> int type IMyInterface2 = abstract member Add: Add module MyModule2 = let myImplementation2 = let add (a:int) (b:int) : int = a + b { new IMyInterface2 with member this.Add = add }
核心差异解析
看起来两种接口声明的类型逻辑等价,但F#编译器对它们的处理完全不同:
带参数名的签名被识别为CLR方法
当你写abstract member Add: a:int -> b:int -> int时,编译器将这个成员解析为CLR方法,它要求实现时必须显式写出参数列表(比如member this.Add a b = ...),不能直接赋值一个函数值——因为方法和函数值在F#/CLR体系里是不同的概念:方法是需要接收参数调用的成员,而函数值是实现了FSharpFunc的对象实例。命名函数类型的签名被识别为函数属性
当你用type Add = int -> int -> int定义命名函数类型,再声明abstract member Add: Add时,编译器将这个成员解析为返回函数值的属性,它的类型本身就是一个函数,所以可以直接赋值匹配的函数值add。显式传参的本质是方法实现
写member this.Add a b = add a b时,你是把接口成员实现为一个CLR方法,内部调用了函数add,编译器会自动完成方法到函数的适配,满足接口的方法声明要求。
内容的提问来源于stack exchange,提问作者Matthew MacFarland

