F# 2.0 Generic Inlining With Member Constraints
In this post, we are going to create a
Converter class who converts values to strings. The conversion rules are speciﬁed by passing the name of a culture to the class' constructor. The culture name can contain just the language (as ISO 639-1 alpha-2 code, e. g. "en") or the language and region (as ISO 3166 code, e. g. "US") combined with hyphen, e. g. "en-US". An empty culture name "" specifies the invariant culture, which falls back to "en".
The class has only one conversion method
ToString, which is generic. It takes a single parameter, which is the value to be converted. The parameter's type is inferred to have a member constraint, who restricts the type (at compile time) to have an instance member with the signature
ToString: IFormatProvider -> string. The parameter’s member is invoked with a member constraint invocation expression (see § 6.4.8. in The F# 2.0 Language Specification).
open System open System.Globalization type Converter(cultureName:string) = /// The culture used by this converter. member val Culture = CultureInfo.GetCultureInfo cultureName /// Converts value to a string, based on the specified culture. member inline self.ToString value = (^T: (member ToString: IFormatProvider -> string) value, self.Culture) // Test let germanConverter = Converter "de" let nrString = germanConverter.ToString 1234.643 // "1234,643" let dtString = germanConverter.ToString <| DateTime(2003, 11, 25, 17, 38, 47) // "25.11.2003 17:38:47"
Update (Oct 15, 2012)
The converter's culture is now exposed via a
public property, based on F# 3.0 auto property syntax. (The Culture's value is evaluated only once, during class construction time.) Previously, the culture was a
private field, and the example could not compile in a regular source file, due to access rule violation. Strangely, it did compile in a scripting file; this is an example where the scripting compiler (wrongly) does not behave in the exact same way as the regular compiler.