Logo Xantham

Name — modified-source-preserving identifiers

The decoder threads a Name value type through anywhere an identifier is exposed. Name carries either:

This dual representation lets generators rename or normalize identifiers without losing the original spelling — which is often required for output attributes ([<CompiledName "...">]), error messages, or round-tripping.

Creating names

let n1 = Name.create "fooBar", Name.create "type" // backtick-normalized if needed
let n2 = Name.Create "fooBar", Name.Create "type" // raw, no normalization
let n3 = Name.Create("type", "``type``")
let n4 = Name.createModified "type" "``type``"
{ n1 = (Source "fooBar", Modified ("type", "``type``"))
  n2 = (Source "fooBar", Source "type")
  n3 = Modified ("type", "``type``")
  n4 = Modified ("type", "``type``") }

Name.create runs the active normalization (default: F# backtick quoting via NormalizeIdentifierBackticks). Use the static Create if you need the raw form.

Reading the value back

Two accessors:

let original = n3.ValueOrSource     // "type"
let display  = n3.ValueOrModified   // "``type``"
{ display = "``type``"
  original = "type" }

Mapping over names

Name exposes three map functions that differ in which string they transform and how the result is wrapped:

Pre-baked normalisations

Several common transforms are provided:

Function

Behaviour

Name.normalize

Apply the active normalization (default backticks).

Name.pascalCase / sourcePascalCase

PascalCase the modified / source value.

Name.camelCase / sourceCamelCase

camelCase the modified / source value.

Name.capitalize / sourceCapitalize

Uppercase first character only.

Name.normalizeForType

Alias for pascalCase.

Name.normalizeForParameter

Alias for camelCase.

Name.normalizeForProperty / Method / EnumCase

Alias for the appropriate casing.

Name.normalizeForTypeParameter

PascalCase + leading single quote.

Name.mapToModuleName

PascalCase + leading I, used for module-as-interface naming.

Each for… helper has a source… variant that operates on the original string regardless of any prior modification.

Customising normalization

The active normalization is global mutable state and can be swapped at process startup via Name.Normalization.setNormalizeSetting:

open Xantham.Decoder.Name.Normalization

setNormalizeSetting (SafeCustom (fun s -> s.Replace("$", "_")))
Name.create "$foo"
Modified ("$foo", "_foo")

Three modes:

Casing as a unit of measure

When you want the type of a Name to encode its casing, the decoder offers units of measure under Case:

These are tags only — they perform no transformation by themselves. Combine them with the strongly-typed sub-modules to get both the cast and the transformation:

let typeName : Name<Case.pascal> = Name.Pascal.create "foo_bar"
let propName : Name<Case.camel>  = Name.Camel.create  "FooBar"
let modName  : Name<Case.modulename> = Name.Module.create "foo"   // → IFoo
let typar    : Name<Case.typar>  = Name.Typar.create  "T"          // → 'T
typeName = Modified ("foo_bar", "FooBar")
propName = Modified ("FooBar", "fooBar")
modName = Modified ("foo", "IFoo")
typar = Modified ("T", "'T")

The four sub-modules (Name.Pascal, Name.Camel, Name.Module, Name.Typar) each provide:

For runtime dispatch over an unknown casing, use the CasedName DU:

let any : CasedName = CasedName.Pascal typeName
let underlying : Name = any.Value

Working with cased names generically

Name.Case (the inner module) has measure-aware versions of the common accessors so you don't have to drop measures manually:

let v1 = Name.Case.valueOrModified typeName
let v2 = Name.Case.map (fun s -> s + "Suffix") propName
let isMod = Name.Case.isModified modName

Forcing measures

Case is a module that provides the unsafe primitives for working with cased names and powers the strongly typed sub-modules Name.Pascal, Name.Camel, Name.Module, and Name.Typar.

It DOES NOT perform any transformation on the underlying Name, and so should be avoided in general use.

One scenario this is useful for is when you have a strongly typed field such as a field representing a member name which is typed as Name<Case.camel>, but you need to render a special meaning name such as Invoke or Item.

// Trying to create a camel cased member named "Invoke"
Name.Camel.create "Invoke" // Name<Case.camel>
|> printfn "❌ %A"

Name.Pascal.create "Invoke" // Name<Case.pascal>
|> Name.Camel.fromCase // Name<Case.camel>
|> printfn "❌ %A"

Name.create "Invoke" // Name
|> Case.addCamelMeasure // Name<Case.camel>
|> printfn "✔️ %A"
❌ Modified ("Invoke", "invoke")
❌ Modified ("Invoke", "invoke")
✔️ Source "Invoke"
namespace Xantham
namespace Xantham.Decoder
val n1: Name * Name
Multiple items
module Name from Xantham.Decoder
<summary></summary>
<category index="3">Names and Casing</category>


--------------------
type Name = | Modified of original: string * modified: string | Source of original: string static member Create: value: string -> Name + 1 overload static member CreateModified: original: string * modified: string -> Name member ValueOrModified: string member ValueOrSource: string
<summary> Utility type for working with names or manipulating the names of types and members while preserving the original source. </summary>
<category index="3">Names and Casing</category>


--------------------
type Name<'u> = Name
<summary>Provide static typing over the casing of a name</summary>
<category index="3">Names and Casing</category>
val create: value: string -> Name
<summary> Creates a Name from a string. Will automatically normalize the name with backticks if required.. Use the static member Create if you don't want automatic normalization. </summary>
val n2: Name * Name
static member Name.Create: value: string -> Name
static member Name.Create: original: string * modified: string -> Name
val n3: Name
val n4: Name
val createModified: original: string -> modified: string -> Name
<summary> Creates a modified Name DU. </summary>
val original: string
property Name.ValueOrSource: string with get
<summary>The original source string regardless of whether the name has been modified.</summary>
val display: string
property Name.ValueOrModified: string with get
<summary>The modified string if the name has been modified; otherwise the original source string.</summary>
module Normalization from Xantham.Decoder.NameModule
val setNormalizeSetting: newSetting: Setting -> unit
union case Setting.SafeCustom: (string -> string) -> Setting
val s: string
System.String.Replace(oldValue: string, newValue: string) : string
System.String.Replace(oldChar: char, newChar: char) : string
System.String.Replace(oldValue: string, newValue: string, comparisonType: System.StringComparison) : string
System.String.Replace(oldValue: string, newValue: string, ignoreCase: bool, culture: System.Globalization.CultureInfo) : string
val typeName: Name<Case.pascal>
module Case from Xantham.Decoder
<summary> This provides unsafe means of removing/adding measures to a <c>Name</c>. Use with caution, as they are not associated with any transformations of the underlying strings. </summary>
<category index="3">Names and Casing</category>
type pascal
<summary>Measure to signify Pascal casing.</summary>
<category index="3">Names and Casing</category>
module Pascal from Xantham.Decoder.NameModule
<summary> Provides means for working with Pascal case measure annotated names directly. </summary>
val create: (string -> Name<Case.pascal>)
<summary>Normalize a string into a <c>Name&lt;pascal&gt;</c>.</summary>
val propName: Name<Case.camel>
type camel
<summary>Measure to signify camel casing.</summary>
<category index="3">Names and Casing</category>
module Camel from Xantham.Decoder.NameModule
<summary> Provides means for working with Camel case measure annotated names directly. </summary>
val create: (string -> Name<Case.camel>)
<summary>Normalize a string into a <c>Name&lt;camel&gt;</c>.</summary>
val modName: Name<Case.modulename>
type modulename
<summary> Measure to signify the name is modified to represent the module interface. This is used to distinguish between the module interface and the module itself. </summary>
<category index="3">Names and Casing</category>
module Module from Xantham.Decoder.NameModule
<summary> Provides means for working with Module measure annotated names directly. </summary>
val create: (string -> Name<Case.modulename>)
<summary>Normalize a string into a <c>Name&lt;modulename&gt;</c>.</summary>
val typar: Name<Case.typar>
type typar
<summary>Measure to signify a type parameter (prefixed with a single quote).</summary>
<category index="3">Names and Casing</category>
module Typar from Xantham.Decoder.NameModule
<summary> Provides means for working with Typar measure annotated names directly. </summary>
val create: (string -> Name<Case.typar>)
<summary>Normalize a string into a <c>Name&lt;typar&gt;</c>.</summary>
val nameof: 'T -> string
val unbox: value: objnull -> 'T
Multiple items
module List from Microsoft.FSharp.Collections

--------------------
type List<'T> = | op_Nil | op_ColonColon of Head: 'T * Tail: 'T list interface IReadOnlyList<'T> interface IReadOnlyCollection<'T> interface IEnumerable interface IEnumerable<'T> member GetReverseIndex: rank: int * offset: int -> int member GetSlice: startIndex: int option * endIndex: int option -> 'T list static member Cons: head: 'T * tail: 'T list -> 'T list member Head: 'T member IsEmpty: bool member Item: index: int -> 'T with get ...
val iter: action: ('T -> unit) -> list: 'T list -> unit
val x: string * obj
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
val any: CasedName
type CasedName = | Pascal of Name<pascal> | Camel of Name<camel> | Module of Name<modulename> | Typar of Name<typar> member Value: Name
<summary> A <c>Name</c> tagged with one of the supported casing measures (Pascal, camel, module, or typar). Useful as an envelope when the casing flavour is decided dynamically rather than known statically. </summary>
<category index="3">Names and Casing</category>
union case CasedName.Pascal: Name<Case.pascal> -> CasedName
<summary> A name carrying the Pascal-case measure (e.g. type, module, or class names). </summary>
val underlying: Name
property CasedName.Value: Name with get
<summary>The underlying untyped <c>Name</c>, dropping the casing measure.</summary>
val v1: string
module Case from Xantham.Decoder.NameModule
<summary> Provides some equivalency functions for working with Names that have measures. </summary>
val valueOrModified: name: Name<'u> -> string
val v2: Name
val map: fn: (string -> string) -> name: Name<'u> -> Name
val isMod: bool
val isModified: name: Name<'u> -> bool
val fromCase: name: Name<'u> -> Name<Case.camel>
<summary>Re-cast any cased name to a <c>Name&lt;camel&gt;</c>, applying camelCase normalization.</summary>
val addCamelMeasure: name: Name -> Name<Case.camel>
<summary>Tag a <c>Name</c> with the <c>camel</c> casing measure.</summary>

Type something to start searching.