97jaz / gregor

Date and time library for Racket
45 stars 10 forks source link

Contracts for resolvers are inflexible #11

Open jackfirth opened 8 years ago

jackfirth commented 8 years ago

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:

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 commented 8 years ago

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 commented 8 years ago

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:

jackfirth commented 8 years ago

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.