Skip to content

Commit

Permalink
Introduce Sendable SharedRepository (#14)
Browse files Browse the repository at this point in the history
# Introduce Sendable SharedRepository

## ♻️ Current situation & Problem
Until now, `Sendable` conformance for values stored in the
`ValueRepository` (the builtin `SharedRepository` implementation) never
checked if values do indeed conform to Sendable. Implementors were
always required

## ⚙️ Release Notes 
* Introduced new `SendableSharedRepository` and
`SendableValueRepository` variant.

### Breaking Changes
* Computed KnowledgeSources now need to specify the type of the
SharedRepository via an associated type.
* The `HeapRepository` was removed. 

## 📚 Documentation
Documentation was updated.


## ✅ Testing
Additional unit testing was added for the new sendable shared repository
variants.


## 📝 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 Aug 13, 2024
1 parent 4781d96 commit d3a6757
Show file tree
Hide file tree
Showing 15 changed files with 705 additions and 476 deletions.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func swiftLintPlugin() -> [Target.PluginUsage] {

func swiftLintPackage() -> [PackageDescription.Package.Dependency] {
if ProcessInfo.processInfo.environment["SPEZI_DEVELOPMENT_SWIFTLINT"] != nil {
[.package(url: "https://github.com/realm/SwiftLint.git", .upToNextMinor(from: "0.55.1"))]
[.package(url: "https://github.com/realm/SwiftLint.git", from: "0.55.1")]
} else {
[]
}
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,40 @@
//


/// Represents type erased ``RepositoryValue``.
/// Represents type erased `RepositoryValue`.
///
/// Refer to ``RepositoryValue``.
public protocol AnyRepositoryValue {
/// This property gives access to a type-erased version of ``RepositoryValue/Source``
/// This property gives access to a type-erased version of `Source`.
var anySource: any KnowledgeSource.Type { get }
/// This property gives access to a type-erased version of ``RepositoryValue/value``.
var anyValue: Any { get }
}


/// A type that represents a stored value inside a ``SharedRepository``.
/// A stored value in a shared repository.
///
/// You may also think of it as a stored instance of a ``KnowledgeSource``.
public protocol RepositoryValue<Source>: AnyRepositoryValue {
/// The ``KnowledgeSource`` for which the value is stored.
associatedtype Source: KnowledgeSource
/// This container type contains the stored value an type information of the associated ``KnowledgeSource``.
public struct RepositoryValue<Source: KnowledgeSource> {
/// The value of the knowledge source.
public let value: Source.Value

/// Initialize a new repository value.
/// - Parameter value: The value instance.
init(_ value: Source.Value) {
self.value = value
}
}


/// The value instance of the ``KnowledgeSource``.
var value: Source.Value { get }
extension RepositoryValue: AnyRepositoryValue {}


init(_ value: Source.Value)
}
extension RepositoryValue: Sendable where Source.Value: Sendable {}


extension RepositoryValue {
/// The type erased ``RepositoryValue/Source``.
/// The type erased `Source`.
public var anySource: any KnowledgeSource.Type {
Source.self
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//


/// A Sendable Shared Repository.
public struct SendableValueRepository<Anchor> {
private var storage: [ObjectIdentifier: AnyRepositoryValue & Sendable] = [:]


/// Initializes an empty shared repository.
public init() {}
}


extension SendableValueRepository: SendableSharedRepository {
public func get<Source: KnowledgeSource<Anchor>>(_ source: Source.Type) -> Source.Value? where Source.Value: Sendable {
(storage[ObjectIdentifier(source)] as? RepositoryValue<Source>)?.value
}

public mutating func set<Source: KnowledgeSource<Anchor>>(_ source: Source.Type, value newValue: Source.Value?) where Source.Value: Sendable {
storage[ObjectIdentifier(source)] = newValue.map { RepositoryValue<Source>($0) }
}

public func collect<Value>(allOf type: Value.Type) -> [Value] {
storage.values.compactMap { value in
value.anyValue as? Value
}
}
}


extension SendableValueRepository: Collection {
public typealias Index = Dictionary<ObjectIdentifier, AnyRepositoryValue & Sendable>.Index

public var startIndex: Index {
storage.values.startIndex
}

public var endIndex: Index {
storage.values.endIndex
}

public func index(after index: Index) -> Index {
storage.values.index(after: index)
}


public subscript(position: Index) -> AnyRepositoryValue & Sendable {
storage.values[position]
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,33 @@
//


/// A ``ValueRepository`` that allows to store any ``KnowledgeSource``s.
public typealias UniversalValueRepository = ValueRepository<Any>


/// A ``SharedRepository`` implementation that itself is a value type.
///
/// - Note: The ``ValueRepository`` is considered `Sendable` if the ``RepositoryAnchor`` conforms to `Sendable`.
public struct ValueRepository<Anchor>: SharedRepository, BuiltinRepository {
var storage: Container = [:]
/// A Shared Repository.
public struct ValueRepository<Anchor> {
private var storage: [ObjectIdentifier: AnyRepositoryValue] = [:]


/// Initializes an empty shared repository.
public init() {}
}


extension ValueRepository: SharedRepository {
public func get<Source: KnowledgeSource<Anchor>>(_ source: Source.Type) -> Source.Value? {
get0(source)
(storage[ObjectIdentifier(source)] as? RepositoryValue<Source>)?.value
}

public mutating func set<Source: KnowledgeSource<Anchor>>(_ source: Source.Type, value newValue: Source.Value?) {
set0(source, value: newValue)
self.storage[ObjectIdentifier(source)] = newValue.map { RepositoryValue<Source>($0) }
}

public func collect<Value>(allOf type: Value.Type) -> [Value] {
collect0(allOf: type)
storage.values.compactMap { value in
value.anyValue as? Value
}
}
}


extension ValueRepository: Collection {
public typealias Index = Dictionary<ObjectIdentifier, AnyRepositoryValue>.Index

Expand All @@ -54,6 +54,3 @@ extension ValueRepository: Collection {
storage.values[position]
}
}


extension ValueRepository: @unchecked Sendable where Anchor: Sendable {}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@


/// A ``KnowledgeSource`` that allows to compose it's values from it's surrounding knowledge environment.
public protocol ComputedKnowledgeSource<Anchor>: SomeComputedKnowledgeSource {
public protocol ComputedKnowledgeSource<Anchor, Repository>: SomeComputedKnowledgeSource {
associatedtype Repository

/// Computes the value of the ``KnowledgeSource``.
///
/// - Note: The implementation of this method must be deterministic.
/// - Parameter repository: The repository to use for computation.
/// - Returns: Returns the computed value.
static func compute<Repository: SharedRepository<Anchor>>(from repository: Repository) -> Value
@Sendable
static func compute(from repository: Repository) -> Value
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,4 @@ public protocol KnowledgeSource<Anchor> {
associatedtype Value = Self
/// The ``RepositoryAnchor`` to which this `KnowledgeSource` is anchored to.
associatedtype Anchor: RepositoryAnchor


/// Optional reduction algorithm to handle overriding existing entries.
///
/// The default implementation overrides the existing value.
/// - Parameters:
/// - value: The existing value to reduce into.
/// - nextValue: The next value.
static func reduce(value: inout Self.Value, nextValue: Self.Value)
}

extension KnowledgeSource {
/// The default implementation override the current value.
/// - Parameters:
/// - value: The existing value to reduce into.
/// - nextValue: The next value.
public static func reduce(value: inout Self.Value, nextValue: Self.Value) {
value = nextValue
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@

/// A ``KnowledgeSource`` that allows to compose it's values from it's surrounding knowledge environment
/// but may deliver a optional value.
public protocol OptionalComputedKnowledgeSource<Anchor>: SomeComputedKnowledgeSource {
public protocol OptionalComputedKnowledgeSource<Anchor, Repository>: SomeComputedKnowledgeSource {
associatedtype Repository

/// Computes the value of the ``KnowledgeSource``.
///
/// - Note: The implementation of this method must be deterministic.
/// - Parameter repository: The repository to use for computation.
/// - Returns: Returns the computed value or nil if nothing could be computed.
static func compute<Repository: SharedRepository<Anchor>>(from repository: Repository) -> Value?
@Sendable
static func compute(from repository: Repository) -> Value?
}
Loading

0 comments on commit d3a6757

Please sign in to comment.