A .NET family of libraries for replacing primitive obsession with strongly-typed, self-validating domain models. Each pillar ships as its own NuGet package, so you take only what you need.
- Semantic Strings: type-safe wrappers like
EmailAddress,UserId,BlogSlugwith attribute-driven validation, plus a batteries-included identifiers package (Uuid,Ulid,Iban,Isbn,CreditCardNumber,JwtToken). - Semantic Paths: a polymorphic
IPathhierarchy for files, directories, absolute, relative, and combinations. - Semantic Quantities: a metadata-generated, type-safe quantity system with a unified
IVector0..IVector4model covering 60+ physical dimensions and 200+ generated types. Optional per-storage-type alias packages let you writeMassinstead ofMass<double>. - Semantic Music: immutable musical value types (
Pitch,Interval,Scale,Chord,Key,Duration,TimeSignature), with chord-symbol parsing and voicing, plus a harmonic and structural analysis layer. - Semantic Color: a physically-grounded
Colortype with linear-RGB math, perceptual Oklab operations, and built-in WCAG accessibility checks.
Each package has its own README with the full API surface, examples, and reference tables. This document is the family overview.
| Package | What it provides | Details |
|---|---|---|
ktsu.Semantics.Strings |
SemanticString<T> framework and validation attributes |
README |
ktsu.Semantics.Strings.Identifiers |
Ready-made identifier types (Uuid, Iban, Isbn, ...) |
README |
ktsu.Semantics.Paths |
Polymorphic, typed file system path types | README |
ktsu.Semantics.Quantities |
Generated, dimensionally-safe physical quantities | README |
ktsu.Semantics.Quantities.Double |
double storage-type aliases |
README |
ktsu.Semantics.Quantities.Float |
float storage-type aliases |
README |
ktsu.Semantics.Quantities.Decimal |
decimal storage-type aliases |
README |
ktsu.Semantics.Music |
Musical value types and harmonic analysis | README |
ktsu.Semantics.Color |
Linear/perceptual color with accessibility tooling | README |
All packages target net8.0–net10.0. Strings, Identifiers, Paths, Music, and Color additionally target netstandard2.0/netstandard2.1. Quantities and its alias packages are net8.0+ (they require INumber<T>).
Define a string-shaped domain type, attach validation attributes, and get a compile-time-distinct type that validates on construction.
using ktsu.Semantics.Strings;
[IsEmailAddress]
public sealed record EmailAddress : SemanticString<EmailAddress> { }
[StartsWith("USER_"), HasNonWhitespaceContent]
public sealed record UserId : SemanticString<UserId> { }
EmailAddress email = EmailAddress.Create("user@example.com");
UserId userId = UserId.Create("USER_12345");
public void SendWelcomeEmail(EmailAddress to, UserId who) { /* ... */ }
// SendWelcomeEmail(userId, email); // does not compileThe ktsu.Semantics.Strings.Identifiers package adds ready-made identifier types with real check-digit and structural validation:
using ktsu.Semantics.Strings.Identifiers;
Uuid id = Uuid.Create("123E4567-E89B-12D3-A456-426614174000"); // canonicalised to lowercase
Iban iban = Iban.Create("GB82 WEST 1234 5698 7654 32"); // whitespace stripped, mod-97 validatedFull detail: Semantics.Strings README and Semantics.Strings.Identifiers README.
A path is a type that encodes whether it names a file or a directory and whether it is absolute or relative. Compose them with /, decompose with typed properties, and hold mixed paths behind IPath.
using ktsu.Semantics.Paths;
AbsoluteDirectoryPath projectDir = AbsoluteDirectoryPath.Create(@"C:\repos\app");
AbsoluteFilePath source = projectDir / RelativeFilePath.Create(@"src\Program.cs");
FileName name = source.FileName; // Program.cs
FileExtension ext = source.FileExtension; // .cs
RelativeFilePath rel = source.AsRelative(projectDir);Full detail: Semantics.Paths README.
Every quantity is a vector whose direction-space dimensionality is part of the type. Operators flow from a metadata definition, so Force · Displacement yields Energy at compile time.
using ktsu.Semantics.Quantities;
Mass<double> m = Mass<double>.FromKilogram(2.0);
Speed<double> v = Speed<double>.FromMeterPerSecond(3.0);
MomentumMagnitude<double> p = m * v; // Mass * Speed -> MomentumMagnitude
Force3D<double> f = new() { X = 3.0, Y = 4.0, Z = 0.0 };
ForceMagnitude<double> mag = f.Magnitude(); // 5.0If a project uses one storage type throughout, reference an alias package and drop the generic argument entirely:
using ktsu.Semantics.Quantities; // with ktsu.Semantics.Quantities.Double referenced
Mass mass = Mass.FromKilogram(10.0);
Speed speed = Speed.FromMeterPerSecond(15.0);Full detail: Semantics.Quantities README. The unified vector model and generator are documented in docs/strategy-unified-vector-quantities.md and docs/physics-generator.md.
Immutable, validated musical value types plus an analysis layer that models harmony nested inside structure. Pure logic, no I/O. The pitch convention is MIDI 60 = C4.
using ktsu.Semantics.Music;
Progression prog = Progression.Parse("| Dm7 | G7 | Cmaj7 |");
Key key = prog.InferKey()!; // C major
IReadOnlyList<string> roman = prog.RomanNumerals(key); // ii7, V7, Imaj7
Chord cmaj7 = Chord.Parse("Cmaj7");
IReadOnlyList<Pitch> voicing = cmaj7.Voice(octave: 4); // C4, E4, G4, B4Full detail: Semantics.Music README.
The canonical Color stores linear RGBA, so mixing, interpolation, and luminance are physically correct. Perceptual work happens in Oklab, and WCAG accessibility tooling is built in.
using ktsu.Semantics.Color;
Color text = Color.FromHex("#777777");
Color background = NamedColors.White;
if (text.AccessibilityLevelAgainst(background) < AccessibilityLevel.AA)
{
text = text.AdjustForContrast(background, AccessibilityLevel.AA);
}
IReadOnlyList<Color> ramp = NamedColors.Red.Gradient(NamedColors.Blue, 5); // Oklab gradientFull detail: Semantics.Color README.
services.AddTransient<ISemanticStringFactory<EmailAddress>, SemanticStringFactory<EmailAddress>>();
public class UserService(ISemanticStringFactory<EmailAddress> emails)
{
public User CreateUser(string raw) =>
emails.TryFromString(raw, out EmailAddress? email)
? new User(email!)
: throw new ArgumentException("invalid email");
}The quantity system is metadata-driven. The single source of truth is Semantics.SourceGenerators/Metadata/dimensions.json (with units.json, magnitudes.json, conversions.json, domains.json, and logarithmic.json alongside it), and a Roslyn incremental generator emits the quantity records, unit-conversion factories, cross-dimensional operators, and physical constants. Generated output is committed to Semantics.Quantities/Generated/ so the project compiles without first running the generator.
The string and path systems share an attribute → strategy → rule → factory validation pipeline. See docs/architecture.md.
- Complete library guide (start here for a feature tour).
- Architecture (strings/paths/validation)
- Architecture (physics, unified vector model)
- Source-generator workflow
- Physics domains tour
- Validation attributes reference
- Advanced usage patterns
Per-package API detail lives in each package's own README, linked from the Packages table above.
Contributions are welcome. Please open an issue first for major changes so we can discuss the direction. The branch's open work items are tracked as GitHub issues.
MIT. See LICENSE.md.