Организация кода F#: типы и модули
Как вы решаете, писать ли функцию внутри модуля или как статический член какого-либо типа?
например, в исходном коде F# существует множество типов, которые определяются вместе с одноименным модулем следующим образом:
type MyType = // ...
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module MyType = // ...
почему бы вам просто не определить операции как статические члены типа MyType?
4 ответов
вот некоторые заметки о технических различий.
модули могут быть открытой объед (если они не имеют 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.Ядро.dll) использовать модули только для F#-specific типы (которые обычно не используются при взаимодействии с другими языками .Net) и статические методы для API, которые более нейтральны к языку. Например, все функции с параметрами Карри отображаются в модулях (функции Карри нетривиальны для вызова с других языков).
В F# я предпочитаю статический член типа функции в модуле if ...
- Я должен определить тип независимо от члена
- член функционально связан с типом, который я определяю
В дополнение к другим ответы, есть еще один случай использования модулей:
для типов значений они могут помочь определить статические свойства, которые не переоцениваются при каждом доступе. например:
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 )
некоторые большие различия, которые изначально не упоминались:
функции являются значениями первого класса В F#, но статические члены-нет. Так что вы можете написать
objs |> Seq.map Obj.func
но ты не можешь писатьobjs |> Seq.map Obj.Member
.функции могут быть curried, но члены не могут.
компилятор автоматически выводит типы при вызове функции, но не при вызове члена. Так что вы можете написать
let func obj = obj |> Obj.otherFunc
но вы не можете написатьlet func obj = obj.Member
.
поскольку члены более ограничены, я обычно использую функции, если я явно не хочу поддерживать OOP/C#.