我試圖約束 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
這是編譯器錯誤或限制,還是我做錯了什么?
有沒有另一種方法可以實作這一點而不必使用具體型別多次撰寫代碼?
編輯:我在fslang-suggestions上發現了問題:https ://github.com/fsharp/fslang-suggestions/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
uj5u.com熱心網友回復:
您可以使用非泛型包裝器型別和靈活型別:
// 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
起初,我嘗試過使用繼承,但型別推斷不再起作用??
// 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...
uj5u.com熱心網友回復:
這不漂亮,但它編譯:
type IReaderIntFloat =
inherit IReader<int>
inherit IReader<float>
let useReaders (reader : IReaderIntFloat) =
(reader :> IReader<int>).readValue, (reader :> IReader<float>).readValue
uj5u.com熱心網友回復:
謝謝你羅曼,你讓我走上了以下軌道:
(* 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)
謝謝大家的幫助,很抱歉發了這么長的帖子,也許它對將來的人有幫助(歡迎所有建議和評論)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/460212.html
