如何将LanguagePrimitives.GenericZero/get_Zero添加到System.String?

Guy*_*der 8 generics extension-methods f# type-inference generic-constraints

注意:我最后添加了很多Of interest评论.这些并不是在暗示一个人应该使用inlinestatic type parameters 威利愿意不愿意,他们有这么一个没有花时间搜索大量的SO与此相关的问题,以更好地理解这些概念的问题.

我知道当需要使函数通用并且需要零(0)值时,F#提供GenericZero.

解析为任何原始数字类型的零值或具有名为Zero的静态成员的任何类型.

所以这让我相信使用GenericZero字符串类型我只需要添加一个名为Zero的静态成员.

由于System.String是.Net框架的一部分,修改.Net源代码不是应该做的.但是,F#提供了Type Extensions.

类型扩展允许您将新成员添加到先前定义的对象类型.

此外,F#提供了String模块,但缺少GenericZero.

有关创建类型扩展的好教程,请参阅:将函数附加到类型.

我测试的代码:

这是在一个名为的项目中 Library1

namespace Extension.Test

module Extensions = 

    type System.String with
        static member Something = "a"

        static member StaticProp
            with get() = "b"

        static member Zero
            with get() = "c"
Run Code Online (Sandbox Code Playgroud)

这是在一个名为的项目中 Workspace

namespace Extension.Test
module main =

    open Extensions

    [<EntryPoint>]
    let main argv = 

        let stringSomething = System.String.Something
        printfn "something: %s" stringSomething

        let staticProperty = System.String.StaticProp
        printfn "staticProperty: %s" staticProperty

        let zeroProperty = System.String.Zero
        printfn "zeroProperty: %s" zeroProperty

        let inline addTest (x : ^a) (y : ^a) : ^a =
            x + y

        let intAdd = addTest 2 LanguagePrimitives.GenericZero
        let floatAdd = addTest 2.0 LanguagePrimitives.GenericZero
//        let stringAdd = addTest "a" LanguagePrimitives.GenericZero

        printfn "intAdd: %A" intAdd
        printfn "floatAdd: %A" floatAdd
//        printfn "stringAdd: %A" stringAdd

        printf "Press any key to exit: "
        System.Console.ReadKey() |> ignore
        printfn ""

        0 // return an integer exit code
Run Code Online (Sandbox Code Playgroud)

当运行输出时:

something: a
staticProperty: b
zeroProperty: c
intAdd: 2
floatAdd: 2.0
Press any key to exit
Run Code Online (Sandbox Code Playgroud)

所以我正在创建和访问扩展成员并使用GenericZero而没有任何问题.

最后一部分是使用GenericZero作为字符串,但是取消注释该行

let stringAdd = addTest "a" LanguagePrimitives.GenericZero
Run Code Online (Sandbox Code Playgroud)

导致错误:

类型'string'不支持运算符'get_Zero'

我确实检查了F#规格,但没有找到任何帮助.

我可以添加GenericZero来输入System.String,我在代码中做错了什么,或者我是否遗漏了文档中的内容?

TL; DR

搜索时感兴趣的问题

F# - 如何使用get_Zero扩展类型,以便我可以使用现有类型?
哪个IMO是一个误导性的标题,应该在阅读答案后改变.

f#的int中get_Zero是什么/在哪里?
杰克给出了一个很好的评论:

如果从数字的角度来看,零对字符串没有意义; 但是,在string上有一个返回空字符串的Zero成员是有意义的,因为这会使字符串在字符串连接下成为一个monoid.

感兴趣的F#文档

自动泛化

F#编译器在对函数执行类型推断时,确定给定参数是否可以是通用的.编译器检查每个参数并确定该函数是否依赖于该参数的特定类型.如果没有,则推断该类型是通用的.

类型推断

类型推断的想法是您不必指定F#构造的类型,除非编译器无法最终推断出类型.

对于未明确指定的类型,编译器会根据上下文推断类型.如果未另外指定类型,则推断它是通用的.

泛型

F#函数值,方法,属性和聚合类型(如类,记录和区分联合)可以是通用的.通用构造包含至少一个类型参数,该参数通常由泛型构造的用户提供.通用函数和类型使您能够编写适用于各种类型的代码,而无需重复每种类型的代码.在F#中使代码通用可以很简单,因为编译器的类型推断和自动泛化机制通常会隐式推断您的代码是通用的.

静态分辨的类型参数

静态解析的类型参数是一个类型参数,在编译时而不是在运行时用实际类型替换.它们前面有一个插入符号(^).

静态解析的类型参数主要与成员约束结合使用,成员约束是允许您指定类型参数必须具有特定成员才能使用的约束.无法通过使用常规泛型类型参数来创建此类约束.

在F#语言中,有两种不同类型的参数.第一种是标准泛型类型参数.这些用撇号(')表示,如'T和'U. 它们等同于其他.NET Framework语言中的泛型类型参数.另一种是静态解析的并且由插入符号表示,如在^ T和^ U中.

约束

内联函数

使用静态类型参数时,由类型参数参数化的任何函数都必须是内联的.

编辑

这是一个GenericZero用于用户定义类型的示例,不使用有效的扩展,两个变体显示GenericZero不适用于intrinsic extensionoptional extension

第一次运行程序,看看GenericZero工作,然后取消注释行Program.fs看到错误的intrinsic extensionoptional extension.

固有扩展是出现在相同的命名空间或模块中,相同的源文件中的扩展,并且以相同的组件(DLL或可执行文件)作为类型被扩展.

一个可选的扩展是,原来的模块,命名空间,或类型的组件的外部显示被延伸的延伸部.当通过反射检查类型时,内部扩展显示在类型上,但可选扩展不会.可选扩展必须位于模块中,并且仅当包含扩展的模块处于打开状态时,它们才在范围内.

Library1.fs项目中Library1

namespace Extension.Test

module module001 = 

    // No extension
    type MyType01(x: string) =
        member this.x = x

        override this.ToString() = this.x.ToString()

        static member Something = MyType01("a")        
        static member (+) (mt1 : MyType01, mt2 : MyType01) = MyType01(mt1.x + mt2.x)
        static member (+) (mt1 : MyType01, s : string) = MyType01(mt1.x + s)
        static member (+) (s : string, mt2 : MyType01) = MyType01(s + mt2.x)

        static member Zero
            with get() = MyType01("b")

    // uses intrinsic extension
    type MyType02(x: string) =
        member this.x = x

        override this.ToString() = this.x.ToString()

        static member Something = MyType02("g")        
        static member (+) (mt1 : MyType02, mt2 : MyType02) = MyType02(mt1.x + mt2.x)
        static member (+) (mt1 : MyType02, s : string) = MyType02(mt1.x + s)
        static member (+) (s : string, mt2 : MyType02) = MyType02(s + mt2.x)

//        static member Zero
//            with get() = MyType02("h")

    // uses optional extension
    type MyType03(x: string) =
        member this.x = x

        override this.ToString() = this.x.ToString()

        static member Something = MyType03("m")        
        static member (+) (mt1 : MyType03, mt2 : MyType03) = MyType03(mt1.x + mt2.x)
        static member (+) (mt1 : MyType03, s : string) = MyType03(mt1.x + s)
        static member (+) (s : string, mt2 : MyType03) = MyType03(s + mt2.x)

//        static member Zero
//            with get() = MyType03("n")


module module002 = 

    open module001

    // intrinsic extension
    type MyType02 with

        static member Zero
            with get() = MyType02("h")
Run Code Online (Sandbox Code Playgroud)

Library2.fs项目Library2

namespace Extension.Test

open module001

module module003 = 

    type MyType01 with 

        static member Anything = MyType02("c")

    type MyType02 with 

        static member Anything = MyType02("i")

    // optional extension
    type MyType03 with 

        static member Anything = MyType03("p")

        static member Zero
            with get() = MyType03("n")
Run Code Online (Sandbox Code Playgroud)

Program.fs项目Workspace

namespace Workspace

open Extension.Test.module001
open Extension.Test.module002
open Extension.Test.module003

module main =

    [<EntryPoint>]
    let main argv = 


        let staticFromBaseType = MyType01.Something
        printfn "MyType01 staticFromBaseType: %A" staticFromBaseType

        let staticFromExtensionType = MyType01.Anything
        printfn "MyType01 staticFromExtensionType: %A" staticFromExtensionType

        let zeroValue = MyType01.Zero
        printfn "MyType01 zeroValue: %A" zeroValue

        let (genericZero: MyType01) = LanguagePrimitives.GenericZero
        printfn "MyType01 genericZero: %A" genericZero

        let staticFromBaseType = MyType02.Something
        printfn "MyType02 staticFromBaseType: %A" staticFromBaseType

        let staticFromExtensionType = MyType02.Anything
        printfn "MyType02 staticFromExtensionType: %A" staticFromExtensionType

        let zeroValue = MyType02.Zero
        printfn "MyType02 zeroValue: %A" zeroValue

//        let (genericZero: MyType02) = LanguagePrimitives.GenericZero
//        printfn "MyType02 genericZero: %A" genericZero


        let staticFromBaseType = MyType03.Something
        printfn "MyType03 staticFromBaseType: %A" staticFromBaseType

        let staticFromExtensionType = MyType03.Anything
        printfn "MyType03 staticFromExtensionType: %A" staticFromExtensionType

        let zeroValue = MyType03.Zero
        printfn "MyType03 zeroValue: %A" zeroValue

//        let (genericZero: MyType03) = LanguagePrimitives.GenericZero
//        printfn "MyType03 genericZero: %A" genericZero

        let inline addTest (x : ^a) (y : ^a) : ^a =
            x + y

        let intAdd = addTest 2 LanguagePrimitives.GenericZero
        let floatAdd = addTest 2.0 LanguagePrimitives.GenericZero
        let (myType01Add : MyType01) = addTest (MyType01("d")) LanguagePrimitives.GenericZero
//        let (myType02Add : MyType02) = addTest (MyType02("d")) LanguagePrimitives.GenericZero
//        let (myType03Add : MyType03) = addTest (MyType03("o")) LanguagePrimitives.GenericZero

        printfn "intAdd:      %A" intAdd
        printfn "floatAdd:    %A" floatAdd
        printfn "myType01Add: %A" myType01Add
//        printfn "myType02Add: %A" myType02Add
//        printfn "myType03Add: %A" myType03Add


        printf "Press any key to exit: "
        System.Console.ReadKey() |> ignore
        printfn ""

        0 // return an integer exit code
Run Code Online (Sandbox Code Playgroud)

kvb*_*kvb 5

扩展成员不被视为成员约束解析的一部分,所以你运气不好.对于涉及多个类型的约束(例如约束(+)),您可以通过使用第二种类型解决此问题,但是对于GenericZero没有良好解决方法的约束.


归档时间:

查看次数:

341 次

最近记录:

8 年,10 月 前