Best Practice for Storing Value and ValueAccuracy

I have a struct that stores a variety of measurements, and the accuracy of measurements (i.e. 10 cm ± 2 cm). Not all measurements are necessarily measuring the same thing (angle, length, speed, etc.) Is there a best practice for storing such information? I started with:

struct Example {
  let x: Double
  let xAccuracy: Double

  let y: CGPoint
  let yAccuracy: Double // radius around y

  let z: Int
  let zAccuracy: Int
}

It feels off to have two fields for every property. I'm considering something like this:

struct ApproximateValue<Value, Accuracy> {
  let value: Value
  let accuracy: Accuracy
}

struct Example2 {
  let x: ApproximateValue<Double, Double>
  let y: ApproximateValue<CGPoint, Double>
  let z: ApproximateValue<Int, Int>
}

Any suggestions?

You can use a property wrapper. Property wrappers can have a projected value, which you could set to the accuracy of the wrapped value:

@propertyWrapper struct Measurement<Value> {
  var wrappedValue: Value
  var accuracy: Value
  
  var projectedValue: Value { accuracy }
  
  init(wrappedValue: Value, accuracy: Value) {
    self.wrappedValue = wrappedValue
    self.accuracy = accuracy
  }
}

struct Square {
  @Measurement(accuracy: 0.2) var width: Double = 4
  @Measurement(accuracy: 0.2) var height: Double = 8
}

let square = Square()
square.width  // returns 4.0
square.$width // returns 0.2, its accuracy
1 Like

A simple struct is probably good. You might want to also check out the Measurements framework available in Foundation if you want to keep track of the units being stored.

let a = Measurement(value: 3, unit: UnitLength.meters)
let b = Measurement(value: 3, unit: UnitLength.feet)
let c = a + b 
c.value // will get the value in meters

You can use it with your ApproximateValue abstraction. You might want to make some typealias to improve the common cases for your problem domain.

struct ApproximateValue<Value, Accuracy> {
  var value: Value
  var accuracy: Accuracy
}

typealias Length = ApproximateValue<Measurement<UnitLength>,
                                    Measurement<UnitLength>>
typealias Point = ApproximateValue<CGPoint, Double>
typealias QuantizedThing =  ApproximateValue<Int, Int>

struct Example3 {
    var x: Length
    var y: Point
    var z: QuantizedThing
}

let x3 = Example3(x: .init(value: c, accuracy: b),
                  y: .init(value: .zero, accuracy: 0.2),
                  z: .init(value: 10, accuracy: 1))

The way that I like to approach problems in Swift is to build up a little kingdom of abstractions and then solve the problem at hand simply with that.

As a side note, you probably want to declare your struct properties var since you can declare the struct let and make everything immutable if you need to.

And for shorthand, you can define a “±” operator:

let x = 10 ± 2