将 F# 函数参数限制为通用接口的多个实例失败并显示 FS0193

发布于 2025-01-19 05:39:49 字数 3919 浏览 2 评论 0原文

我试图限制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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

茶底世界 2025-01-26 05:39:49

这并不漂亮,但它可以编译:

type IReaderIntFloat =
    inherit IReader<int>
    inherit IReader<float>

let useReaders (reader : IReaderIntFloat) =
    (reader :> IReader<int>).readValue, (reader :> IReader<float>).readValue

This isn't pretty, but it compiles:

type IReaderIntFloat =
    inherit IReader<int>
    inherit IReader<float>

let useReaders (reader : IReaderIntFloat) =
    (reader :> IReader<int>).readValue, (reader :> IReader<float>).readValue
千と千尋 2025-01-26 05:39:49

您可以使用非泛型包装类型和灵活类型:

// Input type
type IReader<'a> =
    abstract member readValue : 'a

// Wrapper types
type IReaderInt =
    abstract readInt : int

type IReaderFloat =
    abstract readFloat : float

// Implementation
type NumberReader(intReader: IReader<int>, floatReader: IReader<float>) =
    interface IReaderInt with
        member _.readInt = intReader.readValue

    interface IReaderFloat with
        member _.readFloat = floatReader.readValue

// Helpers
let readInt (reader: #IReaderInt) =
    reader.readInt

let readFloat (reader: #IReaderFloat) =
    reader.readFloat

// Final function
let readNumbers reader =
    readInt reader, readFloat reader
// val readNumbers:
//  reader: 'a -> int * float when 'a :> IReaderInt and 'a :> IReaderFloat

首先,我尝试使用继承,但类型推断不再起作用

You can use non-generic wrapper types and flexible types:

// Input type
type IReader<'a> =
    abstract member readValue : 'a

// Wrapper types
type IReaderInt =
    abstract readInt : int

type IReaderFloat =
    abstract readFloat : float

// Implementation
type NumberReader(intReader: IReader<int>, floatReader: IReader<float>) =
    interface IReaderInt with
        member _.readInt = intReader.readValue

    interface IReaderFloat with
        member _.readFloat = floatReader.readValue

// Helpers
let readInt (reader: #IReaderInt) =
    reader.readInt

let readFloat (reader: #IReaderFloat) =
    reader.readFloat

// Final function
let readNumbers reader =
    readInt reader, readFloat reader
// val readNumbers:
//  reader: 'a -> int * float when 'a :> IReaderInt and 'a :> IReaderFloat

At first, I've tried using inheritance but type inference does not work any more ????

// Input type
type IReader<'a> =
    abstract member readValue : 'a

// Wrapper types
type IReaderInt =
    inherit IReader<int>

type IReaderFloat =
    inherit IReader<float>

// Implementation
type NumberReader(intReader: IReader<int>, floatReader: IReader<float>) =
    interface IReaderInt with
        member _.readValue = intReader.readValue

    interface IReaderFloat with
        member _.readValue = floatReader.readValue

let readInt (reader: #IReaderInt) =
    reader.readValue

let readFloat (reader: #IReaderFloat) =
    reader.readValue

let readNumbers reader =
    readInt reader, readFloat reader
//                            ~~~~~~ Error FS0001...
一曲爱恨情仇 2025-01-26 05:39:49

谢谢 Romain,你让我走上了以下道路:

(* Input type *)
type IReader<'a> =
    abstract member readValueTupled : unit -> 'a * 'a
    abstract member readValue : unit -> 'a

(* generic implementation *)
type GenericImplementation<'a> (a : 'a) =
    interface IReader<'a> with
      member this.readValueTupled () = (a, a)
      member this.readValue () = a

module Int =
  (* Wrapper types *)
  type IReader =
      abstract member readValueTupled : unit -> int * int
      abstract member readValue : unit -> int

module Float =
  (* Wrapper types *)
  type IReader =
      abstract member readValue : unit -> float
      abstract member readValueTupled : unit -> float * float

(* Implementation of class, using the generic implementation above *)
type NumberReader(anInt : int, aFloat : float) =
  let IReaderInt = (GenericImplementation(anInt) :> IReader<int>)
  let IReaderFloat = (GenericImplementation(aFloat) :> IReader<float>)

  interface Int.IReader with
    member _.readValueTupled () = IReaderInt.readValueTupled ()
    member _.readValue () = IReaderInt.readValue ()

  interface Float.IReader with
    member _.readValueTupled () = IReaderFloat.readValueTupled ()
    member _.readValue () = IReaderInt.readValue ()

(* Final function *)
(* val reader: 'a (requires 'a :> Int.IReader and 'a :> Float.IReader) *)
let readNumbers reader =
    (reader :> Int.IReader).readValueTupled ()
    , (reader :> Float.IReader).readValue ()

这将允许我定义一个接口并通用地编写实现,然后将不同的实例传递到尊重接口隔离的函数中。

我添加了第二个成员来测试重构,发现在正确的位置进行正确的更改很痛苦。

因此,我已经接受了,而不是在调用站点传递一组所需的读者,这意味着我可以做这样的事情:

  (* Input type *)
  type IReader<'a> =
      abstract member readValueTupled : unit -> 'a * 'a
      abstract member readValue : unit -> 'a
  
  (* generic implementation - do something *)
  type Reader<'a> (a : 'a) =
      interface IReader<'a> with
        member this.readValueTupled () = (a, a)
        member this.readValue () = a
  
  (* Implementation of class, using the generic implementation above *)
  type NumberReaderAndOtherThings(anInt : int, aFloat : float) =
    member _.IReaderInt = Reader<int>(anInt) :> IReader<int>
    member _.IReaderFloat = Reader<float>(aFloat) :> IReader<float>    
  
  
  (* Called function *)
  let useReaders (readerInt : IReader<int>, readerFloat : IReader<float>) =
      readerInt.readValueTupled (), readerFloat.readValueTupled ()

  (* Call site *)
  let useAllThis = 
    let myClass = NumberReaderAndOtherThings(1, 1.1)
    useReaders (myClass.IReaderInt, myClass.IReaderFloat)

在我的例子中,目前只有通用实现,所以我意识到我可以省去完全界面(目前):

(* generic implementation - do something *)
type Reader<'a> (a : 'a) =
  member this.readValueTupled () = (a, a)
  member this.readValue () = a

(* Implementation of class, using the generic implementation above *)
type NumberReaderAndOtherThings(anInt : int, aFloat : float) =
  member _.IReaderInt = Reader<int>(anInt)
  member _.IReaderFloat = Reader<float>(aFloat)

(* Called function *)
let useReaders (readerInt : Reader<int>, readerFloat : Reader<float>) =
    readerInt.readValueTupled (), readerFloat.readValueTupled ()

(* Call site *)
let useAllThis =
  let myClass = NumberReaderAndOtherThings(1, 1.1)
  useReaders (myClass.IReaderInt, myClass.IReaderFloat)

谢谢大家的帮助,很抱歉这篇文章很长,也许它对将来的人有帮助(欢迎所有建议和评论)。

Thank you Romain, you put me on track to the following:

(* Input type *)
type IReader<'a> =
    abstract member readValueTupled : unit -> 'a * 'a
    abstract member readValue : unit -> 'a

(* generic implementation *)
type GenericImplementation<'a> (a : 'a) =
    interface IReader<'a> with
      member this.readValueTupled () = (a, a)
      member this.readValue () = a

module Int =
  (* Wrapper types *)
  type IReader =
      abstract member readValueTupled : unit -> int * int
      abstract member readValue : unit -> int

module Float =
  (* Wrapper types *)
  type IReader =
      abstract member readValue : unit -> float
      abstract member readValueTupled : unit -> float * float

(* Implementation of class, using the generic implementation above *)
type NumberReader(anInt : int, aFloat : float) =
  let IReaderInt = (GenericImplementation(anInt) :> IReader<int>)
  let IReaderFloat = (GenericImplementation(aFloat) :> IReader<float>)

  interface Int.IReader with
    member _.readValueTupled () = IReaderInt.readValueTupled ()
    member _.readValue () = IReaderInt.readValue ()

  interface Float.IReader with
    member _.readValueTupled () = IReaderFloat.readValueTupled ()
    member _.readValue () = IReaderInt.readValue ()

(* Final function *)
(* val reader: 'a (requires 'a :> Int.IReader and 'a :> Float.IReader) *)
let readNumbers reader =
    (reader :> Int.IReader).readValueTupled ()
    , (reader :> Float.IReader).readValue ()

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:

  (* Input type *)
  type IReader<'a> =
      abstract member readValueTupled : unit -> 'a * 'a
      abstract member readValue : unit -> 'a
  
  (* generic implementation - do something *)
  type Reader<'a> (a : 'a) =
      interface IReader<'a> with
        member this.readValueTupled () = (a, a)
        member this.readValue () = a
  
  (* Implementation of class, using the generic implementation above *)
  type NumberReaderAndOtherThings(anInt : int, aFloat : float) =
    member _.IReaderInt = Reader<int>(anInt) :> IReader<int>
    member _.IReaderFloat = Reader<float>(aFloat) :> IReader<float>    
  
  
  (* Called function *)
  let useReaders (readerInt : IReader<int>, readerFloat : IReader<float>) =
      readerInt.readValueTupled (), readerFloat.readValueTupled ()

  (* Call site *)
  let useAllThis = 
    let myClass = NumberReaderAndOtherThings(1, 1.1)
    useReaders (myClass.IReaderInt, myClass.IReaderFloat)

In my case there is currently only the generic implementation, so I have realised I can dispense with the interface altogether (for now):

(* generic implementation - do something *)
type Reader<'a> (a : 'a) =
  member this.readValueTupled () = (a, a)
  member this.readValue () = a

(* Implementation of class, using the generic implementation above *)
type NumberReaderAndOtherThings(anInt : int, aFloat : float) =
  member _.IReaderInt = Reader<int>(anInt)
  member _.IReaderFloat = Reader<float>(aFloat)

(* Called function *)
let useReaders (readerInt : Reader<int>, readerFloat : Reader<float>) =
    readerInt.readValueTupled (), readerFloat.readValueTupled ()

(* Call site *)
let useAllThis =
  let myClass = NumberReaderAndOtherThings(1, 1.1)
  useReaders (myClass.IReaderInt, myClass.IReaderFloat)

Thank you everyone for the help, sorry for the long post, maybe it helps someone in the future (all advice and comments welcome).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文