feat(quantities): IPhysicalQuantity surface + typed In() (closes #59)#80
Merged
Merged
Conversation
…loses #59) Implement the IPhysicalQuantity<T> contract decided in #59 with compile-time dimension safety on unit conversion. Runtime: - IPhysicalQuantity<T> now exposes Dimension (DimensionInfo), IComparable, and IEquatable; CompareTo throws on cross-dimension comparison, Equals returns false (equality is total). - IUnit gains Name/Symbol/Dimension/ToBaseFactor/ToBaseOffset plus default ToBase<T>/FromBase<T> methods over the affine conversion. - New UnitConversionException type (reserved for any future untyped path). Generator: - DimensionsGenerator emits a marker interface per dimension (I{Dim}Unit : IUnit) so units can be dimensionally typed at compile time. - UnitsGenerator now combines units.json with dimensions.json so each unit implements the correct I{Dim}Unit marker(s); emits class records (not structs) with full property surface (folding metric magnitude + conversionFactor into ToBaseFactor) plus a static Units catalogue of singletons. - QuantitiesGenerator emits Dimension override + typed In(I{Dim}Unit) on every V0/V1 base and overload type. Cross-dimension In() calls fail to compile. Tests + docs: - PhysicalQuantityCoreTests rewritten against the new compile-time API. - docs/architecture.md documents the IPhysicalQuantity contract and the typed In(I{Dim}Unit) pattern. The previously aspirational test's runtime "should throw UnitConversionException" case is now expressed at compile time (length.In(Units.Kilogram) doesn't build). V2/V3/V4 vector types don't implement IPhysicalQuantity and are out of scope for In() — per-component conversion is left as follow-up work. https://claude.ai/code/session_01LqtywMUn5GwFATD5FDdLn6
…arning CI flagged 9 IDE0370 "Suppression is unnecessary" findings on the null-forgiving operator (`!`) applied to return expressions in SemanticQuantity.cs and a reflection call site in SemanticString.cs. Create<TResult>() is non-nullable already, so the suppression is noise. Also adds `if-no-files-found: ignore` to the Upload Coverage Report step so runs that don't produce coverage don't surface a workflow warning. https://claude.ai/code/session_01LqtywMUn5GwFATD5FDdLn6
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
This PR contains the actual #59 implementation plus a small cleanup. The earlier PR #79 merged with only the docs commit; the implementation work was pushed to the same branch after merge and never went through CI on its own.
Closes #59 — canonical IPhysicalQuantity surface + typed In()
Implement the
IPhysicalQuantity<T>contract decided in #59 with compile-time dimension safety on unit conversion.Runtime:
IPhysicalQuantity<T>now exposesDimension(DimensionInfo),IComparable<IPhysicalQuantity<T>>,IEquatable<IPhysicalQuantity<T>>.CompareTothrows on cross-dimension comparison;Equalsreturnsfalse(equality is total).IUnitgainsName/Symbol/Dimension/ToBaseFactor/ToBaseOffsetplus default-implementedToBase<T>/FromBase<T>over the affine conversionbase = value × factor + offset.UnitConversionExceptiontype (reserved for any future untyped path).Generator:
DimensionsGeneratoremits a marker interface per dimension (I{Dim}Unit : IUnit).UnitsGeneratorcombines units.json with dimensions.json so each unit implements the rightI{Dim}Unitmarker(s). Emits class records with the full property surface, folds metric magnitude + conversionFactor into a singleToBaseFactor, and emits a staticUnitscatalogue of singletons.QuantitiesGeneratoremitsDimensionoverride + typedIn(I{Dim}Unit)on every V0/V1 base + V0/V1 overload type.Cleanup commit
CI flagged 9 IDE0370 "Suppression is unnecessary" findings on the
!null-forgiving operator on return expressions inSemanticQuantity.csand a reflection call site inSemanticString.cs.Create<TResult>()is non-nullable, so the suppression was noise. Also addsif-no-files-found: ignoreto the Upload Coverage Report step so runs that don't produce coverage don't surface a workflow warning.Out of scope (follow-up)
In()(vector types don't implementIPhysicalQuantity).Heads-up — landed without local build verification
The sandbox build is blocked by an analyzer mismatch unrelated to this change (existing committed files fail IDE0055 on net8 with the current SDK). Generator changes shipped blind. CI is the source of truth on this one.
Test plan
Semantics.Quantitiesfor all four TFMs (net7/8/9/10).PhysicalQuantityCoreTests(Length+Kilometer = 0.01, Temperature+Celsius = 26.85, CompareTo / Equals cross-dim behaviour).!suppressions doesn't introduce CS86xx nullable warnings.Semantics.Quantities/Generated/and verify the newDimension+In(...)members appear on every V0/V1 type.https://claude.ai/code/session_01LqtywMUn5GwFATD5FDdLn6
Generated by Claude Code