Создание и использование универсальной функции в неуниверсальном интерфейсе F # - PullRequest
2 голосов
/ 14 ноября 2011

Это вне меня в данный момент. Я пытаюсь создать интерфейс, который выглядит примерно так это.

type IFetchData = 
     abstract FetchData: string -> seq<'a>

Приведенное выше объявление действительно (и компилируется), но когда я его использую, я получаю ошибку во время компиляции. Ожидалось, что это выражение будет иметь тип «a», но здесь есть тип «то, что я сейчас пытаюсь вернуть», т.е. seq.

Однако мой пример использования выглядит следующим образом:

type SampleFetchData() =
    interface IFetchData with
        member self.FetchData str =           
           seq {
                    for letter in str do
                        yield letter // compile error here
                }

Я не уверен, что делаю не так. Все, что я хотел бы сделать, это позволить разработчику интерфейса написать любую функцию, которая возвращает общую последовательность: seq<string>, seq<int>, seq<record type here>, seq<union type here> и т. Д.

Может кто-нибудь сказать мне, что мне здесь не хватает?

Спасибо.

Ответы [ 2 ]

4 голосов
/ 14 ноября 2011

Если вы загружаете реализацию интерфейса с помощью Reflection, то с ним будет довольно сложно работать. Проблема в том, что вы получаете объект типа obj. Вы знаете, что он реализует IFetchData<'T> для некоторых 'T, но статически вы не знаете, для каких 'T. Это проблема, потому что вы не можете привести объект к более конкретному типу - если вы попытались использовать IFetchData<obj>, он не будет работать, потому что вы не можете привести, например, IFetchData<int> к этому типу.

Я бы рекомендовал использовать неуниверсальный интерфейс, который является довольно распространенным шаблоном .NET:

type IFetchDataUntyped = 
  abstract FetchData : string -> System.Collections.IEnumerable

type IFetchData<'T> =  
  inherit IFetchDataUntyped
  abstract FetchData : string -> seq<'T> 

Когда вы загружаете реализацию, используя Reflection, вы можете привести объект к IFetchDataUntyped и работать с ним довольно разумным способом (используя Seq.cast для преобразования последовательности в более конкретный тип, если вы знаете тип элемента) .

В зависимости от вашего приложения вы также можете просто сделать метод FetchData универсальным методом и оставить интерфейс не универсальным. Затем вы можете привести динамически загруженные объекты к интерфейсу и вызвать метод. Однако это меняет дизайн (поскольку метод должен работать для любого типа, который он получает в качестве параметра типа):

type IFetchData =  
  abstract FetchData<'T> : string -> seq<'T>  // Note: Generic parameter here!
3 голосов
/ 14 ноября 2011

Вам нужно сделать что-то вроде

type IFetchData<'a> = 
     abstract FetchData: string -> seq<'a>

type SampleFetchData() =
    interface IFetchData<char> with
        member self.FetchData str =           
           seq {
                    for letter in str do
                        yield letter 
                }

т.е. интерфейс должен быть универсальным. Если вы хотите избежать универсальности, вы можете использовать некоторые встроенные ограничения вместо интерфейса

РЕДАКТИРОВАТЬ: Inline magic версия

let inline Fetchdata string obj=
   (^a: (member FetchData: string -> seq<'b> )(obj, string))

type SampleFetchData() =
        member self.FetchData str =           
           seq {
                    for letter in str do
                        yield letter 
                }

Fetchdata "hello" (new SampleFetchData())
...