gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 08:22:52 26 4
gpt4 key购买 nike

注意:我在末尾添加了许多Of interest注释。这些并不是要建议一个人使用inlinestatic type parameters willy nilly,所以他们不必花大量时间搜索与该问题相关的大量SO问题就可以更好地理解这些概念。

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

Resolves to the zero value for any primitive numeric type or any type with a static member called Zero.



因此,这使我相信使用字符串类型的 GenericZero只需添加一个名为Zero的静态成员。

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

Type extensions let you add new members to a previously defined object type.



F#还提供了 String module,但是缺少GenericZero。

有关创建类型扩展的良好教程,请引用: Attaching functions to types

我测试过的代码:

这在一个名为 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"

这在一个名为 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

运行时输出:
something: a
staticProperty: b
zeroProperty: c
intAdd: 2
floatAdd: 2.0
Press any key to exit

因此,我正在创建和访问扩展成员,并使用GenericZero没有任何问题。

最后一部分是对字符串使用GenericZero,但是取消注释行
let stringAdd = addTest "a" LanguagePrimitives.GenericZero

导致错误:

The type 'string' does not support the operator 'get_Zero'



我确实检查了 F# spec,但没有发现任何帮助。

我可以将GenericZero添加为 System.String类型吗,我在代码中做错了什么吗?还是我错过了文档中的某些内容?

TL; DR

这样搜索时感兴趣的问题

F# - How do I extend a type with get_Zero so I can use an existing type generically?
该IMO是一个误导性标题,应在阅读答案后进行更改。

What/where is get_Zero in F#'s int?
jack 有一个很好的评论:

Zero doesn't make sense on string if you think of it in terms of numerics; however, it does make sense to have a Zero member on string which returns the empty string, as that would make string a monoid under string concatenation.



感兴趣的F#文档

Automatic Generalization

The F# compiler, when it performs type inference on a function, determines whether a given parameter can be generic. The compiler examines each parameter and determines whether the function has a dependency on the specific type of that parameter. If it does not, the type is inferred to be generic.



Type Inference

The idea of type inference is that you do not have to specify the types of F# constructs except when the compiler cannot conclusively deduce the type.

For those types that you do not specify explicitly, the compiler infers the type based on the context. If the type is not otherwise specified, it is inferred to be generic.



Generics

F# function values, methods, properties, and aggregate types such as classes, records, and discriminated unions can be generic. Generic constructs contain at least one type parameter, which is usually supplied by the user of the generic construct. Generic functions and types enable you to write code that works with a variety of types without repeating the code for each type. Making your code generic can be simple in F#, because often your code is implicitly inferred to be generic by the compiler's type inference and automatic generalization mechanisms.



Statically Resolved Type Parameters

A statically resolved type parameter is a type parameter that is replaced with an actual type at compile time instead of at run time. They are preceded by a caret (^) symbol.

Statically resolved type parameters are primarily useful in conjunction with member constraints, which are constraints that allow you to specify that a type argument must have a particular member or members in order to be used. There is no way to create this kind of constraint by using a regular generic type parameter.

In the F# language, there are two distinct kinds of type parameters. The first kind is the standard generic type parameter. These are indicated by an apostrophe ('), as in 'T and 'U. They are equivalent to generic type parameters in other .NET Framework languages. The other kind is statically resolved and is indicated by a caret symbol, as in ^T and ^U.



Constraints

Inline Functions

When you use static type parameters, any functions that are parameterized by type parameters must be inline.



编辑

这是一个示例,它使用 GenericZero作为用户定义的类型,而不使用有效的扩展名,并且两个变体表明 GenericZerointrinsic extensionoptional extension不起作用

首先运行程序以查看 GenericZero的工作原理,然后取消注释 Program.fs中的行以查看 intrinsic extensionoptional extension的错误。

An intrinsic extension is an extension that appears in the same namespace or module, in the same source file, and in the same assembly (DLL or executable file) as the type being extended.

An optional extension is an extension that appears outside the original module, namespace, or assembly of the type being extended. Intrinsic extensions appear on the type when the type is examined by reflection, but optional extensions do not. Optional extensions must be in modules, and they are only in scope when the module that contains the extension is open.



在项目 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")

在项目 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")

在项目 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

最佳答案

扩展成员不被视为成员约束解决方案的一部分,因此您很不走运。对于涉及多个类型的约束(例如(+)的约束),您可以通过使用第二种类型来解决,但是对于GenericZero的约束,没有很好的解决方法。

关于generics - 如何将LanguagePrimitives.GenericZero/get_Zero添加到System.String?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37117089/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com