Dynamic member lookup with KeyPath. How to differentiate between overloads?

Hi!

I am writing state memory system which allows user to observe mutation from state.

Let's model simplest state using

struct State {
  var left = Counter()
  var right = Counter()
}
struct Counter {
  var value = 0
}

This state can be changed in a controlled environment. For example:

class Store {
  var state = State()

  func incrementLeft() {
    state.left.value += 1
  }
}

Now I want to allow users observer this state:

class Store {
  var observers: [Observer] = []

  func incrementLeft() {
    state.left.value += 1
    for observer in observers { observer.notify(state) }
  }
}

This works great but all observers will receive all updates.
I want to notify observer only if the portion of the state that this observer did read was mutated.

func incrementLeft() {
  state.left.value += 1
  for observer in observers where observer.keyPaths.contains(\.left.value) {
    observer.notify(state)
  }
}

To collect information about accessed properties I want to use @dynamicMemberLookup capabilities:

class Reader<T> {
  init(value: T) { self.value = value}
  let value: T
  let accessedKeyPaths = [] as Set<PartialKeyPath<T>>

  subscript<V>(dynamicMember keyPath: KeyPath<T, V>) -> V {
    accessedKeyPath.insert(keyPath)
    return value[keyPath: keyPath]
  } 
}

This concept works great on flat structures, but nesting causes a lot of problems.

  1. Accessing any value inside a counter will not be recorded. To mitigate this I can add overload
subscript<V>(dynamicMember keyPath: KeyPath<T, V>) -> Reader<V> {
    ...
 }

But then swift is struggling to disambiguate between them.

Alternatively I can mark all terminal types with special marker protocol, and add value resolution only for them, but this idea doesn't looks scalable for me.

I also tried special operator and function to actually fetch data from readers, but all this is not seamless enough.

Maybe there is a way to nudge Swift to choosing Reader overload over regular one?

1 Like
Terms of Service

Privacy Policy

Cookie Policy