Think how to make Future generic on the ErrorType with no impact on Carlos API
vittoriom opened this issue · comments
From @vittoriom on April 9, 2016 13:43
Copied from original issue: spring-media/Carlos#152
From @bkase on April 13, 2016 5:55
Hi!
I have some ideas:
How about supplying the Future rather than the specific output type in a CacheLevel
? Then you still only need two associatedtype
s.
protocol CacheLevel {
associatedtype KeyType
associatedtype AsyncOutputType
func get(key: KeyType) -> Self.AsyncOutputType
func set(value: Self.AsyncOutputType.ValueType, forKey key: KeyType)
/* ... */
}
struct MemoryCacheLevel<..>: CacheLevel {
typealias AsyncOutputType = Future<UIImage, NSCacheError>
/* ... */
}
For composition problems you could introduce a =!>>
operator to transform errors like:
let cache1: BasicCache<String, Future<Int, ErrorA>>
let cache2: BasicCache<String, Future<Int, ErrorB>>
let errorTransformA: ErrorTransform<ErrorA, CompositeError>
let errorTransformB: ErrorTransform<ErrorB, CompositeError>
let cacheLayered: BasicCache<String, Future<Int, CompositeError>> =
((cache1 =!>> errorTransformA) >>> (cache2 =!>> errorTransformB)).normalize()
For simplicity -- for the builtin caches (DiskCacheLevel
, NetworkFetcher
), you could use a IOError
to ease composition.
This has a slight impact on the Carlos API, but I think it's worth it for better type-safety.
Hi @bkase , thanks for your suggestion!
I'm a bit confused though how I could constrain the AsyncOutputType
associatedtype of CacheLevel
to be a Future
. I actually need that to be the case otherwise get
could return whatever type and when composing CacheLevel
s I can't compose the Future
s like I do now.
I think in Swift 2.2 this is not possible yet, maybe in Swift 3?
What I was thinking is maybe I can add a generic error type to Future
s and Result
s and then in Carlos
APIs they will just be "promoted" to the ErrorType
common supertype. This would still be a bit suboptimal but doesn't impact PiedPiper
, actually it improves it.
From @bkase on April 13, 2016 10:11
Oh good point -- didn't think of that; yeah that seems not possible in the current version of Swift.
What I was thinking is maybe I can add a generic error type to Futures and Results and then in Carlos APIs they will just be "promoted" to the ErrorType common supertype. This would still be a bit suboptimal but doesn't impact PiedPiper, actually it improves it.
Nice -- makes sense
From @bkase on April 14, 2016 4:32
Not saying this is necessarily a good idea, but you could sort of constrain the associatedtype to be a Future.
Start with the same definition of a cache as above:
protocol CacheLevel {
associatedtype KeyType
associatedtype AsyncOutputType
func get(key: KeyType) -> Self.AsyncOutputType
func set(value: Self.AsyncOutputType.ValueType, forKey key: KeyType)
/* ... */
}
Implement all the combinators on constrained extensions to the protocol rather than constraining the associated type within the protocol (since we can't)
extension CacheLevel where Self.AsyncOutputType: Async {
public func compose<A: CacheLevel where A.AsyncOutputType: Async, A.KeyType == KeyType, A.AsyncOutputType.Value == Self.AsyncOutputType.Value>(cache: A) -> BasicCache<A.KeyType, A.AsyncOutputType> { /* ... */ }
}
Unfortunately, you'll still be able to conform to CacheLevel
without using an Async
for your AsyncOutputType
, you just won't be able to use certain operators.
Yeah, as you said this is quite hacky because in the definition of CacheLevel
there is no constraint and it's just up to the API (all of them) to make this constraint explicit in their signature.
Also, in your snippet don't forget you can't actually write set(value: Self.AsyncOutputType.ValueType)
since AsyncOutputType
can be whatever. I think until Swift 3.0 comes with full generics this is just not viable.
From @bkase on April 15, 2016 1:34
Oops, good point