Danger Close: Why Types Matter
A startup’s gene analysis samples nearly melted because someone confused Fahrenheit and Celsius in their monitoring system. A Mars orbiter was lost because of mixed metric and imperial units. Medication dosing errors have killed patients due to milligrams versus micrograms confusion. These are not edge cases. They are symptoms of a recurring problem in how we build mission-critical systems:
Most languages approach types as an afterthought rather than a first line of defense.
The Near-Disaster That Should Never Have Been Possible
Consider a gene analysis startup that nearly lost years of irreplaceable samples due to a simple type confusion. A developer accidentally configured their freezer monitoring system to read temperatures in Fahrenheit while leaving alarm thresholds in Celsius. When 32°F (0°C) triggered alerts, they were dismissed as a configuration error.
The real crisis came over the weekend when an electrical contractor tripped every circuit breaker in the building and left without telling anyone. The monitoring system, disabled due to the earlier “false” alarms, failed to notify anyone of the power loss. By pure luck, the samples survived. This near-catastrophe exposed a flaw in the design: their solution allowed temperature values to exist without their units, creating a disaster waiting to happen.
This wasn’t a failure of testing, monitoring, or procedures. It was a failure of types. The system allowed temperature values to exist without their units of measure, creating a ticking time bomb that eventually exploded.
Clef Units of Measure: Your First Line of Defense
Our Clef language carries units of measure with zero runtime cost. We have found no other representative implementations of native zero-cost units of measure in the standing literature we have reviewed. Here is how that freezer monitoring system would be written in Clef:
[<Measure>] type celsius
[<Measure>] type fahrenheit
[<Measure>] type kelvin
// Temperature conversion functions that preserve units
let celsiusToFahrenheit (temp: float<celsius>) : float<fahrenheit> =
temp * 9.0<fahrenheit/celsius> / 5.0 + 32.0<fahrenheit>
let fahrenheitToCelsius (temp: float<fahrenheit>) : float<celsius> =
(temp - 32.0<fahrenheit>) * 5.0<celsius/fahrenheit> / 9.0
// This would NEVER compile - catching the error at build time
type FreezerMonitor = {
CurrentTemp: float<celsius>
AlarmThreshold: float<celsius>
}
// Attempting to mix units causes compile-time error
let checkTemperature (monitor: FreezerMonitor) (reading: float<fahrenheit>) =
// COMPILE ERROR: Expected float<celsius> but got float<fahrenheit>
if reading > monitor.AlarmThreshold then
Alert.Critical "Temperature exceeded threshold"These unit checks disappear entirely at runtime. The compiled code is identical to using plain floats, and the type system ensures you can never mix units incorrectly.
Beyond Numbers: UMX for Domain Safety
Our Fidelity Framework extends this concept beyond numeric types using the UMX (Units of Measure eXtensions) approach. This brings the same compile-time safety to any type in your domain:
// Define domain-specific units for a medical system
type [<Measure>] patient
type [<Measure>] practitioner
type [<Measure>] prescription
// Type-safe identifiers prevent catastrophic mix-ups
type PatientId = Guid<patient>
type PractitionerId = Guid<practitioner>
type PrescriptionId = Guid<prescription>
// This function signature makes invalid states unrepresentable
let prescribeMedication
(doctor: PractitionerId)
(patient: PatientId)
(medication: Medication) : PrescriptionId =
// Cannot accidentally swap patient and doctor IDs
Prescription.create doctor patient medication
// Attempting to mix identifiers causes compile error
let wrongOrder = prescribeMedication patientId doctorId medication
// COMPILE ERROR: Expected Guid<practitioner> but got Guid<patient>Real-World Applications in Systems Programming
Our Fidelity Framework uses units of measure throughout its stack for systems-level safety:
Memory Management with Units
[<Measure>] type byte
[<Measure>] type kb = byte * 1024
[<Measure>] type mb = kb * 1024
[<Measure>] type gb = mb * 1024
// Network protocols with bandwidth units
[<Measure>] type second
[<Measure>] type mbps = mb / second
type NetworkBuffer = {
Capacity: int<mb>
CurrentUsage: int<byte>
Bandwidth: float<mbps>
}
// Compile-time prevention of buffer overflows
let allocateBuffer (size: int<mb>) (data: byte[]) =
let dataSize = data.Length * 1<byte>
if dataSize > size * 1<mb/byte> then
// Unit conversion is explicit and checked
Error "Data exceeds buffer capacity"
else
Ok (Buffer.create size)Hardware Interface Safety
// GPIO pin safety for embedded systems
type [<Measure>] input
type [<Measure>] output
type [<Measure>] pwm
type Pin<[<Measure>] 'mode> = int<'mode>
// Functions that only accept correctly configured pins
let readDigital (pin: Pin<input>) : bool =
GPIO.read (int pin)
let writeDigital (pin: Pin<output>) (value: bool) : unit =
GPIO.write (int pin) value
let setPWMDutyCycle (pin: Pin<pwm>) (duty: float<percent>) : unit =
GPIO.setPWM (int pin) (float duty)
// Compile error if you try to write to an input pin
let inputPin = 7<input>
writeDigital inputPin true // COMPILE ERROR!Protocol-Level Type Safety
// Network protocol units for Modbus (like the freezer example)
type [<Measure>] holding_register
type [<Measure>] input_register
type [<Measure>] coil
type [<Measure>] discrete_input
// Modbus addresses with their register types
type ModbusAddress<[<Measure>] 'register> = uint16<'register>
// The freezer monitoring system, done right
type FreezerSensor = {
TempRegister: ModbusAddress<input_register>
DoorStatus: ModbusAddress<discrete_input>
SetPoint: ModbusAddress<holding_register>
}
// Read operations that ensure correct register access
let readTemperature (sensor: FreezerSensor) : float<celsius> =
let rawValue = Modbus.readInputRegister sensor.TempRegister
float rawValue * 0.1<celsius> // Sensor reports in 0.1°C increments
// Cannot accidentally read a holding register as input
let wrong = Modbus.readInputRegister sensor.SetPoint
// ERROR: Expected uint16<input_register> but got uint16<holding_register>Memory Safety Without the Performance Tax
One of the harder type safety problems in .NET is the “byref restriction,” the inability to store or return references to memory, which forces defensive copying that can degrade performance in data-intensive applications. Our Fidelity Framework addresses this limitation by combining UMX with our BAREWire capability-based memory management:
// Traditional .NET - forced to copy due to byref restrictions
let updateLargeData (data: float[]) =
// Can't return a reference, must copy entire array
let copy = Array.copy data
for i = 0 to copy.Length - 1 do
copy.[i] <- copy.[i] * 2.0
copy // Expensive copy operation
// Fidelity approach - type-safe capabilities with zero copying
[<Measure>] type readonly
[<Measure>] type readwrite
[<Measure>] type exclusive
type MemoryCapability<'T, [<Measure>] 'access> =
| Capability of BAREWire.Buffer<'T> * AccessToken<'access>
// Functions can only accept appropriately typed capabilities
let readData (cap: MemoryCapability<float, readonly>) =
match cap with
| Capability(buffer, _) -> buffer.Read(0)
let modifyData (cap: MemoryCapability<float, readwrite>) =
match cap with
| Capability(buffer, _) ->
for i = 0 to buffer.Length - 1 do
buffer.[i] <- buffer.[i] * 2.0
// Compile error prevents accidental mutation
let badFunction (cap: MemoryCapability<float, readonly>) =
modifyData cap // ERROR: Expected readwrite but got readonlyThis design prevents both type confusion and performance degradation. Where .NET’s byref restrictions force copying at every boundary, our BAREWire capabilities are designed to pass safely through async boundaries, to be stored in data structures, and to be shared across processes, all while maintaining type safety through UMX:
// Fidelity design target; impossible under standard .NET byref rules
let asyncProcessWithoutCopying (cap: MemoryCapability<float[], readwrite>) = async {
// Capability can be captured in async - no byref restriction!
do! Async.Sleep(100)
// Direct memory access after await - no defensive copying
modifyData cap
// Can even return capabilities from async
return cap
}
// Share memory across process boundaries with type safety
let shareAcrossProcesses (localCap: MemoryCapability<SensorData, exclusive>) =
// Convert exclusive access to shared read-only for other processes
let sharedCap = BAREWire.downgrade<exclusive, readonly> localCap
// Type system ensures other processes can only read
IPC.share "sensor-data" sharedCapThe integration of UMX with memory capabilities addresses multiple critical requirements:
- Type safety: Prevents accidental writes to read-only memory at compile time
- Memory safety: Makes use-after-free errors impossible through capability tracking
- Performance: Eliminates defensive copying throughout the system
- Composability: Capabilities work with async, can be stored, and passed between processes
The Zero-Cost Abstraction Promise
Our Clef units of measure provide their safety at zero runtime cost. The compiler erases all unit information after types have been marshaled to the lowest level of compilation:
// Clef source with units - type safe at compile time
let calculatePower (voltage: float<volt>) (current: float<ampere>) : float<watt> =
voltage * currentThe compilation pipeline shows how units of measure disappear at each stage:
// MLIR from Composer - units erased, but semantics preserved
func.func @calculatePower(%voltage: f64, %current: f64) -> f64 {
%result = arith.mulf %voltage, %current : f64
func.return %result : f64
}; LLVM IR after mlir-opt - further optimized
define double @calculatePower(double %0, double %1) #0 {
%2 = fmul fast double %0, %1
ret double %2
}; x86-64 assembly - pure machine operations
calculatePower:
mulsd xmm0, xmm1 ; multiply two doubles in SSE registers
ret ; return result in xmm0
At each stage, more abstraction disappears. Units of measure exist only in the Clef source. By MLIR, they’re gone but the operation remains typed. By assembly, even the operation names vanish. The type safety that prevents unit confusion exists only at compile time, protecting us during development and vanishing completely in the deployed code.
Learning from Near-Disasters
The freezer monitoring incident teaches us several critical lessons:
- Types are your specification: The type system should encode your domain knowledge
- Make illegal states unrepresentable: If mixing units is wrong, make it impossible
- Zero-cost abstractions exist: Safety doesn’t require runtime overhead
- Catch errors at compile time: This constructive constraint during design saves time and money, and eliminates entire categories of operational risk
Integration with the Fidelity Framework
Our Fidelity Framework builds on Clef’s units of measure to extend that safety across the stack:
// BAREWire protocol with type-safe channels
type [<Measure>] sensor_data
type [<Measure>] control_command
type [<Measure>] telemetry
type Channel<[<Measure>] 'msgType> = {
Endpoint: BAREWire.Endpoint
Protocol: Protocol<'msgType>
}
// Channels can only connect compatible types
let connectChannels
(sender: Channel<'a>)
(receiver: Channel<'a>) : Connection<'a> =
BAREWire.connect sender receiver
// Type error if you try to connect incompatible channels
let sensorChan = createChannel<sensor_data> "tcp://sensor:5555"
let controlChan = createChannel<control_command> "tcp://control:5556"
let invalid = connectChannels sensorChan controlChan
// COMPILE ERROR: Cannot connect Channel<sensor_data> to Channel<control_command>Beyond Safety: Types as Documentation
Well-designed units of measure serve as living documentation:
// The types tell the whole story
type TurbineMonitoring = {
RotationSpeed: float<rpm>
Temperature: float<celsius>
Pressure: float<pascal>
Efficiency: float<percent>
PowerOutput: float<megawatt>
Runtime: TimeSpan<hour>
}
// Function signatures become self-documenting
let calculateMaintenance
(runtime: TimeSpan<hour>)
(avgTemp: float<celsius>)
(peakPressure: float<pascal>) : TimeSpan<hour> =
// unit correctness checked at compile time
let tempFactor = (avgTemp - 85.0<celsius>) / 10.0<celsius>
let pressureFactor = peakPressure / 100_000.0<pascal>
let interval = 2000.0<hour> / (1.0 + abs tempFactor + pressureFactor)
interval - runtimeThe Path Forward
As we build increasingly critical systems, from medical devices to autonomous vehicles to financial infrastructure, we can’t afford to treat types as optional. The gene analysis startup nearly lost irreplaceable samples because their monitoring system allowed temperature units to be confused. The cost of catching that class of error at compile time is one annotation; the cost of missing it can be a year of irreplaceable work.
Our Fidelity Framework shows that type safety doesn’t require sacrificing performance. By using Clef’s units of measure and extending them through UMX, we can build systems that are at once:
- Safer: Entire classes of errors become impossible
- Faster: Zero runtime overhead from type checking
- Clearer: Types in source code document intent and constraints
- Maintainable: The compiler flags unit mismatches introduced during refactoring
Where We Are Taking This
That company’s freezer monitoring system nearly failed because the monitoring software allowed a dangerous state to exist, with no compiler in the way. The DNA samples survived by luck. With Clef’s units of measure, that error would have been caught at compile time, long before any samples were ever at risk.
In our Fidelity Framework, we are applying this lesson at every level, from low-level memory management to high-level protocol design. When you are building critical systems, “it works for my happy path use case” is not enough. Future systems need the compile-time guarantees a strong type system can provide, and we have found no other representative implementation in the standing literature we have reviewed that provides those guarantees at no additional runtime cost.
The next time you reach for a raw float for temperature, or a plain string for an identifier, the unit and the domain are the part of the design worth encoding in the type. That is the line of defense we are continuing to build out across the framework, from the dimensional type system upward, as the work continues.