Skip to content
This repository was archived by the owner on Aug 2, 2024. It is now read-only.
This repository was archived by the owner on Aug 2, 2024. It is now read-only.

Think how to make Future generic on the ErrorType with no impact on Carlos API #1

Open

Description

@vittoriom

From @vittoriom on April 9, 2016 13:43

Copied from original issue: spring-media/Carlos#152

Activity

  1. vittoriom commented on May 16, 2016

    @vittoriom
    ContributorAuthor

    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 associatedtypes.

    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.

  2. vittoriom commented on May 16, 2016

    @vittoriom
    ContributorAuthor

    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 CacheLevels I can't compose the Futures 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 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.

  3. vittoriom commented on May 16, 2016

    @vittoriom
    ContributorAuthor

    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

  4. vittoriom commented on May 16, 2016

    @vittoriom
    ContributorAuthor

    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.

  5. vittoriom commented on May 16, 2016

    @vittoriom
    ContributorAuthor

    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.

  6. vittoriom commented on May 16, 2016

    @vittoriom
    ContributorAuthor

    From @bkase on April 15, 2016 1:34

    Oops, good point

  7. added this to the 0.9 milestone on May 18, 2016
  8. modified the milestones: Future, 0.9 on Jun 1, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions