Reputation: 43
Trying create a generic function, with the recursive object, but it got some issues. This is what I did
enum Color {
case red, green, blue
}
enum Size {
case small, medium, large
}
struct Product {
let name: String
let color: Color
let size: Size
}
protocol Specification {
associatedtype T
func isSatisfied(item: T) -> Bool
}
protocol Filtering {
associatedtype T
func filter<S: Specification>(in items: [T], with specification: S) -> [T] where S.T == T
}
struct BetterFilter: Filtering {
typealias T = Product
func filter<S>(in items: [Product], with specification: S) -> [Product] where S : Specification, Product == S.T {
var results = [Product]()
for item in items {
if specification.isSatisfied(item: item) {
results.append(item)
}
}
return results
}
}
struct ColorSpecification: Specification {
typealias T = Product
let colorToFilter: Color
func isSatisfied(item: Product) -> Bool {
return item.color == colorToFilter
}
}
struct SizeSpecification: Specification {
typealias T = Product
let sizeToFilter: Size
func isSatisfied(item: Product) -> Bool {
return item.size == sizeToFilter
}
}
struct MultiSpecifications<T, Spec: Specification>: Specification where Spec.T == T {
let listOfSpecifications: Array<Spec>
func isSatisfied(item: T) -> Bool {
listOfSpecifications.allSatisfy {
$0.isSatisfied(item: item)
}
}
}
///******************* RUN ***********************///
let apple = Product(name: "Apple", color: .red, size: .small)
let banana = Product(name: "Banana", color: .green, size: .large)
let bmwCar = Product(name: "BMW", color: .blue, size: .large)
let laptop = Product(name: "Dell laptop", color: .blue, size: .large)
let allProducts: [Product] = [apple, banana, bmwCar, laptop]
/// ⛔️ THIS IS WHERE THE ERROR OCCURS
let multiSpecs = MultiSpecifications(listOfSpecifications: [
SizeSpecification(sizeToFilter: .large),
ColorSpecification(colorToFilter: .blue)
])
let results = betterFilter.filter(in: allProducts, with: multiSpecs)
Seem like I've been missing something declaring the MultiSpecifications struct. It keeps throwing the error /// >>>> "Type of expression is ambiguous without more context" when I create an instance of MultiSpecifications
Upvotes: 2
Views: 88
Reputation: 272845
Right now, the array in MultiSpecification
can only contain one type of Specification
, that being Spec
, but you are giving it two types of Specification
- SizeSpecification
and ColorSpecification
.
You can make the array contain multiple types of Specification
s if its type were [any Specification<T>]
. And to be able to write a type like that, you'd need to make T
the primary associated type:
protocol Specification<T> {
associatedtype T
func isSatisfied(item: T) -> Bool
}
With this change, the declaration of filter
can also be more concise:
protocol Filtering {
associatedtype T
func filter(in items: [T], with specification: any Specification<T>) -> [T]
}
struct BetterFilter: Filtering {
func filter(in items: [Product], with specification: any Specification<Product>) -> [Product] { ... }
}
Then you can do:
struct MultiSpecifications<T>: Specification {
let listOfSpecifications: [any Specification<T>]
...
}
let apple = Product(name: "Apple", color: .red, size: .small)
let banana = Product(name: "Banana", color: .green, size: .large)
let bmwCar = Product(name: "BMW", color: .blue, size: .large)
let laptop = Product(name: "Dell laptop", color: .blue, size: .large)
let allProducts: [Product] = [apple, banana, bmwCar, laptop]
let multiSpecs = MultiSpecifications(listOfSpecifications: [
SizeSpecification(sizeToFilter: .large),
ColorSpecification(colorToFilter: .blue)
])
let results = BetterFilter().filter(in: allProducts, with: multiSpecs)
Upvotes: 1