I've been pondering this issue for a while now, and I keep reaching the
same conclusion, so I'm going to just briefly share what I think.
In my opinion, the real issue is not poor design of the DateTime class - it
works as you would expect classes and objects to work, in the sense that
when you make changes (adding, subtracting, changing the timezone) you're
making changes to the instance.
I can't help but think that the real underlying issue, is the fact that you
really want value-types and not objects/classes for something like this -
and we only have a few value-types in php, namely the scalar types (int,
string, float, bool) and arrays.
If you've ever attempted to build an object-abstraction for other
value-types, such as colors, URLs (or routes), e-mail addresses, etc. -
you've faced the same issues: when somebody increases the saturation of an
RGB color object, do you change the RGB values, or return a new color
instance? Questions like this stem from the fact that what you really
wanted for something as self-contained as a color or URL, is not an object,
but a value-type.
I don't tend to like solutions that solve one isolated problem, when the
whole class of problems could potentially be address - hearing about
another DateTime type makes my skin crawl, in particular because it
introduces all manner of issues with current APIs that most likely already
depend on the existing DateTime type. Even coming up with a reasonably
descriptive (yet practical) name for this class, is difficult - which to me
indicates a contrived attempt to extend something that only needs to be
extended for technical (as opposed to functional) reasons.
Please consider addressing the real underlying issue: the need for
value-types.
Off the top of my head, I'm thinking this could be addressed by introducing
a new keyword for value-types, interchangeable with "class", for example
"value".
Value-types would work syntactically like classes and objects. But
internally they would work like arrays, meaning they are always passed by
value.
If this could be implemented in such a way that value objects were actually
internally implemented as arrays, we would get all the benefits of arrays -
mainly that they would be passed by value, but would not physically be
copied until modified.
It seems to me, that really is precisely the behavior you want for
something like a date/time, color, URL, e-mail, etc. - in many frameworks
and libraries, this behavior is actually emulated, by using arrays to
represent colors, routes, etc., in order to be able to treat them like
value-types, but with the enormous downside of having to know which
value-type is represented by an array, typically relying on static
helper-methods to modify their properties, and with no chance of any IDE
support.
Just putting that on the table...
- Rasmus Schultz
On Thu, 04 Apr 2013 19:13:54 +0400, Rasmus Schultz rasmus@mindplay.dk
wrote:
I've been pondering this issue for a while now, and I keep reaching the
same conclusion, so I'm going to just briefly share what I think.In my opinion, the real issue is not poor design of the DateTime class -
it
works as you would expect classes and objects to work, in the sense that
when you make changes (adding, subtracting, changing the timezone) you're
making changes to the instance.I can't help but think that the real underlying issue, is the fact that
you
really want value-types and not objects/classes for something like this -
and we only have a few value-types in php, namely the scalar types (int,
string, float, bool) and arrays.If you've ever attempted to build an object-abstraction for other
value-types, such as colors, URLs (or routes), e-mail addresses, etc. -
you've faced the same issues: when somebody increases the saturation of
an
RGB color object, do you change the RGB values, or return a new color
instance? Questions like this stem from the fact that what you really
wanted for something as self-contained as a color or URL, is not an
object,
but a value-type.I don't tend to like solutions that solve one isolated problem, when the
whole class of problems could potentially be address - hearing about
another DateTime type makes my skin crawl, in particular because it
introduces all manner of issues with current APIs that most likely
already
depend on the existing DateTime type. Even coming up with a reasonably
descriptive (yet practical) name for this class, is difficult - which to
me
indicates a contrived attempt to extend something that only needs to be
extended for technical (as opposed to functional) reasons.Please consider addressing the real underlying issue: the need for
value-types.Off the top of my head, I'm thinking this could be addressed by
introducing
a new keyword for value-types, interchangeable with "class", for example
"value".Value-types would work syntactically like classes and objects. But
internally they would work like arrays, meaning they are always passed by
value.If this could be implemented in such a way that value objects were
actually
internally implemented as arrays, we would get all the benefits of
arrays -
mainly that they would be passed by value, but would not physically be
copied until modified.It seems to me, that really is precisely the behavior you want for
something like a date/time, color, URL, e-mail, etc. - in many frameworks
and libraries, this behavior is actually emulated, by using arrays to
represent colors, routes, etc., in order to be able to treat them like
value-types, but with the enormous downside of having to know which
value-type is represented by an array, typically relying on static
helper-methods to modify their properties, and with no chance of any IDE
support.Just putting that on the table...
- Rasmus Schultz
Hi,
this is great idea, I thought about it lately.
Though I think it should be called struct like in other other languages.
And it would be very useful if we could declare structs in-place (it's
useful when dealing with some data-proccessing and all you want is just
strong-typing but without the need for declaring a whole new class in a
new file, etc).
But this is a very big feature and I doubt it will get any support :(
Is it a really big feature if it's just syntactic sugar and internally
stored as an array? say:
struct Color
{
public $r = 1.0;
public $g = 1.0;
public $b = 1.0;
}
Stored internally this might be something like:
array('__type'=>'Color', 'r'=>1.0, 'g'=>1.0, 'b'=>1.0)
Have you worked extensively with DateTime or other value-as-object types?
$today = new DateTime();
$yesterday = $today->modify('-1 day');
echo "today: " . $today->format('Y-m-d') . "\n";
echo "yesterday: " . $yesterday->format('Y-m-d');
Code like this is a major mind-boggle to most programmers - it's not by any
means obvious from reading this code that $yesterday and $today are in fact
references to the same object, and the output of this script is the same
date, twice.
Most programmers automatically think of "atomic" things like date/time and
color as being values, and it's easy (even for experienced developers) to
make mistakes. In my own code, for example, constructors that take DateTime
instances as arguments usually have to safeguard by doing something like
this:
public function __construct(DateTime $begin, DateTime $end)
{
$this->begin = clone $begin;
$this->end = clone $end;
}
This makes redundant copies of objects in every instance, which isn't
optimal - but enables code like this to actually do what you expect:
$date = new DateTime();
$week = array();
for ($i=0; $i<7; $i++) {
$week[] = new Day($date);
$date->add('P1D');
}
if the Day constructor doesn't clone $date, you have problems - that's what
started the debate about a new DateTime type in the first place, I think?
existing DateTime is fine, if what you want is a timestamp you can
manipulate for reasons other than using the resulting value, e.g. for
printing out successive dates - as soon as you want to do something other
than using the immediate value contained in the object, trouble's a-brewin'.
a new DateTime-type would solve that by having the add() and sub() methods
clone internally - but I can already do that by extending DateTime myself
(as countless frameworks and libraries have done) with all the same
problems in terms of API issues and expected behavior. (in cases where you
do expect the internal value to change.)
as said, it only addresses the problem for DateTime, which is just one
isolated example of natural value-types represented as dysfunctional
objects.
why not address the real issue, which is the lack of value-types?
other languages have them for the same reasons.
had we had value-types at the time, no one would have thought to implement
DateTime as an object...
OOP is not a beginner's concept. I don't want to sacrifice good coding
practices for a better learning curve.
Also, a glance on the manual would reveal that the method returns the same
instance for chaining (which is also debatable, why do we even do that?)
Is it a really big feature if it's just syntactic sugar and internally
stored as an array? say:struct Color
{
public $r = 1.0;
public $g = 1.0;
public $b = 1.0;
}Stored internally this might be something like:
array('__type'=>'Color', 'r'=>1.0, 'g'=>1.0, 'b'=>1.0)
Have you worked extensively with DateTime or other value-as-object types?
$today = new DateTime();
$yesterday = $today->modify('-1 day');echo "today: " . $today->format('Y-m-d') . "\n";
echo "yesterday: " . $yesterday->format('Y-m-d');Code like this is a major mind-boggle to most programmers - it's not by any
means obvious from reading this code that $yesterday and $today are in fact
references to the same object, and the output of this script is the same
date, twice.Most programmers automatically think of "atomic" things like date/time and
color as being values, and it's easy (even for experienced developers) to
make mistakes. In my own code, for example, constructors that take DateTime
instances as arguments usually have to safeguard by doing something like
this:public function __construct(DateTime $begin, DateTime $end)
{
$this->begin = clone $begin;
$this->end = clone $end;
}This makes redundant copies of objects in every instance, which isn't
optimal - but enables code like this to actually do what you expect:$date = new DateTime();
$week = array();
for ($i=0; $i<7; $i++) {
$week[] = new Day($date);
$date->add('P1D');
}if the Day constructor doesn't clone $date, you have problems - that's what
started the debate about a new DateTime type in the first place, I think?existing DateTime is fine, if what you want is a timestamp you can
manipulate for reasons other than using the resulting value, e.g. for
printing out successive dates - as soon as you want to do something other
than using the immediate value contained in the object, trouble's
a-brewin'.a new DateTime-type would solve that by having the add() and sub() methods
clone internally - but I can already do that by extending DateTime myself
(as countless frameworks and libraries have done) with all the same
problems in terms of API issues and expected behavior. (in cases where you
do expect the internal value to change.)as said, it only addresses the problem for DateTime, which is just one
isolated example of natural value-types represented as dysfunctional
objects.why not address the real issue, which is the lack of value-types?
other languages have them for the same reasons.
had we had value-types at the time, no one would have thought to implement
DateTime as an object...
2013/4/4 Madara Uchiha dor.tchizik@gmail.com
OOP is not a beginner's concept. I don't want to sacrifice good coding
practices for a better learning curve.
This is interesting. Best practices from other languages, including C#,
Scala etc, have shown that some things are better represented by value
types. Even in PHP, strings are value types, because it is better this
way...
Besides, having to think whether to use a value type or a reference type
does not make anything easier to learn! Value types is an advanced,
post-OOP concept, not targeted to beginners anyway.
Also, a glance on the manual would reveal that the method returns the same
instance for chaining (which is also debatable, why do we even do that?)Is it a really big feature if it's just syntactic sugar and internally
stored as an array? say:struct Color
{
public $r = 1.0;
public $g = 1.0;
public $b = 1.0;
}Stored internally this might be something like:
array('__type'=>'Color', 'r'=>1.0, 'g'=>1.0, 'b'=>1.0)
Have you worked extensively with DateTime or other value-as-object types?
$today = new DateTime();
$yesterday = $today->modify('-1 day');echo "today: " . $today->format('Y-m-d') . "\n";
echo "yesterday: " . $yesterday->format('Y-m-d');Code like this is a major mind-boggle to most programmers - it's not by
any
means obvious from reading this code that $yesterday and $today are in
fact
references to the same object, and the output of this script is the same
date, twice.Most programmers automatically think of "atomic" things like date/time
and
color as being values, and it's easy (even for experienced developers) to
make mistakes. In my own code, for example, constructors that take
DateTime
instances as arguments usually have to safeguard by doing something like
this:public function __construct(DateTime $begin, DateTime $end)
{
$this->begin = clone $begin;
$this->end = clone $end;
}This makes redundant copies of objects in every instance, which isn't
optimal - but enables code like this to actually do what you expect:$date = new DateTime();
$week = array();
for ($i=0; $i<7; $i++) {
$week[] = new Day($date);
$date->add('P1D');
}if the Day constructor doesn't clone $date, you have problems - that's
what
started the debate about a new DateTime type in the first place, I think?existing DateTime is fine, if what you want is a timestamp you can
manipulate for reasons other than using the resulting value, e.g. for
printing out successive dates - as soon as you want to do something other
than using the immediate value contained in the object, trouble's
a-brewin'.a new DateTime-type would solve that by having the add() and sub()
methods
clone internally - but I can already do that by extending DateTime myself
(as countless frameworks and libraries have done) with all the same
problems in terms of API issues and expected behavior. (in cases where
you
do expect the internal value to change.)as said, it only addresses the problem for DateTime, which is just one
isolated example of natural value-types represented as dysfunctional
objects.why not address the real issue, which is the lack of value-types?
other languages have them for the same reasons.
had we had value-types at the time, no one would have thought to
implement
DateTime as an object...
Lazare INEPOLOGLOU
Ingénieur Logiciel