Skip to content

Commit

Permalink
Improve overload resolution for KnowledgeSource protocols (#16)
Browse files Browse the repository at this point in the history
# Improve overload resolution for KnowledgeSource protocols

## ♻️ Current situation & Problem
A `SharedRepository` typically has multiple overloads for a range of
different `KnowledgeSource` protocols. A knowledge source might provide
default values or might be computed. Typically, you only adopt a single
knowledge source protocol. However, you cannot prevent users from
adopting multiple. This PR improves the overload resolution in these
scenarios. All of the specialized KnowledgeSource protocols inherit the
base `KnowledgeSource` protocol. Therefore, Swift resolution will
automatically prefer the more specialized type. Problems arise if you
accidentally (or on purpose) conform to a computed KnowledgeSource
variant and the `DefaultProvidingKnowledgeSource`. In this case we
resolve the conflict and generally prefer the computed knowledge source
protocol. We consider a computed knowledge source more specialized than
only providing a default value.

## ⚙️ Release Notes 
* Improve overload resolution for knowledge source protocols in certain
situations


## 📚 Documentation
--


## ✅ Testing
Added some  unit testing to verify overload behavior.

## 📝 Code of Conduct & Contributing Guidelines 

By submitting creating this pull request, you agree to follow our [Code
of
Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md):
- [x] I agree to follow the [Code of
Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md).
  • Loading branch information
Supereg authored Sep 26, 2024
1 parent 17bd0e0 commit 2506285
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,13 @@ public protocol SendableSharedRepository<Anchor>: Sendable {
/// A subscript to retrieve or set a `KnowledgeSource`.
/// - Parameter source: The ``KnowledgeSource`` type.
/// - Returns: The stored ``KnowledgeSource/Value`` or `nil` if not present.
@_disfavoredOverload
subscript<Source: KnowledgeSource<Anchor>>(_ source: Source.Type) -> Source.Value? where Source.Value: Sendable { get set }

/// A subscript to retrieve or set a `DefaultProvidingKnowledgeSource`.
/// - Parameter source: The ``DefaultProvidingKnowledgeSource`` type.
/// - Returns: The stored ``KnowledgeSource/Value`` or the ``DefaultProvidingKnowledgeSource/defaultValue``.
@_disfavoredOverload
subscript<Source: DefaultProvidingKnowledgeSource<Anchor>>(_ source: Source.Type) -> Source.Value where Source.Value: Sendable { get }

/// A subscript to retrieve a `ComputedKnowledgeSource`.
Expand Down Expand Up @@ -119,6 +121,7 @@ extension SendableSharedRepository {


/// Default subscript implementation delegating to ``get(_:)`` or ``set(_:value:)``.
@_disfavoredOverload
public subscript<Source: KnowledgeSource<Anchor>>(_ source: Source.Type) -> Source.Value? where Source.Value: Sendable {
get {
get(source)
Expand All @@ -142,6 +145,7 @@ extension SendableSharedRepository {
}

/// Default subscript implementation delegating to ``get(_:)`` or providing a ``DefaultProvidingKnowledgeSource/defaultValue``.
@_disfavoredOverload
public subscript<Source: DefaultProvidingKnowledgeSource<Anchor>>(_ source: Source.Type) -> Source.Value where Source.Value: Sendable {
self.get(source) ?? source.defaultValue
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,13 @@ public protocol SharedRepository<Anchor> {
/// A subscript to retrieve or set a `KnowledgeSource`.
/// - Parameter source: The ``KnowledgeSource`` type.
/// - Returns: The stored ``KnowledgeSource/Value`` or `nil` if not present.
@_disfavoredOverload
subscript<Source: KnowledgeSource<Anchor>>(_ source: Source.Type) -> Source.Value? { get set }

/// A subscript to retrieve or set a `DefaultProvidingKnowledgeSource`.
/// - Parameter source: The ``DefaultProvidingKnowledgeSource`` type.
/// - Returns: The stored ``KnowledgeSource/Value`` or the ``DefaultProvidingKnowledgeSource/defaultValue``.
@_disfavoredOverload
subscript<Source: DefaultProvidingKnowledgeSource<Anchor>>(_ source: Source.Type) -> Source.Value { get }

/// A subscript to retrieve a `ComputedKnowledgeSource`.
Expand Down Expand Up @@ -120,6 +122,7 @@ extension SharedRepository {


/// Default subscript implementation delegating to ``get(_:)`` or ``set(_:value:)``.
@_disfavoredOverload
public subscript<Source: KnowledgeSource<Anchor>>(_ source: Source.Type) -> Source.Value? {
get {
get(source)
Expand All @@ -140,6 +143,7 @@ extension SharedRepository {
}

/// Default subscript implementation delegating to ``get(_:)`` or providing a ``DefaultProvidingKnowledgeSource/defaultValue``.
@_disfavoredOverload
public subscript<Source: DefaultProvidingKnowledgeSource<Anchor>>(_ source: Source.Type) -> Source.Value {
self.get(source) ?? source.defaultValue
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,4 +188,10 @@ final class SendableSharedRepositoryTests: XCTestCase {
optionalComputedValue = nil
XCTAssertEqual(repository[OptionalComputedTestStruct<_StoreComputePolicy, Repository>.self], 4)
}

@MainActor
func testComputedKnowledgeSourcePreferred() {
let value = repository[ComputedDefaultTestStruct<_StoreComputePolicy, Repository>.self]
XCTAssertEqual(value, computedValue)
}
}
25 changes: 24 additions & 1 deletion Tests/SpeziFoundationTests/SharedRepositoryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ struct DefaultedTestStruct: DefaultProvidingKnowledgeSource, TestTypes {
}


struct ComputedTestStruct<Policy: ComputedKnowledgeSourceStoragePolicy, Repository>: ComputedKnowledgeSource {
struct ComputedTestStruct<Policy: ComputedKnowledgeSourceStoragePolicy, Repository>: KnowledgeSource, ComputedKnowledgeSource {
typealias Anchor = TestAnchor
typealias Value = Int
typealias StoragePolicy = Policy
Expand All @@ -65,6 +65,23 @@ struct ComputedTestStruct<Policy: ComputedKnowledgeSourceStoragePolicy, Reposito
}
}

struct ComputedDefaultTestStruct<Policy: ComputedKnowledgeSourceStoragePolicy, Repository>: ComputedKnowledgeSource, DefaultProvidingKnowledgeSource {
typealias Anchor = TestAnchor
typealias Value = Int
typealias StoragePolicy = Policy

static var defaultValue: Int {
XCTFail("\(Self.self) access result in default value execution!")
return -1
}

static func compute(from repository: Repository) -> Int {
MainActor.assumeIsolated {
computedValue
}
}
}


struct OptionalComputedTestStruct<Policy: ComputedKnowledgeSourceStoragePolicy, Repository>: OptionalComputedKnowledgeSource {
typealias Anchor = TestAnchor
Expand Down Expand Up @@ -261,4 +278,10 @@ final class SharedRepositoryTests: XCTestCase {
optionalComputedValue = nil
XCTAssertEqual(repository[OptionalComputedTestStruct<_StoreComputePolicy, Repository>.self], 4)
}

@MainActor
func testComputedKnowledgeSourcePreferred() {
let value = repository[ComputedDefaultTestStruct<_StoreComputePolicy, Repository>.self]
XCTAssertEqual(value, computedValue)
}
}

0 comments on commit 2506285

Please sign in to comment.