Note. At the end, I added a lot of Of interest comments. This does not mean that you should use inline and static type parameters willy nilly , they are, so you do not need to spend hours looking for a lot of questions related to this issue in order to better understand these concepts.
I know that when you need to create a generic function and you need a value of zero (0), F # provides GenericZero .
Allows a null value for any primitive numeric type or any type with a static member of Zero.
So, this leads me to believe that in order to use GenericZero with a string type, I need to add a static member called Zero.
Since System.String is part of the .NET environment, modifying the .Net source code is not what should be done. However, F # provides Type Extensions .
Type extensions allow you to add new members to a previously defined type object.
F # also provides a String module , but it lacks GenericZero.
For a good guide on linking to extension types: Attaching functions to types .
Code I tested:
This is in a project called 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"
This is in a project called 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
when performing outputs:
something: a staticProperty: b zeroProperty: c intAdd: 2 floatAdd: 2.0 Press any key to exit
Thus, I create and get access to extension members without using GenericZero.
The last part is to use GenericZero for the string, however uncommenting the string
let stringAdd = addTest "a" LanguagePrimitives.GenericZero
leads to an error:
Type 'string' does not support operator 'get_Zero'
I checked F # spec but found nothing.
Can I add GenericZero to enter System.String , did I do something wrong in the code, or did I miss something in the documentation?
TL DR
SO questions of interest when searching
F # - How to extend a type with get_Zero so that I can use the existing type as a whole?
which IMO is a misleading name that should be changed after reading the answers.
What / where get_Zero in F # int?
which has a good comment from Jack:
Zero doesn't make sense in a string if you think about it in terms of numbers; however, it makes sense to have a Zero member in the string that returns an empty string, as this will make the string monoid when the strings are concatenated.
F # documentation of interest
Automatic generalization
The F # compiler, when it performs type inference in a function, determines whether this parameter can be shared. The compiler checks each parameter and determines whether the function depends on the specific type of this parameter. If this is not the case, the type is displayed as generic.
Type request
The idea of type inference is that you do not need to specify the types of F # constructs, unless the compiler cannot finally infer the type.
For those types that you do not explicitly specify, the compiler introduces a type based on context. Unless the type is otherwise specified, it is assumed that it is a generic one.
General information
F # function values, methods, properties, and types of aggregates, such as classes, records, and discriminatory associations can be common. generic constructs contain at least one type parameter, which is usually provided by the user of the generic construct. Common functions and types allow you to write code that works with different types without repeating code for each type. Creating shared code can be simple in F #, because often your code is implicitly defined as generic by outputting a compiler type and automatically generalizing mechanisms.
Static Resolution Options
A static type parameter is a type parameter that is replaced with the actual type at compile time, and not at run time. They are preceded by a carriage symbol (^).
Parameters of statically permitted types are primarily useful in conjunction with member constraints , which are constraints that allow you to specify that the type argument must have a specific member or members to use. It is not possible to create such a type using a regular typical type parameter.
In F #, there are two different types of type parameters. The first type is a standard type parameter. These are indicated by an apostrophe ('), as in' T 'and' U. They are equivalent to parameters of a general type in other .NET Framework languages. another view is statically permitted and is indicated by a carriage symbol, as in ^ T and ^ U.
Limitations
Built-in functions
When using parameters of a static type, any functions that are parameterized by type parameters must be inline .
EDIT
Here is an example that uses GenericZero for a user-defined type without using an extension that works, and two options showing that GenericZero does NOT work for intrinsic extension and optional extension
First run the program to see GenericZero working, then uncomment the lines in Program.fs to see errors for intrinsic extension and optional extension .
an internal 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 an extensible type.
Optional extension is an extension that appears outside of the original module, namespace, or assembly of an extensible type. Internal extensions appear in the type when the type is checked for reflection, but there are no optional extensions. Additional extensions must be in the modules, and they are only available to the extent that the module that contains the extension is open.
In Library1.fs in the project 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")
in Library2.fs in the project 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")
in Program.fs in the Workspace project
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