Re: Parser/Backend API proposals
On Fri, 17 Jan 2003, John Peacock wrote: > > my $formatter = > > DateTime::Formats::ISO8601->new > > ( class => 'DateTime::Implementation::TAI64' ); > > > > my $dt = $formatter->parse( ... ); > > That's OK, but too verbose for the average user, IMHO. It also leaps too deeply > into the API for my taste. I would suggest that the average user shouldn't need > to do anything more than > > my $dt = DateTime->new($datestring); The default constructor is going take components (year, month, day ...), not a parseable string. > and have that do something useful for the majority of the trivial cases. I hate > to keep bringing up Math::BigInt, but the model is very close to what I think is > ideal. The base package works out of the box, but if you want better > performance, you add one line and your existing code works just the same, only > faster: > > use Math::BigInt; # implicitly uses Math::BigInt::Calc > use Math::BigInt lib => 'BitVect'; # faster backend > > and there is no need to change your existing code; I think it unlikely that > anyone would want to use more than one storage/backend module at the same time. So you're suggesting that DateTime.pm would be a factory class that might instantiate some other implementation, based on a previously set class variable? That's fine. > I also think I didn't explain my proposal sufficiently. I would argue that the > parser modules should be extremely thin and not full fledged constructors. All > that the parser would do is parse into a common format and leave the object > creation to the storage module. That's what DateTime::Formats::ICal does. But the common format is a named set of components. > which is exactly my $obj->as_array method would return. The reason that I Right, I just like named parameters. > suggested multiple possible parses is that some backends (like TAI) already have > a conversion from a specific external representation to their internal > representation. If the array of arguments is agreed to be the most likely I think for TAI this will have to deal with in the DateTime::Thingy::TAI64 module, not in a parser. There's no reason to burden parser implementors with having to create different sets of returns values just to handle various implementations. > "normalized" representation, I would actually have the Formats modules include > only a single function Parse() which would return the normalized relationship > (or undef if this parser cannot handle that date string). That's more or less how DateTime::Formats::ICal works, except it returns a DateTime.pm object. > > I think we should assume that creating a format/parse object will be > > one step, and actual parsing/formatting will be a different one. > > Why? It requires the average user to know far more about the internals than is > strictly necessary. Sure, it has to be available for those who need it, but it > shouldn't be the default interface. Cause it's the simplest, least bizarre interface. Everybody seems to have different ideas about how to make this more convenient, but absent any consensus I'll start with implementing the simple, least clever interface. > > I don't think the parser should be directly responsible for producing > > anything other than "DateTime.pm API-compliant object", which may or may > > not actually _be_ a DateTime.pm object. > > Well then we may be saying the same thing; I want the parser to generate a > normalized date format. I'm just saying that making the parser itself an object > is not _required_ to do that. It's not required, but making it an object may be needed for other things. For example, Date::Manip parsing is language dependent, so you may need one object for English and one for French. > Not at all. DateTime is the gatekeeper class; it contains any overloading code > (which you haven't really mentioned yet, have you?) as well as all of the Overloading code is in there. > No question. Any implemention class will inherit all of the derivable methods > and only implement/override the minority of primitive methods (like $obj->year > for example). Can you explain what the class hierarchy for DateTime.pm-API objects would be? Right now I'm thinking: DateTime.pm - implements reference API, with completely functional internals DateTime::Implementation::TAI64 - implements same API. Inherits from DateTime.pm and overrides some methods. Clearly you're thinking of something else but I don't understand what. -dave /*=== House Absolute Consulting www.houseabsolute.com ===*/
Re: Parser/Backend API proposals
Dave Rolsky wrote: > I should also point out I'm moving the ical-specific bits to > DateTime::Formats::ICal. As soon as SF gets their servers back in operation (what's up with that?), I'll take a look at what you have. My quicky module is not meant to be an example of anything practical, just something to toy with. In particular, you (in particular) will blanche at the fact I used Autoloader (since that is what h2xs normally includes), which is not friendly towards mod_perl code. > How about this instead: > > my $formatter = > DateTime::Formats::ISO8601->new > ( class => 'DateTime::Implementation::TAI64' ); > > my $dt = $formatter->parse( ... ); That's OK, but too verbose for the average user, IMHO. It also leaps too deeply into the API for my taste. I would suggest that the average user shouldn't need to do anything more than my $dt = DateTime->new($datestring); and have that do something useful for the majority of the trivial cases. I hate to keep bringing up Math::BigInt, but the model is very close to what I think is ideal. The base package works out of the box, but if you want better performance, you add one line and your existing code works just the same, only faster: use Math::BigInt; # implicitly uses Math::BigInt::Calc use Math::BigInt lib => 'BitVect'; # faster backend and there is no need to change your existing code; I think it unlikely that anyone would want to use more than one storage/backend module at the same time. I also think I didn't explain my proposal sufficiently. I would argue that the parser modules should be extremely thin and not full fledged constructors. All that the parser would do is parse into a common format and leave the object creation to the storage module. > > The assumption here is that there is a standard set of arguments which can > be given to the constructor, which follows from the assumption that all > DateTime "core object" implementations share an API. These constructor > arguments would probably be: > > year, month, day > hour, minute, second > time_zone which is exactly my $obj->as_array method would return. The reason that I suggested multiple possible parses is that some backends (like TAI) already have a conversion from a specific external representation to their internal representation. If the array of arguments is agreed to be the most likely "normalized" representation, I would actually have the Formats modules include only a single function Parse() which would return the normalized relationship (or undef if this parser cannot handle that date string). > > I think we should assume that creating a format/parse object will be > one step, and actual parsing/formatting will be a different one. Why? It requires the average user to know far more about the internals than is strictly necessary. Sure, it has to be available for those who need it, but it shouldn't be the default interface. > > I don't think the parser should be directly responsible for producing > anything other than "DateTime.pm API-compliant object", which may or may > not actually _be_ a DateTime.pm object. Well then we may be saying the same thing; I want the parser to generate a normalized date format. I'm just saying that making the parser itself an object is not _required_ to do that. > > So what's implemented in DateTime.pm in this idea? Nothing? If nothing, > it might as well just return an object in a different class. I don't > think I understand what you're suggesting. Not at all. DateTime is the gatekeeper class; it contains any overloading code (which you haven't really mentioned yet, have you?) as well as all of the exposed API. But it doesn't need to contain the actual code to implement the API, and I would argue, shouldn't. See below > > >>I have DateTime::Formats::ISO8601 done (well formed ISO dates only); I attach it >>for your pleasure. I can work on DateTime::Formats::Date_Parse for the more > > > By "Date_Parse" do you mean complex Date::Manip style parsing? No, actually I meant Graham Barr's Date::Parse (part of Time::Date). Someone will likely want the Date::Manip parsing available as well. > > As to DateTime::Thingy::TAI64, I think it needs to do the following: > > - implement the same API as DateTime.pm, possibly adding some TAI64 > methods if that's useful > > - inherit from DateTime.pm - this follows from the former. There's no > reason to implement a method like "year_0", which is defined in > DateTime.pm as: > > sub year_0 { $_[0]->year - 1 } > > There's a number of other methods in DateTime.pm it'd be pointless to > override as well, along these same lines. > No question. Any implemention class will inherit all of the derivable methods and only implement/override the minority of primitive methods (like $obj->year for example). John -- John Peacock Director of Information Research and Technology Rowman & Littlefield Publishing Group 4720 Boston Way Lanham, MD 20706 301-459-3366 x.5
Re: Parser/Backend API proposals
On Thu, 16 Jan 2003, John Peacock wrote: > I've been thinking about the API for parsers and storage methods and I have a > different suggestion than what has been discussed before. The current model > seems to assume that there is a single DateTime object type, regardless of > backend storage method. Or at the very least, the current model will require > additional backend work to completely emulate the existing scheme. Although > Date::ICal seems to be a fine base, others have already mentioned wanting to > implement in other internal schemes, for example TAI64. Yes, I expect there to be other DateTime.pm API implementations using different internals, TAI64 being one obvious example. A pure XS implementation may also be useful, just for speed. I should also point out I'm moving the ical-specific bits to DateTime::Formats::ICal. This'll be a good example parser/formatter, and there's already working code for it, and tests that can be tweaked a bit. It will also handle parsing & formatting DateTime::Duration objects, which other DateTime::Formats::* modules may need to do as well. > I suggest that the parsing modules (currently referred to as DateTime::Formats) > be strictly limited to parsing some subset of the possible input formats and > return an object with a few standard methods which would retrieve a normalized > representation. Then, the parsed and normalized object could be used by the > chosen backend to store the date for later processing. > > For example, DateTime::Formats:: would return an object which would have the > following methods: > > $obj->as_array # "big endian" array of date/time values > $obj->as_unix_time # seconds since the epoch > $obj->as_string# some sort of "standard" text format > $obj->as_ical # the ICal normalized format How about this instead: my $formatter = DateTime::Formats::ISO8601->new ( class => 'DateTime::Implementation::TAI64' ); my $dt = $formatter->parse( ... ); The assumption here is that there is a standard set of arguments which can be given to the constructor, which follows from the assumption that all DateTime "core object" implementations share an API. These constructor arguments would probably be: year, month, day hour, minute, second time_zone or something extremely similar. This assumes that DateTime implementations would implement the same API as DateTime.pm (maybe with extra methods), but that seems like a _good_ implementation. I think it'd be confusing as hell if DateTime.pm offered one set of get methods and DateTime::Implementation::TAI64 offered another set. > I've actually been programming some sample code, so for this object: > > my $prs = DateTime::Formats::ISO8601("1998-06-03T12:30:01"); > > you would have these possible methods: > > $prs->as_array => ARRAY(0x8064c68) > 0 1998 > 1 06 > 2 03 > 3 12 > 4 30 > 5 01 > $prs->as_unix_time => 899483401 > $prs->as_string => '1998-06-03T12:30:01' > $prs->as_ical => '19980603T123001' I think we should assume that creating a format/parse object will be one step, and actual parsing/formatting will be a different one. I don't think the parser should be directly responsible for producing anything other than "DateTime.pm API-compliant object", which may or may not actually _be_ a DateTime.pm object. > I would suggest that the DateTime objects themselves simply be a wrapper for the > DateTime::Storage:: module, which itself would rely on the DateTime::Format > modules to provide it with a normalized date format. The DateTime->new() method > would be very shallow: > > ... > my $self = { > DATE => DateTime::Storage->new(DateTime::Format->new($val)), > }; > > bless $self, $class; So what's implemented in DateTime.pm in this idea? Nothing? If nothing, it might as well just return an object in a different class. I don't think I understand what you're suggesting. > I have DateTime::Formats::ISO8601 done (well formed ISO dates only); I attach it > for your pleasure. I can work on DateTime::Formats::Date_Parse for the more By "Date_Parse" do you mean complex Date::Manip style parsing? As to DateTime::Thingy::TAI64, I think it needs to do the following: - implement the same API as DateTime.pm, possibly adding some TAI64 methods if that's useful - inherit from DateTime.pm - this follows from the former. There's no reason to implement a method like "year_0", which is defined in DateTime.pm as: sub year_0 { $_[0]->year - 1 } There's a number of other methods in DateTime.pm it'd be pointless to override as well, along these same lines. -dave /*=== House Absolute Consulting www.houseabsolute.com ===*/