【问题标题】:AutoOpen attribute in F#F# 中的 AutoOpen 属性
【发布时间】:2012-10-19 15:23:41
【问题描述】:

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

(这个问题大概是when to use module function VS static function on similarly named type的延续)

Expert F# 指出“这在您定义临时顶级运算符时很有用 和功能:"

所以这似乎是为了削弱模块在代码组织中的作用,当您在技术上需要一个来编写代码但您从客户的角度删除了它的存在时。

还有别的吗? 什么时候用?

【问题讨论】:

    标签: f# code-organization


    【解决方案1】:

    我认为AutoOpen 属性的主要用途是当您希望在库的用户打开命名空间时使某些 let-bound 值可用。这是该属性非常有用的地方,因为我认为库通常应该导出 namespaces 中的所有定义,但出于某些目的,您需要导出 values 并且不能在其中定义值命名空间。

    这是来自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)来访问功能,所以虽然你可以使用这个对于嵌套模块,我没有经常看到这种情况。

    【讨论】:

      【解决方案2】:

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

      module Outer =
      
        [<AutoOpen>]
        module Inner1 =
          let f1() = ()
      
        [<AutoOpen>]
        module Inner2 =
          let f2() = ()
      
      open Outer
      
      let x = f1()
      let y = f2()
      

      FParsec 这样做:open FParsec 打开所有子模块(PrimitivesCharParsers 等)。

      【讨论】:

        【解决方案3】:

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

        我倾向于使用[&lt;AutoOpen&gt;] 在命名空间中公开类型。

        // 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 模块也可以自己打开,只访问与其他类型组合的类型。

        比起使用静态成员扩充类型,我更喜欢这种解决方案。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-18
          • 2011-11-19
          • 1970-01-01
          • 2015-05-04
          • 1970-01-01
          • 2013-07-12
          • 1970-01-01
          相关资源
          最近更新 更多