automerge / automerge-repo

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

`DocHandle.value` and `DocHandle.doc` are confusing

alexjg opened this issue · comments

commented

Currently the DocHandle.doc attribute throws an exception when trying to access it before the document is ready, where ready means (as far as I can tell) one of:

  • The handle was created wih Repo.create()
  • The handle was created with Repo.find() and we found something in storage
  • The handle was created with Repo.find(), nothing was found in storage but a peer sent us some changes for the document

The intention here is that the user either call DocHandle.isReady before accessing DocHandle.doc. There is also an async method DocHandle.value which waits for the document to become ready and returns the value. This seems confusing to me because it's not clear from the naming or the types what the difference is between doc and value() and the relationship to isReady is oblique.

I think a nicer API would be for DocHandle.doc to return null until the document is ready. For typescript users this will signal the difference between value() and doc in a way which hopefully will prompt reading the documentation for doc, which should include a mention of isReady and value().

Should be undefined but yeah.

I believe we agreed to rename doc to syncValue (instead of renaming value() to asyncValue()). Or was it the other way? Here's a PR for the former. Certainly they should both be the same.

PS @acurrieclark this one will probably make you change your code, sorry.

Just been looking over the PR. I think that changing the return type of doc to undefined is a good one.

I would suggest that renaming is unnecessary here. If anything, i think this warrants an additional method:

handle.doc // => the actual underlying document. If it exists, it's defined

handle.value() => the resolved handle, which returns when ready

handle.syncValue() => returns the document if ready, errors if not

Having value() as a method and syncValue as a getter strikes me as counterintuitive.

The issue here is that value() also returns the same thing as .doc but has a different name. That's confusing. The .doc value also always* exists and is non-null: we have to create it early in order to be able to start synchronizing.

My aim is to make the two names consistent. I would accept an argument that .value() is wrong.

That said, I think you're right that a getter is also confusing/wrong. I'll update the patch to just make it a normal function. I have a real antipathy towards things that look like real values but aren't, it just didn't occur to me to change it at the time.

I guess here's the question: value, or doc?

Looking at other code and callbacks signatures, we use the term doc everywhere else in the system (including internally.) This convinces me that the correct path is to call both doc. The result will be:
async doc(): Promise<Automerge.Doc<T>> and syncDoc(): Automerge.Doc<T> | undefined

Not to go round in circles, but now is probably the time to nail this down.

In my own codebase I am only ever using the async version to ensure that the handle is ready to be read from. I don't think I even use the return value.

My own preference would be that we just do away with a method which resolves to the doc value and replaced the whole shebang with something like:

await handle.isReady(); // <-- I know there is a sync `isReady()` right now, but this is more descriptive

doSomethingWithAReadyDoc(handle.doc)

I think that doc is the most appropriate accessor, however it is used.

How about I add awaitReady() to make it a bit more explicit as a pattern? isReady() being sync, and awaitReady() for async?

Is await awaitReady() a little odd? Purely aesthetic I know.

yeah it is... Suggestions?

await untilReady()?

I feel that we might be dancing around the fact that isReady() is likely the most sensible choice?

If we used that, could we expose a state() method to get the current state?

Well, given the advice for doc() is to check isReady() first...

That would still be the case!

Thinking about this, I feel like isReady() doesn't imply blocking.