Организация кода F #: типы и модули - PullRequest
17 голосов
/ 06 февраля 2010

Как вы выбираете между написанием функции внутри модуля или статическим членом какого-либо типа?

Например, в исходном коде F # есть много типов, которые определены вместе с модулем с одинаковыми именами, следующим образом:

type MyType = // ...

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module MyType = // ...

Почему бы вам просто не определить операции как статические члены типа MyType?

Ответы [ 4 ]

25 голосов
/ 06 февраля 2010

Вот некоторые заметки о технических различиях.

Модули могут быть «открыты» (если у них нет RequireQualifiedAccessAttribute). То есть, если вы помещаете функции (F и G) в модуль (M), то вы можете написать

open M
... F x ... G x ...

тогда как при статическом методе вы всегда пишете

... M.F x ... M.G x ...

Функции модуля не могут быть перегружены . Функции в модуле имеют функцию let-bound, а функции let-bound не допускают перегрузки. Если вы хотите иметь возможность позвонить как

X.F(someInt)
X.F(someInt, someString)

вы должны использовать member s типа, которые работают только с «квалифицированными» вызовами (например, type.StaticMember(...) или object.InstanceMember(...)).

(Есть ли другие различия? Я не могу вспомнить.)

Это основные технические различия, которые влияют на выбор одного над другим.

Кроме того, во время выполнения F # (FSharp.Core.dll) существует некоторая тенденция использовать модули только для F # -специфических типов (которые обычно не используются при взаимодействии с другими языками .Net) и статические методы для API, которые более не зависит от языка. Например, все функции с параметрами карри появляются в модулях (функции карри не тривиальны для вызова из других языков).

3 голосов
/ 06 февраля 2010

В F # я предпочитаю статический член типа над функцией в модуле, если ...

  1. Я должен определить тип независимо от члена
  2. Элемент функционально связан с типом, который я определяю
2 голосов
/ 30 мая 2014

В дополнение к другим ответам есть еще один случай использования модулей:

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

type [<Struct>] Point =
    val x:float
    val y:float
    new (x,y) = {x=x;y=y}

    static member specialPoint1 = // sqrt is computed every time the property is accessed
        Point (sqrt 0.5 , sqrt 0.5 )

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Point = 

    let specialPoint2 = // sqrt is computed only once when the Module is opened
        Point (sqrt 0.5 , sqrt 0.5 )
0 голосов
/ 15 июля 2017

Некоторые большие различия, которые изначально не упоминались:

  • Функции являются значениями первого класса в F #, но статические члены - нет.Таким образом, вы можете написать objs |> Seq.map Obj.func, но не можете написать objs |> Seq.map Obj.Member.

  • Функции могут быть каррированы, но члены не могут.

  • Компилятор выведет типы автоматически при вызове функции, но не при вызове члена.Таким образом, вы можете написать let func obj = obj |> Obj.otherFunc, но не можете написать let func obj = obj.Member.

Поскольку члены более ограничены, я обычно использую функции, если я не хочу явно поддерживать OOP / C #.

...