Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restore API for functions #45

Merged
merged 10 commits into from Oct 11, 2015
Merged

Conversation

danthorpe
Copy link
Owner

The only problem with adding functions directly to YapDatabaseReadWriteTransaction (etc) is that every function needs to be generic. This can be a bit of a problem as this is the generic where clause for value types with value type metadata..

extension Readable
    where
    ItemType: Saveable,
    ItemType: MetadataPersistable,
    ItemType.ArchiverType: NSCoding,
    ItemType.ArchiverType.ValueType == ItemType,
    ItemType.MetadataType: Saveable,
    ItemType.MetadataType.ArchiverType: NSCoding,
    ItemType.MetadataType.ArchiverType.ValueType == ItemType.MetadataType {

which is very verbose and error prone - although less so now that the protocol types are relatively stable.

So, there are a couple of options which might work as alternatives.

  1. Similar to the Read and Write structures, we could wrap YapDatabaseReadWriteTransaction, YapDatabaseConnection inside a generic type, which then has generic methods for read & write. e.g.

    if let person: Person = transaction.read.byKey(key) {
       // etc
    }

    which is probably the best, although not sure that this is possible.

  2. Add a generic ItemType to the facade protocols ReadTransactionType etc and then define the functions in protocol extensions? This will work - but don't think that it'll be possible to have the YapDatabase types then conform to the protocols. - correct, not a viable solution.

I think it's going to have to be fully generic functions. But the can be added to the facade protocols which is better for testing at least. Anyway, so the APIs are as follows...

let item: Item? = transaction.readAtIndex(index)
let item: Item? = transaction.readByKey(key)
let items: [Item] = transaction.readAtIndexes(indexes)
let items: [Item] = transaction.readByKeys(keys)

let item: Item? = connection.readAtIndex(index)
let item: Item? = connection.readByKey(key)
let items: [Item] = connection.readAtIndexes(indexes)
let items: [Item] = connection.readByKeys(keys)

transaction.write(item)
transaction.write(items)
connection.write(item)
connection.write(items)
connection.asyncWrite(item) { print("did write") }
connection.asyncWrite(items) { print("did write") }

transaction.remove(item)
transaction.remove(items)
connection.remove(item)
connection.remove(items)
connection.asyncRemove(item) { print("did remove") }
connection.asyncRemove(items) { print("did remove") }

where Item is one of the following type patterns:

  • NSCoding class with no metadata
  • NSCoding class with NSCoding class metadata
  • NSCoding class with Saveable value metadata
  • Saveable value with no metadata
  • Saveable value with NSCoding class metadata
  • Saveable value with Saveable value metadata

Notes:

  1. Not going to provide a functional transaction.readAll() -> Use the Persistable extension for this.

@codecov-io
Copy link

Current coverage is 81.06%

Merging #45 into development will increase coverage by +3.93% as of 637074e

@@            development     #45   diff @@
===========================================
  Files                 5       5       
  Stmts               936    1125   +189
  Branches              0       0       
  Methods               0               
===========================================
+ Hit                 722     912   +190
  Partial               0       0       
+ Missed              214     213     -1

Review entire Coverage Diff as of 637074e

Powered by Codecov. Updated on successful CI builds.

@danthorpe
Copy link
Owner Author

Okay, so there is actually a bit of a problem here. The Swift compiler cannot differentiate between two functions in an extension on the same type which have the same name but different generic where clauses which have inclusive inheritance. So.. for example, given these protocols:

protocol FooType { 
    var baz: Int { get }
}
protocol BarType: FooType { 
    typealias BatType
}

If you define this the following:

extension String {
    func world<T where T: FooType>() -> T? { return .None }
    func world<V where V: BarType, V.BatType == Int>() -> V? { return .None }
}

And then try to use it, assuming that you had some Foo and Bar types defined.

let hello = "hello"
let foo: Foo? = hello.world() // Will not compile, ambiguous use of 'world()'
let bar: Bar? = hello.world() // Will not compile, ambiguous user of 'world()'

This is the actual error message I have at the moment:

swift ambiguity

Which begs the question, how did this used to work?

Well, previously, if you check master branch, you'll see that there wasn't any ambiguity because these functions as they were only defined for Object and Value - because there wasn't any need to know about metadata because metadata was not optional, meaning that it was encoded with the object. And the write functions avoid this, because there are two protocols for Metadata conformance so ObjectWithValueMetadata is fundamentally different to ObjectWithObjectMetadata and I guess the compiler can figure it out.

Not really sure what to do about this.

  1. I'm not reverting the Persistable extensions - I really like this API
  2. I could re-introduce the two metadata protocols - but this seems like going backwards & would be very confusing - plus storing the metadata inside the object is also not correct.
  3. Provide a limited functional API only supporting a subset of type combinations. i.e. to use the functional API you're models have to be say value with value type metadata style. Or which ever combination can co-exist.

I'll investigate 3 more.

@danthorpe
Copy link
Owner Author

Yep, confirmed, I can get it to work for ObjectWithObjectMetadata and ObjectWithValueMetadata so I'm assuming that it'll work with ValueWithObjectMetadata and ValueWithValueMetadata meaning that the functional API won't be available to normal Persistable objects.

Which is not the end of the world - I can define a VoidMetadata type, so that it's easy to make them MetadataPersistable. Or just use the Persistable type based API to read & write.

This means more code re-use and the underlying read/write methods are only used once for each type pattern.
@danthorpe
Copy link
Owner Author

Okay, think that just about does it.

danthorpe added a commit that referenced this pull request Oct 11, 2015
@danthorpe danthorpe merged commit 6bbce3d into development Oct 11, 2015
@danthorpe danthorpe deleted the feature/YDB-45_read_write_functions branch October 11, 2015 02:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants