F#中的惯用语构造函数

Mai*_*ein 5 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 }
Run Code Online (Sandbox Code Playgroud)

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

喜欢

type VAO = 
    { Handle : int }

    static member Create vboList ?ibo =
     ........
Run Code Online (Sandbox Code Playgroud)

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

我想知道我是否首先编写了惯用的F#代码.你会如何解决这个"问题"?

Jac*_* P. 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)
Run Code Online (Sandbox Code Playgroud)

另一种方法是定义一个模块(它需要放在你的VAO类型的定义下面),它定义了"包装"静态VBO.Create(...)方法的curried函数.这将工作但是你决定声明参数Create(即,使用带有元组样式参数声明和可选参数的单个方法,或使用上述的重载方法).例如,如果您不使用我的重载方法,并决定只定义Createstatic 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)
Run Code Online (Sandbox Code Playgroud)

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

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