Calling though the original implementation in an override

In my app I'm overriding array's subscript to get an additional functionality when array contains my elements (which are adhering to my ValueElement protocol):

protocol ValueElement {
    ...
}

extension Array where Element: ValueElement {
    subscript(_ index: Int) -> Element {
        get {
            print("get element at \(index)")
            // do other stuff
            // finally call through original subscript
        }
        set {
            print("set \(newValue) at \(index)")
            // do other stuff
            // finally call through the original subscript
        }
    }
}

to do the "call through the original subscript" part at the moment I've resorted to having a small helper static library as part of my project that exposes original array subscript renamed:

// MyHelperLibrary.swift
extension Array {
    public subscript(elementAt index: Int) -> Element {
        get { self[index] }
        set { self[index] = newValue }
    }
}

so I can do:

import MyHelperLibrary

extension Array where Element: ValueElement {
    subscript(_ index: Int) -> Element {
        get {
            print("get element at \(index)")
            // do other stuff
            // finally call through original subscript:
            return self[elementAt: index]
        }
        set {
            print("set \(newValue) at \(index)")
            // do other stuff
            // finally call through original subscript:
            self[elementAt: index] = newValue
        }
    }
}

Is there a cleaner way to achieve the same without using a helper library?

I attempted this:

extension Array where Element: ValueElement {
    subscript(_ index: Int) -> Element {
        get {
            print("get element at \(index)")
            // do other stuff
            // finally call through original subscript:
            return (self as [Any])[index] as! Element // O(N)
        }
        set {
            print("set \(newValue) at \(index)")
            // do other stuff
            // finally call through original subscript:
            // (self as! [Any])[index] = newValue // 🛑 Cannot assign to immutable expression of type 'Any'
            var newArray = self as [Any] // O(N)
            newArray[index] = newValue
            self = newArray as! [Element] // O(N)
        }
    }
}

But that's much worse as it now has O(N) instead of O(1) time complexity for both getter and setter.

Any suggestions?

This feels like an XY problem. This will only affect code where the element type is statically known, not in generic code, so this probably isn’t the solution you want.

Why do you want to hook into the array’s subscript?

It's good for me though.

I'm experimenting with an alternative observation machinery where I'm recording all get/set accesses to arbitrary types, as an example:

let name = repo.users[42].name   // read access:  \.users\42\name -> "test"
repo.users[42].name = newName    // write access: \.users\42\name

Might not be particularly elegant but:

extension Array where Element: ValueElement {
  subscript(_ index: Int) -> Element {
    get {
      precondition(indices.contains(index))
      print("get element at \(index)")
      return withUnsafeBufferPointer { $0[index] }
    }
    set {
      precondition(indices.contains(index))
      print("set \(newValue) at \(index)")
      withUnsafeMutableBufferPointer { $0[index] = newValue }
    }
  }
}
1 Like

Excellent, that's what the doctor ordered!

Do you know of a similar thing for dictionaries?

I don't think there is any general solution for hooking into members of existing types or accessing the original functionality from within a shadowed member. You probably just have to be lucky to find something that works case by case. Maybe something like this for Dictionary:

extension Dictionary {
  subscript(_ key: Key) -> Value? {
    get {
      print("get value for key \(key)")
      return index(forKey: key).map { self[$0].value }
    }
    set {
      print("set \(String(describing: newValue)) for key \(key)")
      if let newValue { updateValue(newValue, forKey: key) } else { removeValue(forKey: key) }
    }
  }
}
1 Like

Great!

I was thinking about turning this into an advantage:

i.e. do some cast / etc that would make static type information forgotten - and thus the original implementation called instead of my override. Is that possible, and, as importantly, is it possible to do it without incurring O(N) complexity?

Other than that, the case by case workaround is ok for me. And there's the above static library approach which'd work if there's no good type specific solution. e.g. if I also want to hook into client calling through removeValue, updateValue and subscript(_ index: Dictionary.Index) themselves, I'd need to find either another ad-hoc solution or do that via the static library, unless there's a nicer general solution that works in general case.

Huh, that got me thinking, how about this as a general solution?!

extension Array {
    subscript(normal index: Int) -> Element {
        get { self[index] }
        set { self[index] = newValue }
    }
}

extension Array where Element: ValueElement {
    subscript(_ index: Int) -> Element {
        get { self[normal: index] }
        set { self[normal: index] = newValue }
    }
}

Isn't that the same as your previous:

?

Yes, just the static library is not needed, as I thought initially.

Otherwise yeah, that's funny that the question literally contained the answer. :rofl: