将 F# 函数参数限制为通用接口的多个实例失败并显示 FS0193
我试图限制f#函数参数以符合多个接口,而多个接口又从相同的通用接口声明中得出:
module GenericInterfaceTest =
(* declare an interface using generic type *)
type IReader<'a> =
abstract member readValue : 'a
(* constraining only int works *)
let useReaderInt<'r when 'r :> IReader<int>> (reader : 'r) =
(reader :> IReader<int>).readValue
(* constraining only float works *)
let useReaderFloat<'r when 'r :> IReader<float>> (reader : 'r) =
(reader :> IReader<float>).readValue
(* [FS0193] Type constraint mismatch.
The type ''r' is not compatible with type 'IReader<float>' *)
let useReaders<'r when 'r :> IReader<int> and 'r :> IReader<float>> (reader : 'r) =
(reader :> IReader<int>).readValue, (reader :> IReader<float>).readValue
(* [FS0193] Type constraint mismatch.
The type ''r' is not compatible with type 'IReader<int>' *)
let useReaders<'r when 'r :> IReader<float> and 'r :> IReader<int>> (reader : 'r) =
(reader :> IReader<int>).readValue, (reader :> IReader<float>).readValue
(* ------------------------------------------------------------------------
Not using generic type works
------------------------------------------------------------------------ *)
type IIntReader =
abstract member readValue : int
type IFloatReader =
abstract member readValue : float
(* works *)
let useReaders<'r when 'r :> IIntReader and 'r :> IFloatReader> (reader : 'r) =
(reader :> IIntReader).readValue, (reader :> IFloatReader).readValue
(* ------------------------------------------------------------------------
Using different generic declarations works
------------------------------------------------------------------------ *)
type IReader2<'a> =
abstract member readValue : 'a
(* works *)
let useReaders<'r when 'r :> IReader<int> and 'r :> IReader2<float>> (reader : 'r) =
(reader :> IReader<int>).readValue, (reader :> IReader2<float>).readValue
(* ------------------------------------------------------------------------
Using type aliases does not work
------------------------------------------------------------------------ *)
type IReaderInt = IReader<int>
type IReaderFloat = IReader<float>
(* [FS0193] Type constraint mismatch.
The type ''r' is not compatible with type 'IReaderInt' *)
let useReaders<'r when 'r :> IReaderFloat and 'r :> IReaderInt> (reader : 'r) =
(reader :> IReader<int>).readValue, (reader :> IReader2<float>).readValue
这是编译器错误还是限制,还是我做错了什么?
是否有另一种方法可以实现此目的,而不必使用具体类型多次编写代码?
编辑:我在 https://github.com/fsharp/fsharp/fsharp/fsharp/fslang-suggestions/issues/issues/1036
这是一个涉及类型推理的限制。
其中一条评论通过具有中间的非生成界面继承通用专业知识来提及围绕此问题。如果有人能帮助我以一个如何做这件事的示例,我将非常感激 - 我最好的猜测是不起作用的:
module GenericInterfaceTest2 =
(* declare an interface using generic type *)
type IReader<'a> =
abstract member readValue : 'a
type IReaderInt = inherit IReader<int>
type IReaderFloat = inherit IReader<float>
(* works *)
let useReaders<'r when 'r :> IReaderInt> (reader : 'r) =
(reader :> IReader<int>).readValue
(* [FS0193] Type constraint mismatch.
The type ''r' is not compatible with type 'IReaderFloat' *)
let useReaders<'r when 'r :> IReaderInt and 'r :> IReaderFloat> (reader : 'r) =
(reader :> IReaderInt).readValue, (reader :> IReaderFloat).readValue
I'm trying to constrain an F# function parameter to conform to multiple interfaces, which are in turn derived from the same generic interface declaration:
module GenericInterfaceTest =
(* declare an interface using generic type *)
type IReader<'a> =
abstract member readValue : 'a
(* constraining only int works *)
let useReaderInt<'r when 'r :> IReader<int>> (reader : 'r) =
(reader :> IReader<int>).readValue
(* constraining only float works *)
let useReaderFloat<'r when 'r :> IReader<float>> (reader : 'r) =
(reader :> IReader<float>).readValue
(* [FS0193] Type constraint mismatch.
The type ''r' is not compatible with type 'IReader<float>' *)
let useReaders<'r when 'r :> IReader<int> and 'r :> IReader<float>> (reader : 'r) =
(reader :> IReader<int>).readValue, (reader :> IReader<float>).readValue
(* [FS0193] Type constraint mismatch.
The type ''r' is not compatible with type 'IReader<int>' *)
let useReaders<'r when 'r :> IReader<float> and 'r :> IReader<int>> (reader : 'r) =
(reader :> IReader<int>).readValue, (reader :> IReader<float>).readValue
(* ------------------------------------------------------------------------
Not using generic type works
------------------------------------------------------------------------ *)
type IIntReader =
abstract member readValue : int
type IFloatReader =
abstract member readValue : float
(* works *)
let useReaders<'r when 'r :> IIntReader and 'r :> IFloatReader> (reader : 'r) =
(reader :> IIntReader).readValue, (reader :> IFloatReader).readValue
(* ------------------------------------------------------------------------
Using different generic declarations works
------------------------------------------------------------------------ *)
type IReader2<'a> =
abstract member readValue : 'a
(* works *)
let useReaders<'r when 'r :> IReader<int> and 'r :> IReader2<float>> (reader : 'r) =
(reader :> IReader<int>).readValue, (reader :> IReader2<float>).readValue
(* ------------------------------------------------------------------------
Using type aliases does not work
------------------------------------------------------------------------ *)
type IReaderInt = IReader<int>
type IReaderFloat = IReader<float>
(* [FS0193] Type constraint mismatch.
The type ''r' is not compatible with type 'IReaderInt' *)
let useReaders<'r when 'r :> IReaderFloat and 'r :> IReaderInt> (reader : 'r) =
(reader :> IReader<int>).readValue, (reader :> IReader2<float>).readValue
Is this a compiler bug or limitation, or am I doing something wrong?
Is there another way of achieving this without having to write the code multiple times using concrete types?
Edit: I have found the issue on fslang-suggestions: https://github.com/fsharp/fslang-suggestions/issues/1036
It is a limitation involving type inference.
One of the comments mentions working around this by having intermediate non-generic interfaces inheriting generic specialisations. I would be very grateful if someone could help me with an example of how to do this - my best guess below does not work:
module GenericInterfaceTest2 =
(* declare an interface using generic type *)
type IReader<'a> =
abstract member readValue : 'a
type IReaderInt = inherit IReader<int>
type IReaderFloat = inherit IReader<float>
(* works *)
let useReaders<'r when 'r :> IReaderInt> (reader : 'r) =
(reader :> IReader<int>).readValue
(* [FS0193] Type constraint mismatch.
The type ''r' is not compatible with type 'IReaderFloat' *)
let useReaders<'r when 'r :> IReaderInt and 'r :> IReaderFloat> (reader : 'r) =
(reader :> IReaderInt).readValue, (reader :> IReaderFloat).readValue
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这并不漂亮,但它可以编译:
This isn't pretty, but it compiles:
您可以使用非泛型包装类型和灵活类型:
首先,我尝试使用继承,但类型推断不再起作用
You can use non-generic wrapper types and flexible types:
At first, I've tried using inheritance but type inference does not work any more ????
谢谢 Romain,你让我走上了以下道路:
这将允许我定义一个接口并通用地编写实现,然后将不同的实例传递到尊重接口隔离的函数中。
我添加了第二个成员来测试重构,发现在正确的位置进行正确的更改很痛苦。
因此,我已经接受了,而不是在调用站点传递一组所需的读者,这意味着我可以做这样的事情:
在我的例子中,目前只有通用实现,所以我意识到我可以省去完全界面(目前):
谢谢大家的帮助,很抱歉这篇文章很长,也许它对将来的人有帮助(欢迎所有建议和评论)。
Thank you Romain, you put me on track to the following:
This would allow me to define an interface and write the implementation generically, and then pass different instantiations into functions respecting interface segregation.
I added the second member to test the refactoring, and found it painful to make the correct changes in the right places.
So I've made peace with rather passing in a tuple of required readers at the callsite, which means that I can just do something like this:
In my case there is currently only the generic implementation, so I have realised I can dispense with the interface altogether (for now):
Thank you everyone for the help, sorry for the long post, maybe it helps someone in the future (all advice and comments welcome).