Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Ticket #1184 (closed defect: worksforme)

Opened 10 years ago

Last modified 10 years ago

Unintuitive behavior of TimeSpan "properties", static vs instance method

Reported by: elite01 Assigned to: schveiguy
Priority: normal Milestone: 0.99.8
Component: Tango Version: 0.99.6 Jeff
Keywords: properties static Cc: kris


Look at this code snippet:

import, tango.time.Time;

void main() {
    TimeSpan ts = TimeSpan.minutes(1);
    ts.seconds = 3;

Now, the output one would expect is 60 and 3 - but the actual output is 60 and 60. This happens because ts.seconds = 3; - more clearly written as ts.seconds(3); - doesn't call an instance method but static TimeSpan.seconds which creates a new TimeSpan that is then discarded. This also applies to other TimeSpan methods and possibly other classes or structs as well.

This doesn't generate a warning in dmd. Also, it's not possible to have a static and an instance method named seconds accepting the same parameters. So, besides fixing the language or compiler, the only way to close that pitfall is renaming the static methods - I suggest something like fromSeconds.

Change History

07/09/08 15:15:26 changed by schveiguy

My first inclination is to reject this because I don't think its ever possible to prevent unintuitive property behavior, as D can treat any single-arg function as an assignment property. But in this case, it's not so much that the property looks weird, it's that you are calling a static function like you are setting a property on an instance. So it's likely to fool many people into thinking they are doing something other than what they intend without compiler complaint.

So I agree the static function could be at least named differently. But we originally wanted shorter names so people didn't complain about how verbose it was (I personally like verbose as long as it's not over-descriptive). In addition, the ts.seconds = 3 seems misleading. Is appears to be setting a seconds component of the TimeSpan, it is reassigning the TimeSpan to 3 seconds. Although it has nice short syntax, and might be preferable to ts = TimeSpan.seconds(3), it might cause more confusion.

So I've compiled a list of a few possible options. Note that I really don't prefer any of these more than the current behavior:

  1. Do what the original report says.
  2. Change the instance member to asSeconds, leave the static method alone. Add an instance member asSeconds(ulong secs) that does *this = TimeSpan.seconds(secs).
  3. Only change the static method to fromSeconds, don't add a member to assign the timespan from seconds. At least the confusion of calling the static function from the instance will be gone, but the static method is now more verbose.

Kris, what do you think?

07/09/08 15:16:13 changed by schveiguy

  • cc set to schveiguy.

07/09/08 15:53:12 changed by larsivi

I think I like 3. best.

08/03/08 19:05:59 changed by kris

  • owner changed from kris to schveiguy.

Yeah, 3 seems to be appropriate, schveiguy ... reassigning to you :)

08/04/08 17:17:12 changed by schveiguy

  • status changed from new to assigned.

ya ok.

08/04/08 17:17:40 changed by schveiguy

  • cc deleted.

08/05/08 16:00:50 changed by schveiguy

  • status changed from assigned to closed.
  • resolution set to fixed.

Fixed in [3857]

08/14/08 14:32:13 changed by Daniel919

  • status changed from closed to reopened.
  • resolution deleted.

Why not change them into free functions like

minutes instead of TimeSpan?.fromMinutes, ...

import, tango.time.Time;

void main() {
    Stdout((minutes(10) + seconds(10)).seconds).newline;

08/14/08 16:23:45 changed by schveiguy

  • cc set to kris.

Hm... Not sure Tango wants to pollute the global namespace. Thoughts Kris?

08/14/08 19:22:36 changed by larsivi

indeed such pollution is unfortunate - maybe with() can be used over a "time consuming" block?

08/14/08 22:19:11 changed by Daniel919

If I import tango.text.Util,

I will get all the text related functions into the namespace, like trim, strip, ...

If I import tango.time.Time,

I will get all the time related functions into the namespace, like minute, second, and their operator overloads

If I don't want them to "pollute" the global namespace, I would

import Time = tango.time.Time;

and use them as Time.minutes, ...

If I needed just the struct TimeSpan?, I would

import tango.time.Time : TimeSpan?;

What do you think?

08/15/08 15:35:06 changed by schveiguy

  • status changed from reopened to closed.
  • resolution set to wontfix.

Except words like 'seconds' and 'minutes', etc. could be commonly used variable names. That is the problem with polluting the namespace.

It's not that I don't agree the name is verbose, but we have a technical problem having both a static and instance function of the same name, and we have a namespace problem if the static methods are not contained in the struct. So I think I'm going to close this. If you can think of anything better, I'd love to hear it.

08/16/08 03:20:52 changed by kris

I agree with schveiguy

08/16/08 13:39:03 changed by Daniel919

  • status changed from closed to reopened.
  • resolution deleted.

The free functions for text related things (trim, strip, ...) are in tango.text.Util;

The free functions for path related things (normalize, patternMatch, ...) are in tango.util.PathUtil?;

The free functions for time related things (minutes, seconds, ...) could be in a new module tango.time.Util;

So let the user decide whether he wants them to pollute the global namespace:

import tango.time.Util;

or not:

import time = tango.time.Util;

08/16/08 16:21:53 changed by kris

  • status changed from reopened to closed.
  • resolution set to worksforme.

and now you'd have to import more 'stuff' instead. That's not a good solution for this particular concern