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

Contracts for resolvers are inflexible #11

Open
jackfirth opened this issue Feb 2, 2016 · 3 comments
Open

Contracts for resolvers are inflexible #11

jackfirth opened this issue Feb 2, 2016 · 3 comments
Labels
maybe for successor Possibly defer to gregor's successor library

Comments

@jackfirth
Copy link

Offset/gap/overlay resolvers are functions with a particular kind of signature. If the API changes in a way that new parameters need to be added, removed, or moved around, all client code using custom offset resolvers breaks. The details of a resolver's implementation are only useful to Gregor users constructing their own resolvers - most users don't need anything beyond the already provided ones. Instead Gregor could provide constructors that accept the resolver functions and produce opaque resolver values instead. This has a number of advantages:

  • The documentation can be simpler, particularly the documentation of the Gregor-provided resolvers, since their function contracts don't need to be mentioned.
  • If the contract for resolvers changes, client code breaks, but if client code has to pass their resolver function to a constructor, the constructor can wrap old resolvers by rearranging parameters, supplying default behavior, etc
  • Resolvers provided by Gregor can avoid being wrapped in contracts.
  • Resolvers can't be used outside Gregor functions. This is a mixed bag - clients lose some flexibility, but gain better error messages and checking since it becomes much harder to use a non-resolver function where you meant to use a resolver and vice-versa.

This is something that bit me in the lens package a while back. It would be tricky to add this to Gregor in a backwards compatible way, but it could be worth it. Let me know what you think!

@97jaz
Copy link
Owner

97jaz commented Feb 3, 2016

Let me make sure I know what you mean. Do you mean that we just provide the existing resolvers plus a constructor of (gap-resolver? overlap-resolver? -> offset-provider?)? So users can use the existing ones and create new offset resolvers from the existing gap/overlay resolvers, but they can't create new ways of resolving gaps and overlaps?

One one hand, I like this simplification, but it would effectively mean that we don't really expect anything other than moment to implement gen:moment-provider -- since it's really the provider's responsibility to use the offset resolvers. Of course, if you don't know what a resolver's signature is, then there's no way to use it. Possibly other moment providers could be implemented in terms of moment, but this change would disallow any that aren't. That said, I can't really think of why anyone would want to implement gen:moment-provider.

@97jaz
Copy link
Owner

97jaz commented Feb 3, 2016

By the way, I'm currently working on moving the Gregor code into a new tree while cleaning it up a bit. The biggest change is that I'm simplifying the internal representations of the basic data types. For example, instead of the date struct containing a YMD struct and a Julian date number, it will simply contain a year, a month, and a day. That is, it will essentially be what the YMD struct is now.

More to the point, though, I'm also making two changes to resolvers:

  • The final argument to a resolver is no longer (or/c moment? #f) but instead (or/c (integer-in -64800 64800) #f) -- which is to say, an optional UTC offset in seconds. Obviously this will be invisible to clients if we make resolvers opaque -- which goes to your point about being able to change the API.
  • I've introduced two parameters current-offset-resolver and current-arithmetic-offset-resolver. The former defaults to resolve-offset/raise and the latter resolve-offset/retain. The former is used as the default value of any #:resolve-offset parameter except in date arithmetic functions, where the latter is used instead.

@jackfirth
Copy link
Author

Re: clarification - I believe that's what I mean. Let me be more explicit: I would create a module defining gap-resovler, overlap-resolver, and offset-resolver structs, along with defining the existing offset resolvers, and constructors of the form (gap-resolve-proc/c -> gap-resolver?), (overlap-resolve-proc/c -> overlap-resolver?), and (gap-resolver? overlap-resolver? -> offset-resolver?). These constructors can handle adapting in the future in the case of changes to the resolver signatures. In addition, the module would create each of the existing resolvers (causing them to be created without being wrapped in contracts).

This does mean only Gregor is capable of using the resolvers, meaning nobody can implement gen:moment-provider. I can't think of a decent reason for implementing that either - as I understand it, the primary purpose of the generic interfaces is for interop with Racket's date data structures. In this case, Gregor would be providing the implementations of these interfaces for interop. In the case of some other third-party date/time package, maybe some sort of gregor/provider library for exposing the necessary internals? With suitable documentation that the exact interfaces are less stable than the rest of Gregor.

@97jaz 97jaz added the maybe for successor Possibly defer to gregor's successor library label Nov 12, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
maybe for successor Possibly defer to gregor's successor library
Projects
None yet
Development

No branches or pull requests

2 participants