October 10, 2010
On Sunday 10 October 2010 06:43:59 Andrei Alexandrescu wrote:
> > One issue that would arise from this is one which I'm beginning to think we need to address in the language somehow, and that is template constraint failures. When a template constraint fails, all the compiler tells you is that no version of the template matches the arguments that you're trying to instantiate with. That's highly uninformative. Ideally, it would tell you which portion of the constraint failed. For instance, by using strings instead of TUnit, you have the issue of getting the string exactly right. If the string had to be "year", and someone used "years" or "Year", then the template constraint would fail, and they would have no idea why. At least with the enum, the compiler will complain about the parameter that you're giving the template (though it complains that the mistyped value is not a property of int (e.g. that years is not a property of int if you used TUnit.years instead of TUnit.year), which isn't a very good message either - it's just that it's specific enough that it's easy to figure out). Using a string will result in worse error messages.
> 
> In fact that's not an issue. Template constraints were meant to avoid a template biting off more than it can chew. In this case, however, you want to cover the total set, so you can simply write:
> 
> long convert(string from, string to)(long) {
>      static if (from == "seconds" && to == "minutes") {
>      } else static if (...) {
>      ...
>      } else {
>          static assert("Invalid duration specifiers. ...");
>      }
> }

Except, what if a template constraint has half a dozen conditions in it? In such cases, it's not at all clear which condition was false. And while you can combine conditions into eponymous templates or functions or whatnot so that there are fewer conditions listed in the template constraint, that just buries them as far as tracking down what failed goes. It can be very irritating to have to mess around with a template constraint until you figure out which condition is failing. And if the templated function or type is in a library as opposed to your own code, it's that much harder because you can't comment out portions of the condition to see what happens. convert() may not be too bad, but it's not hard at all to create template constraints which need to be true but which are complicated enough that it's a pain to figure out what's wrong when they fail.

- Jonathan M Davis
October 10, 2010
On Sunday 10 October 2010 07:33:51 Andrei Alexandrescu wrote:
> On 10/9/10 19:57 CDT, Jonathan M Davis wrote:
> > I ended up making it fairly easy to _use_ in generic code, I believe, but it doesn't do much to generate code, which does indeed result in a lot of generic and repetitive code.
> 
> Libraries that contain type names in function names have trouble with generic code.

True, but while I do have functions such as addYears() and addSeconds(), I also have functions like addTUnit() which call those functions. So, the generic code has generic functions that it can call, but there are non-generic versions which are more pleasant for programmers to deal with in non-generic code (though just using durations solves the problem in this particular case).

> > For Clock, Dur, and IRange, however (which are similar namespaces), I
> > really do think that the added namespacing is well worth it. It makes
> > the code much clearer. Dur in particular would be bad to have its
> > functions separated out, because then you have functions like years(),
> > months(), and days() which are going to conflict all over the place with
> > variable  names.
> 
> I'm just wondering how come dates and times are the only API that's hairy enough to require carving namespaces within the whole Phobos. I'm just saying a solid argument should be brought up. Otherwise anyone could use this as a precedent to justify unnecessarily baroque APIs.

Well, for Clock, it makes it quite explicit that you're getting the time and it nicely organizes the various functions which deal with getting the time. You could choose not to have it, but it seems cleaner to me to have it.

For Dur, it avoids name conflicts since names like years, days, and hours are going to conflict with variable names and the like. And even if they don't conflict in a way that results in compilation errors, it's much easier to screw up and get logic errors if they aren't encapsulated in Dur. Now, if we change TUnit to use strings, then you can do something like Dur!"days"(5) and then you don't need the Dur class as a namespace anymore.

For IRange, it not only encapsulates the various range-generating functions, but it makes it quite clear in user code that that's what you're doing when you have IRange.funcName() - certainly clearer than just funcName().

I am by no means arguing that you should have such namespaces all over the place in Phobos, but I do think that they can be useful in some cases. I think that one of the major reasons that this sort of thing hasn't been done previously is because most modules either contain completely unrelated functions (such as std.algorithm) or _all_ of the functions are related (such as std.path). In such cases, namespacing some of them really doesn't make much sense.

> >> * As another random example:
> >> 
> >> DayOfWeek getDayOfWeek(DayOfGregorianCal day) pure nothrow
> >> 
> >> could/should be (in client use):
> >> 
> >> get!(TUnit.dayOfWeek, TUnit.dayOfGregorianCal)(day);
> > 
> > Since those functions would all have to be have specialized version of such templates, I'm not sure how much you gain with that approach - particularly when there aren't very many of those functions (and IIIRC they're all package or private rather than in the API). But it may be worth doing something like that. I'm more skeptical of this suggestion than the others though.
> 
> What you gain is that the client only needs to remember "get" and the types, instead of the cross product of types. Not to mention advantages in generic code, although I don't have an example handy.
> 
> Fewer functions for a given functionality == better library.

I agree that in the general case, this sort of thing is useful, but in this case, we're only talking about code which is private/package to datetime and not used outside of it, and I believ that getDayOfWeek is the only get function like this which exists. That's why I don't see much point in templatizing it like that. If there were several of them and/or they were public, then that would make good sense. But it's a not a public function, and there's only one.

- Jonathan M Davis
October 10, 2010
On Sunday 10 October 2010 06:43:59 Andrei Alexandrescu wrote:
> Then you have only one duration type that's always
> precise and you can drop the interval type. If someone wants to figure
> out e.g. the number of months between two dates, we can provide specific
> functions.

I meant to reply to this in another response, but apparently I missed  it. Sorry about that.

In any case, if we get rid of the interval type, we're basically saying that we doing care about the ability to check whether intervals intersect or adjoin one another or about getting intersections of them or merging them, etc. That functionality is from Boost and not something that I thought of myself. In fact, I rather doubt that I'll ever use it in my own code, but I'm not sure that we want to get rid of it. It strikes me as the sort of thing that a few programmers will love and be very grateful that it's there but which most programmers won't care about at all. If we ditch it, it wouldn't be all that hard to just create some functions which return ranges given a begin and an end rather than using the time points to create an interval which we get a range from, but I don't know if we want to ditch it. It would simplify the code but at the cost of some potentially useful functionality. You could try and have some of it as free-form functions, but it wouldn't work anywhere near as well as with an actual type. So, I'm not totally against ditching interval types, but I question that doing so is a good idea.

- Jonathan M Davis
October 10, 2010
Le 2010-10-10 ? 11:16, Jonathan M Davis a ?crit :

> Part of me would definitely like to keep MonthDuration, HNSecDuration, and JointDuration as is, but the more I think about it, the more it looks like it wouldn't be all that bad to have to work around their lack, and while using the durations as they are really isn't all that hard, it's going to confuse a fair number of people when they first encounter them. So, I'm beginning to lean towards just simplifying it to HNSecDuration (though I'd rename it as Duration in that case). It's annoying in some cases, but it'll definitely cut down on the learning curve for using the library.

Another option I might propose is to get rid of MonthDuration by using JointDuration instead and rename it to CalendarDuration.

But in my opinion, calendar things like this should simply belong to a separate module. This would simplify the API for those who don't need to deal with the complexities of the calendar.

-- 
Michel Fortin
michel.fortin at michelf.com
http://michelf.com/



October 10, 2010

Andrei Alexandrescu wrote:
>
> Great. I still suggest using "Bounded" instead of "Ranged" to avoid confusion with anything range. In particular, ranges are open to the right but bounded are closed.
>

So do I. I confused Ranged with Range.
October 10, 2010
On Sat, 09 Oct 2010 23:23:37 -0500, Andrei Alexandrescu <andrei at erdani.com> wrote:

> On 10/9/10 20:40 CDT, Denis wrote:
>> I believe in D one should be able to write a RangedInt(int lowerBound,
>> int upperBound) type
>
> More precisely: BoundedInt(long lowerBound, ulong upperBound) so it can express any range of any number.
>
> Andrei

This is an example, ported from the Boost date time library:

http://bitbucket.org/gomez/yao-library/src/tip/src/yao/datetime/core.d#cl-551

-- 
Yao G.
October 10, 2010
On 10/10/10 15:10 CDT, Yao G. wrote:
> On Sat, 09 Oct 2010 23:23:37 -0500, Andrei Alexandrescu <andrei at erdani.com> wrote:
>
>> On 10/9/10 20:40 CDT, Denis wrote:
>>> I believe in D one should be able to write a RangedInt(int lowerBound,
>>> int upperBound) type
>>
>> More precisely: BoundedInt(long lowerBound, ulong upperBound) so it can express any range of any number.
>>
>> Andrei
>
> This is an example, ported from the Boost date time library:
>
> http://bitbucket.org/gomez/yao-library/src/tip/src/yao/datetime/core.d#cl-551

Do you plan to submit this to Phobos? Then it needs a fair amount of work.

- Need support for unsigned numbers

- Need support for floating-point numbers

- The behavior of the limits is odd - why does IntWrapped!(int, 1, 10) cover [0, 9] and not [1, 10]?

- Limits are adjusted in a Procrustean manner, they should be enforced instead (at least for the Bounded type being discussed)

- asNumber should be exposed as alias this.

- add, subtract etc. should be operators

- Many other functions need to be added


Andrei
October 11, 2010
On Sun, 10 Oct 2010 23:36:13 -0500, Andrei Alexandrescu <andrei at erdani.com> wrote:

> Do you plan to submit this to Phobos? Then it needs a fair amount of work.
>
> - Need support for unsigned numbers
>
> - Need support for floating-point numbers
>
> - The behavior of the limits is odd - why does IntWrapped!(int, 1, 10) cover [0, 9] and not [1, 10]?
>
> - Limits are adjusted in a Procrustean manner, they should be enforced instead (at least for the Bounded type being discussed)
>
> - asNumber should be exposed as alias this.
>
> - add, subtract etc. should be operators
>
> - Many other functions need to be added
>
>
> Andrei

OK. I'm sorry for the noise.

-- 
Yao G.
October 11, 2010
On 10/11/10 0:22 CDT, Yao G. wrote:
> On Sun, 10 Oct 2010 23:36:13 -0500, Andrei Alexandrescu <andrei at erdani.com> wrote:
>
>> Do you plan to submit this to Phobos? Then it needs a fair amount of work.
>>
>> - Need support for unsigned numbers
>>
>> - Need support for floating-point numbers
>>
>> - The behavior of the limits is odd - why does IntWrapped!(int, 1, 10)
>> cover [0, 9] and not [1, 10]?
>>
>> - Limits are adjusted in a Procrustean manner, they should be enforced instead (at least for the Bounded type being discussed)
>>
>> - asNumber should be exposed as alias this.
>>
>> - add, subtract etc. should be operators
>>
>> - Many other functions need to be added
>>
>>
>> Andrei
>
> OK. I'm sorry for the noise.

Sorry if that came across harshly. I meant all that as an actual list of action items, not as a naysay of your work.

Andrei
October 11, 2010
On Mon, 11 Oct 2010 00:26:41 -0500, Andrei Alexandrescu <andrei at erdani.com> wrote:

>
> Sorry if that came across harshly. I meant all that as an actual list of action items, not as a naysay of your work.
>
> Andrei

Don't worry. I wasn't submitting my code for review, it was just a small example of a similar concept. Maybe I should have labeled as such.

-- 
Yao G.