F#中的惯用语构造函数

时间:2014-02-02 17:09:41

标签: f# inline

我从一个小的OpenGL包装器开始,我遇到了一个小问题。我通常用这样的记录创建我的数据

type Shader = 
    { Handle : int }
    static member Create filepath stype = 
        let h = loadshader filepath stype
        { Handle = h }

type VertexShader = 
    { shader : Shader }
    static member Create path = 
        { shader = Shader.Create path ShaderType.VertexShader }

type FragmentShader = 
    { shader : Shader }
    static member Create path = 
        { shader = Shader.Create path ShaderType.FragmentShader }

我总是使用具有非tupled函数的静态构造函数。但是现在我想要另一种我希望有可选参数的类型。

喜欢

type VAO = 
    { Handle : int }

    static member Create vboList ?ibo =
     ........

问题是,使用非tupled函数似乎不可能这样做,我不想将我的Create方法与tupled和non-tupled函数混合使用。

我想知道我是否首先编写了惯用的F#代码。你会如何处理这个“问题”?

1 个答案:

答案 0 :(得分:6)

你是对的 - 如果你想为方法定义可选参数,你需要以tupled形式定义参数。您可以采用的一种方法是将Create方法的实现移动到私有方法中(并将其重命名为CreateImpl),然后将Create重新定义为重载方法,只需调度到实现。例如:

type VAO =
    { Handle : int }

    static member private CreateImpl (vboList, ibo : _ option) =
        failwith "Not implemented."

    static member Create vboList =
        VAO.CreateImpl (vboList, None)

    static member Create (vboList, ibo) =
        VAO.CreateImpl (vboList, Some ibo)

另一种方法是定义一个模块(它需要放在你的VAO类型的定义之下),它定义了“包装”静态VBO.Create(...)方法的curried函数。然而,这将工作,你决定声明Create的参数(即,使用带有元组样式参数声明和可选参数的单个方法,或使用上述的重载方法)。例如,如果您不使用我的重载方法,并决定将Create定义为static member Create (vboList, ?ibo)

// The [<CompilationRepresentation>] attribute is necessary here, because it allows us to
// define a module which has the same name as our type without upsetting the compiler.
// It does this by suffixing "Module" to the type name when the assembly is compiled.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix>]
module VAO =
    let create (vboList : _ list) =
        VAO.Create vboList

    let createWithIndex vboList ibo =
        VAO.Create (vboList, ibo)

这些模块函数非常简单,因此它们很可能会被F#编译器自动内联,并且您不会为它们支付任何运行时成本。如果在Release模式下编译,检查程序集的IL并发现情况并非如此,那么可以将inline关键字添加到函数定义中以强制它们内联。 (除非你进行基准测试并确定它能提供真正的性能提升,否则最好不要强制内联。)

与您关于可选参数的问题无关的一个提示 - 考虑将至少一些类型重新定义为结构而不是F#记录。记录类型被编译成类(即引用类型),因此您无缘无故地支付额外的间接成本;将它们定义为结构将消除这种间接性并为您的应用程序提供更好的性能。更好的是,如果你能够,重用现有的一个OpenGL包装器库,如OpenTKOpenGL4Net