F#中的AutoOpen属性

时间:2012-10-19 15:23:42

标签: f# code-organization

使用AutoOpen属性的推荐原则是什么?

(这个问题可能是继续when to use module function VS static function on similarly named type

专家F#指出“当您定义ad hoc顶级运算符时,这可能很有用 和功能:“

所以这似乎可以减少代码组织中的模块角色,当你在技术上需要编写代码但从客户角度删除它的存在时。

还有别的吗? 你什么时候使用它?

3 个答案:

答案 0 :(得分:38)

我认为AutoOpen属性的主要用途是当您的库的用户打开命名空间时,您想要使一些自由绑定值可用。这是属性非常有用的地方,因为我认为库通常应该导出名称空间中的所有定义,但出于某些目的,您需要导出并且不能在内部定义值命名空间。

以下是F# async extensions中定义计算构建器的示例,因此需要导出asyncSeq值(但同时,所有功能都包含在命名空间中):

namespace FSharp.Async

type AsyncSeq<'T> = (* ... *)
type AsyncSeqBuilder() = (* ... *)

[<AutoOpen>]
module GlobalValues = 
  let asyncSeq = AsyncSeqBuilder()

图书馆的用户只需撰写open FSharp.Async,他们就会看到asyncSeq。我认为相同的模式用于各种数学库(您还希望导出简单命名的函数。)

对于模块(例如ListSeq),我认为大多数人不使用open并通过模块名称访问这些功能(例如List.map ),所以虽然你可以将它用于嵌套模块,但我没有经常看到它。

答案 1 :(得分:20)

它可用于将模块组织成子模块,但在外部呈现统一/单模块视图:

module Outer =

  [<AutoOpen>]
  module Inner1 =
    let f1() = ()

  [<AutoOpen>]
  module Inner2 =
    let f2() = ()

open Outer

let x = f1()
let y = f2()

FParsec执行此操作:open FParsec打开所有子模块(PrimitivesCharParsers等。)

答案 2 :(得分:0)

聚会晚了一点,但是我想添加另一种用法。

我倾向于使用[<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模块,以仅访问与其他类型组成的类型。

我更喜欢这种解决方案,而不是使用静态成员来扩充类型。