Skip to content

Commit 7693360

Browse files
committed
docs: "Type Safety in Practice" chapter updated
1 parent 8aeb718 commit 7693360

File tree

1 file changed

+136
-97
lines changed
  • docs/users_guide/systems

1 file changed

+136
-97
lines changed

docs/users_guide/systems/hep.md

Lines changed: 136 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -485,134 +485,173 @@ simple rename with no logic changes.
485485
486486
## Type Safety in Practice
487487
488-
The following example contrasts a typical `double`-based HEP API with its mp-units
489-
equivalent. It covers all four categories of bugs that specialized quantity types
490-
eliminate: dimensional errors, argument swaps, unit scale mismatches, and same-kind
491-
confusion.
488+
The following examples demonstrate four categories of bugs that specialized quantity
489+
types eliminate: dimensional errors, argument swaps, unit scale mismatches, and
490+
same-kind confusion. Each example compares legacy `double`-based code with the
491+
type-safe mp-units equivalent.
492492
493-
=== "Legacy (unsafe)"
493+
For all examples, consider this typical HEP API:
494+
495+
=== "Legacy API"
494496
495497
```cpp
496-
// Typical HEP API - all parameters are just double
498+
// All parameters are just double - units documented in comments
497499
void setParticleProperties(double mass_MeV_per_c2, // Mass in MeV/c²
498500
double width_MeV, // Decay width in MeV
499501
double charge); // Electric charge
502+
```
500503
501-
// DISASTER 1: Wrong dimensions (energy passed as mass)
502-
{
503-
double higgs_mass = 125.0 * GeV; // Stores 125'000.0 (energy in MeV, not mass!)
504-
double higgs_width = 4.0 * MeV; // Stores 4.0
504+
=== "Type-safe API"
505505
506-
setParticleProperties(higgs_mass, higgs_width, 0.0);
507-
// Passes 125'000.0 as mass_MeV_per_c2 - but it's energy in MeV!
508-
// Should have divided by c_squared first
509-
// ✓ Compiles ☠️ Wrong physics ❌ No warning
510-
}
506+
```cpp
507+
// Types enforce correct dimensions and units at compile time
508+
void setParticleProperties(quantity<hep::rest_mass[MeV / c2]> mass,
509+
quantity<hep::decay_width[MeV]> width,
510+
quantity<hep::electric_charge[eplus]> charge);
511+
```
511512
512-
// DISASTER 2: Arguments swapped
513-
{
514-
double higgs_mass = 125.0 * GeV / c_squared; // Stores correct mass value
515-
double higgs_width = 4.0 * MeV; // Stores 4.0
513+
### Wrong Dimensions
516514
517-
setParticleProperties(higgs_width, // should be higgs_mass!
518-
higgs_mass, // should be higgs_width!
519-
0.0);
520-
// Results: mass=4.0 MeV/c² (should be 125'000 MeV/c²), width has mass dimension!
521-
// ✓ Compiles ☠️ Catastrophic ❌ No warning
522-
}
515+
Passing a value with incorrect physical dimensions is a common error. For example,
516+
passing _energy_ (E) where _mass_ (E/c²) is expected. In legacy code, the compiler
517+
cannot detect this — both are just `double`. The wrong numerical value flows through
518+
calculations, producing physically incorrect results.
523519
524-
// DISASTER 3: Wrong unit scale (ROOT GeV vs CLHEP MeV framework mismatch)
525-
{
526-
double higgs_mass = 125.0 * GeV / c_squared; // Stores correct mass value
527-
double higgs_width = get_from_ROOT(); // Stores 0.004 (ROOT GeV base: 4 MeV = 0.004 GeV)
528-
529-
// get_from_ROOT() returns 0.004 - correct in ROOT (GeV=1), but CLHEP API expects MeV!
530-
setParticleProperties(higgs_mass, // correct mass value
531-
higgs_width, // 0.004 → API reads as 0.004 MeV (should be 4 MeV!)
532-
0.0);
533-
// Forgot ROOT→CLHEP conversion: value needs ×1000 to be in MeV!
534-
// Results: width=0.004 MeV (should be 4 MeV) - off by 1000×!
535-
// ✓ Compiles ☠️ 1000× wrong ❌ No warning
536-
}
520+
=== "Legacy (unsafe)"
537521
538-
// DISASTER 4: Wrong quantity of the same kind (invariant_mass vs rest_mass)
539-
{
540-
double m_inv = compute_invariant_mass(); // reconstructed from 4-momenta, in MeV/c²
541-
double higgs_width = 4.0 * MeV;
542-
543-
setParticleProperties(m_inv, // invariant mass of a candidate event
544-
higgs_width,
545-
0.0);
546-
// Invariant mass of a single event ≠ rest mass of the Higgs boson!
547-
// Both are mass dimension (MeV/c²), but physically different concepts
548-
// ✓ Compiles ☠️ Wrong physics ❌ No warning
549-
}
522+
```cpp
523+
double higgs_mass = 125.0 * GeV; // Energy in MeV (125'000.0), not mass!
524+
double higgs_width = 4.0 * MeV;
525+
526+
// Should have divided by c_squared first - but compiler doesn't know!
527+
setParticleProperties(higgs_mass, higgs_width, 0.0);
528+
// Passes 125'000.0 as mass_MeV_per_c2 - catastrophically wrong
529+
// ✓ Compiles ☠️ Wrong physics ❌ No warning
550530
```
551531
552532
=== "mp-units (safe)"
553533
554534
```cpp
555-
// Type-safe API - compiler enforces correct types
556-
void setParticleProperties(quantity<hep::rest_mass[MeV / c2]> mass,
557-
quantity<hep::decay_width[MeV]> width,
558-
quantity<hep::electric_charge[eplus]> charge);
535+
quantity higgs_mass = 125.0 * GeV; // Compiler knows this is energy
536+
quantity higgs_width = 4.0 * MeV;
559537
560-
// ERROR 1: Wrong dimensions caught at compile time
561-
{
562-
quantity higgs_mass = 125.0 * GeV; // energy quantity, not mass
563-
quantity higgs_width = 4.0 * MeV;
538+
// setParticleProperties(higgs_mass, higgs_width, 0.0 * eplus);
539+
// Compile error! Cannot convert energy to mass
540+
// "note: no known conversion from energy to rest_mass"
564541
565-
// setParticleProperties(higgs_mass, higgs_width, 0.0 * eplus);
566-
// Compile error! Cannot convert quantity<GeV> to rest_mass
567-
// "note: no known conversion from energy to rest_mass"
542+
// Must explicitly convert - the dimension error is caught:
543+
setParticleProperties(higgs_mass / c2, higgs_width, 0.0 * eplus); // ✓
544+
```
568545
569-
// Must explicitly convert energy to mass:
570-
setParticleProperties(higgs_mass / c2, higgs_width, 0.0 * eplus); // ✓
571-
}
546+
### Arguments Swapped
572547
573-
// ERROR 2: Swapped arguments caught at compile time
574-
{
575-
quantity higgs_mass = 125.0 * GeV / c2;
576-
quantity higgs_width = 4.0 * MeV;
548+
Function parameters with the same underlying type (`double`) but different semantic
549+
meanings can be accidentally swapped. The compiler has no way to detect this in
550+
legacy code because all parameters have identical types.
577551
578-
// setParticleProperties(higgs_width, higgs_mass, 0.0 * eplus);
579-
// Compile error! Cannot convert decay_width to rest_mass
580-
// "note: no known conversion from decay_width to rest_mass"
552+
=== "Legacy (unsafe)"
581553
582-
setParticleProperties(higgs_mass, higgs_width, 0.0 * eplus); // ✓
583-
}
554+
```cpp
555+
double higgs_mass = 125.0 * GeV / c_squared;
556+
double higgs_width = 4.0 * MeV;
557+
558+
// Accidentally reversed the arguments!
559+
setParticleProperties(higgs_width, // should be higgs_mass
560+
higgs_mass, // should be higgs_width
561+
0.0);
562+
// Results: mass=4.0 MeV/c² (off by 31'000×), width=125'000 MeV
563+
// ✓ Compiles ☠️ Catastrophic ❌ No warning
564+
```
584565
585-
// ERROR 3: Unit scale confusion caught - raw doubles rejected
586-
{
587-
quantity higgs_mass = 125.0 * GeV / c2;
588-
double higgs_width = get_from_ROOT(); // 0.004
566+
=== "mp-units (safe)"
589567
590-
// setParticleProperties(higgs_mass, higgs_width, 0.0 * eplus);
591-
// Compile error! Cannot convert double to quantity<decay_width[MeV]>
592-
// Forces explicit unit attachment - you must decide: GeV or MeV?
568+
```cpp
569+
quantity higgs_mass = 125.0 * GeV / c2;
570+
quantity higgs_width = 4.0 * MeV;
593571
594-
// Correct: attach the right unit and conversion is automatic
595-
setParticleProperties(higgs_mass, higgs_width * GeV, 0.0 * eplus);
596-
// ✓ Compiles! 0.004 * GeV = 4 MeV, converted automatically
597-
}
572+
// setParticleProperties(higgs_width, higgs_mass, 0.0 * eplus);
573+
// Compile error! Types don't match
574+
// "note: no known conversion from decay_width to rest_mass"
598575
599-
// ERROR 4: Wrong quantity of the same kind caught at compile time
600-
{
601-
quantity<hep::invariant_mass[GeV / c2]> compute_invariant_mass();
576+
setParticleProperties(higgs_mass, higgs_width, 0.0 * eplus); // ✓
577+
```
602578
603-
quantity m_inv = compute_invariant_mass(); // from 4-momenta
604-
quantity higgs_width = hep::decay_width(4.0 * MeV);
579+
### Wrong Unit Scale (Framework Mismatch)
605580
606-
// setParticleProperties(m_inv, higgs_width, 0.0 * eplus);
607-
// Compile error! Cannot convert invariant_mass to rest_mass
608-
// "note: no known conversion from invariant_mass to rest_mass"
609-
// Both are mass, but they are distinct quantity kinds
610-
}
581+
Different HEP frameworks use different base units (ROOT uses GeV=1, CLHEP uses MeV=1).
582+
When interfacing between frameworks, forgetting to convert units produces values
583+
off by factors of 1000 or more. In legacy code, these are just `double` values —
584+
the compiler cannot detect the mismatch.
585+
586+
=== "Legacy (unsafe)"
587+
588+
```cpp
589+
double higgs_mass = 125.0 * GeV / c_squared;
590+
double higgs_width = get_from_ROOT(); // Returns 0.004 (ROOT uses GeV=1)
591+
592+
// Forgot to convert ROOT's GeV to CLHEP's MeV!
593+
setParticleProperties(higgs_mass, higgs_width, 0.0);
594+
// API expects MeV, receives 0.004 → width = 0.004 MeV (off by 1000×!)
595+
// ✓ Compiles ☠️ 1000× wrong ❌ No warning
611596
```
612597
613-
The mp-units version makes all four classes of error **impossible** — they're caught at
614-
compile time before the code ever runs. The physics constraints are enforced by the type
615-
system.
598+
=== "mp-units (safe)"
599+
600+
```cpp
601+
quantity higgs_mass = 125.0 * GeV / c2;
602+
double higgs_width = get_from_ROOT(); // Returns bare 0.004
603+
604+
// setParticleProperties(higgs_mass, higgs_width, 0.0 * eplus);
605+
// Compile error! Cannot convert double to quantity
606+
// Forces explicit unit attachment
607+
608+
// Must specify the unit - conversion happens automatically:
609+
setParticleProperties(higgs_mass, higgs_width * GeV, 0.0 * eplus);
610+
// ✓ Compiles! 0.004 * GeV = 4 MeV (automatic conversion)
611+
```
612+
613+
### Wrong Quantity of the Same Kind
614+
615+
Some physically distinct concepts share the same dimension but have different meanings.
616+
For example, _invariant mass_ reconstructed from decay products vs. _rest mass_ of a
617+
particle species. Mixing these is physically meaningless even though they're
618+
dimensionally identical. Legacy `double` code cannot distinguish them.
619+
620+
=== "Legacy (unsafe)"
621+
622+
```cpp
623+
// From 4-momenta reconstruction
624+
double compute_invariant_mass();
625+
626+
double m_inv = compute_invariant_mass();
627+
double higgs_width = 4.0 * MeV;
628+
629+
// Invariant mass of ONE EVENT ≠ rest mass of THE HIGGS BOSON
630+
setParticleProperties(m_inv, higgs_width, 0.0);
631+
// Both are mass dimension (MeV/c²) - compiler cannot tell them apart
632+
// ✓ Compiles ☠️ Wrong physics ❌ No warning
633+
```
634+
635+
=== "mp-units (safe)"
636+
637+
```cpp
638+
// From 4-momenta reconstruction
639+
quantity<hep::invariant_mass[GeV / c2]> compute_invariant_mass();
640+
641+
quantity m_inv = compute_invariant_mass();
642+
quantity higgs_width = hep::decay_width(4.0 * MeV);
643+
644+
// setParticleProperties(m_inv, higgs_width, 0.0 * eplus);
645+
// Compile error! Wrong quantity kind
646+
// "note: no known conversion from invariant_mass to rest_mass"
647+
// Physics constraints enforced by type system
648+
```
649+
650+
### Summary
651+
652+
The mp-units type system makes all four classes of error **impossible** — they're
653+
caught at compile time before the code ever runs. The physics constraints are
654+
enforced by the compiler, not just by documentation and code review.
616655
617656
The type hierarchy also captures physical relationships. For _energy_, adding child
618657
quantities yields their common parent, enforcing E = KE + mc²:

0 commit comments

Comments
 (0)