Общее программирование на F # - Использование членов - PullRequest
4 голосов
/ 02 сентября 2010

Предположим, у меня есть семейство типов, которые поддерживают данную функцию-член, например член Property ниже:

type FooA = {...} with

    member this.Property = ...

type FooB = {...} with

    member this.Property = ...

Предположим, что свойство-член возвращает целое число для каждого из указанных выше типов.Теперь я хочу написать обобщенную функцию, которая могла бы выполнять следующие действия:

let sum (a: 'T) (b: 'U) = a.Property + b.Property

Я привык писать следующее в C ++:

template<typename T, typename U>
int sum(T a, U b)
{
    return a.Property + b.Property;
}

Каким будет эквивалентная реализация вF #

Ответы [ 3 ]

7 голосов
/ 02 сентября 2010

Это можно сделать в F #, но это не идиоматично:

let inline sum a b = (^T : (member Property : int) a) + (^U : (member Property : int) b)

В общем случае .NET-дженерики сильно отличаются от шаблонов C ++, поэтому, вероятно, стоит сначала узнать об их различиях, прежде чем пытаться эмулировать C ++ в F #. Общая философия .NET заключается в том, что операции определяются номинальными типами, а не структурными типами. В вашем примере вы можете определить интерфейс, представляющий свойство Property, которое реализуют два ваших класса, и тогда ваша функция sum будет принимать экземпляры этого интерфейса.

См. http://msdn.microsoft.com/en-us/library/dd233215.aspx для получения дополнительной информации о дженериках F #; для краткого сравнения шаблонов .NET с шаблонами C ++ см. http://blogs.msdn.com/b/branbray/archive/2003/11/19/51023.aspx (или что-нибудь еще, что вы увидите, когда вы Google ".NET шаблоны C ++ шаблонов").

2 голосов
/ 02 сентября 2010

Шаблоны C ++ примерно используют своего рода отношение «структурный статический подтип» (что-то вроде «утиной типизации»), тогда как в обобщениях .NET используется номинальный подтип. Как говорит @kvb, вы можете эмулировать C ++ с помощью inline и статических ограничений членов в F #, но будьте очень осторожны в этом. Есть несколько других способов выразить подобное отношение; @Tomas показывает одно (подтип OO), а другое - передать словарь методов (который знает, как проецировать .Property из фиксированного набора типов). (Если бы это был Haskell, вы могли бы использовать классы типов.)

2 голосов
/ 02 сентября 2010

Я думаю, что чистый подход в этом случае (даже в F #) заключается в использовании базового объектно-ориентированного программирования и определения интерфейса с необходимыми членами (например, Property):

type IHasProperty =
  abstract Property : int

Обратите внимание, что F # подразумевает, что мы объявляем интерфейс, потому что он имеет только абстрактные члены и не имеет конструктора.Затем вы можете реализовать интерфейс в ваших типах:

type FooA = {...} 
  interface IHasProperty with
    member this.Property = ... 

type FooB = {...} 
  interface IHasProperty with
    member this.Property = ... 

Стоит отметить, что любые типы F # (включая записи, различимые объединения и, конечно, классы) могут реализовывать интерфейсы.Теперь универсальная функция может просто принимать два аргумента типа IHasProperty:

let sum (a: IHasProperty) (b: IHasProperty) = a.Property + b.Property 

. В этом случае вам даже не нужны универсальные шаблоны, но есть несколько приемов, которые универсальные шаблоны могут делать и с интерфейсами -Вы можете потребовать параметр типа T для реализации некоторого указанного интерфейса, но здесь это не нужно, потому что F # будет автоматически преобразовывать аргументы из типов, таких как FooA, в интерфейс, когда вы пишете sum f1 f2.

Использование интерфейсов и типов, которые являются неизменяемыми, часто очень полезно в F #.Может быть «более функциональное» решение вашей проблемы, но для этого потребуется больше контекста.

...