Атрибут AutoOpen в F #
Какова рекомендуемая доктрина для использования атрибута AutoOpen?
(Этот вопрос, вероятно, находится в продолжении когда использовать функцию VS-функции VS статической функции аналогично именованного типа)
Эксперт F # заявляет, что "это может быть полезно, когда вы определяете операторы ad hoc верхнего уровня
и функции: "
Итак, это похоже на то, чтобы уменьшить роль модуля в организации кода, когда вам технически нужно написать код, но вы удаляете его существование с точки зрения клиента.
Есть ли что-то еще?
Когда вы его используете?
Ответы
Ответ 1
Я думаю, что основное использование атрибута AutoOpen
- это когда вы хотите сделать некоторые допустимые значения доступными, когда пользователь вашей библиотеки открывает пространство имен. Здесь атрибут очень полезен, потому что я думаю, что библиотеки обычно должны экспортировать все определения в пространствах имен, но для некоторых целей вам нужно экспортировать значения, а значения не могут быть определены внутри пространства имен.
Вот пример из F # async extensions, который определяет построитель вычислений и, следовательно, ему нужно экспортировать значение asyncSeq
(но при этом время, вся функциональность завернута в пространство имен):
namespace FSharp.Async
type AsyncSeq<'T> = (* ... *)
type AsyncSeqBuilder() = (* ... *)
[<AutoOpen>]
module GlobalValues =
let asyncSeq = AsyncSeqBuilder()
Пользователь библиотеки может просто написать open FSharp.Async
, и они будут видеть asyncSeq
. Я думаю, что тот же шаблон используется с различными математическими библиотеками (где вы также хотите экспортировать простые имена функций.)
Для модулей (например, List
и Seq
) я думаю, что большинство людей не используют open
и получают доступ к функциям через имя модуля (например, List.map
), поэтому, хотя вы можете использовать это для вложенных модулей я не видел это так часто.
Ответ 2
Его можно использовать для организации модуля в подмодулях, но он представляет собой унифицированный/одномодовый вид снаружи:
module Outer =
[<AutoOpen>]
module Inner1 =
let f1() = ()
[<AutoOpen>]
module Inner2 =
let f2() = ()
open Outer
let x = f1()
let y = f2()
FParsec делает следующее: open FParsec
открывает все подмодули (Primitives
, CharParsers
и т.д.).
Ответ 3
Немного опоздал на вечеринку, но я хотел добавить еще одно использование.
Я склонен использовать [<AutoOpen>]
для предоставления типов в пространстве имен.
// SlimSql\Types.fs
namespace SlimSql
[<AutoOpen>]
module Types =
type SqlOperation =
{
Statement : string
Parameters : SqlParam list
}
Затем я могу прикрепить функции к одному и тому же имени типа без получения ошибки компилятора, поскольку это имя уже используется.
// SlimSql\SqlOperation.fs
namespace SlimSql
module SqlOperation =
let merge (operations : SqlOperation list) : SqlOperation =
...
let wrapInTransaction operation =
...
Тогда все хорошо упаковано с тем же именем в потреблении кода. Поэтому, когда пользователь ищет поведение в данных SqlOperation, он может найти его, набрав SqlOperation.
и Intellisense покажет это. Почти так же, как на практике используются такие типы, как List
.
open SlimSql
let operations =
[
sql "INSERT INTO ...." [ p "@Value" 123; ... ]
...
]
let writeOp =
operations
|> SqlOperation.merge
|> SqlOperation.wrapInTransaction
Модуль SlimSql.Types также можно открыть сам по себе, чтобы получить доступ только к типам композиции с другими типами.
Я предпочитаю это решение для дополнения типов статическими членами.