Hello everyone!
Multiple frameworks and libraries define interfaces or objects that wrap
around some scalar value and add behavior, the most common case being
arrays and Collection / Hash[Set|List] objects. Although this objects
benefit from the possibility of having behavior themselves, sometimes the
implementation is outside our project scope (an external dependency) and
interaction between this object and other blocks of code that expect scalar
types usually needs to be controlled by the user, which adds the necessity
for adapters or some sort of mediator.
Now that we have the ability to hint return types, a common interface could
be built that, given an object that implements it, lets userland code use
this object as if it were the scalar type it is wrapping. Unboxing the
object is pretty easy, and I wouldn't go as far as proposing interfaces for
rebuilding the object itself (not now, at least).
So, what do you think of a set of interfaces that allow userland objects to
be used as scalar types?
Some simple examples in PHP here:
https://gist.github.com/guiwoda/5d16c8fb97d29e476d20
Cheers!
On Wed, May 13, 2015 at 11:16 AM, Guido Contreras Woda guiwoda@gmail.com
wrote:
Hello everyone!
Multiple frameworks and libraries define interfaces or objects that wrap
around some scalar value and add behavior, the most common case being
arrays and Collection / Hash[Set|List] objects. Although this objects
benefit from the possibility of having behavior themselves, sometimes the
implementation is outside our project scope (an external dependency) and
interaction between this object and other blocks of code that expect scalar
types usually needs to be controlled by the user, which adds the necessity
for adapters or some sort of mediator.Now that we have the ability to hint return types, a common interface could
be built that, given an object that implements it, lets userland code use
this object as if it were the scalar type it is wrapping. Unboxing the
object is pretty easy, and I wouldn't go as far as proposing interfaces for
rebuilding the object itself (not now, at least).So, what do you think of a set of interfaces that allow userland objects to
be used as scalar types?Some simple examples in PHP here:
https://gist.github.com/guiwoda/5d16c8fb97d29e476d20
I like the idea of supporting more __toType methods than just __toString.
It would certainly make certain things in life a bit easier to manage. For
now, I know I've abused __toString() and done things like (int)(string)$obj
to get the Int value out of it.
Cheers!
Hi,
De : Guido Contreras Woda [mailto:guiwoda@gmail.com]
Multiple frameworks and libraries define interfaces or objects that wrap
around some scalar value and add behavior, the most common case being
arrays and Collection / Hash[Set|List] objects. Although this objects
benefit from the possibility of having behavior themselves, sometimes the
implementation is outside our project scope (an external dependency) and
interaction between this object and other blocks of code that expect scalar
types usually needs to be controlled by the user, which adds the necessity
for adapters or some sort of mediator.Now that we have the ability to hint return types, a common interface could
be built that, given an object that implements it, lets userland code use
this object as if it were the scalar type it is wrapping. Unboxing the
object is pretty easy, and I wouldn't go as far as proposing interfaces for
rebuilding the object itself (not now, at least).So, what do you think of a set of interfaces that allow userland objects to
be used as scalar types?
First, we probably don't need anything for arrays. The Iterator interface family already provides everything we need to consider an object as an array.
For other scalar types, while I agree casting should be supported, I don't like the solution of creating new methods. I would prefer always calling __toString() and then, convert the string to the desired type. So, (int)$object would just be a shortcut for (int)(string)$object. Quite easy to implement, no new method, just need to enrich the convert_to_xxx() functions to go through convert_to_string first. I already had this idea for 7.0 but missed time. Maybe for 7.1.
I also think it's more consistent as an object could not be transformed to different unrelated values when cast to different scalar types. If $object, when cast to int, becomes 10 and, when cast to string, becomes 'foo', is it consistent in such a loose typing system ? So, I prefer that, by construction, (int)$obj always gives the same value as (int)(string)$obj.
Regards
François
@francois I understand your point, but I have a different view of the
userland situation. Let me give some examples:
In the array case, Iterator / Traversable doesn't solve casting, it solves
iteration.
Given a Traversable object from a third party library, if a method from
another (or the same) third party library requires an array as an argument,
the developer would have to iterate the object and build an array herself
in a Mediator or Adapter class. If she could cast it, then all she would
need to do is orchestrate the messaging, without fiddling in the middle.
As for the other cases, there are plenty of examples that could prove that
a userland object may want to have a different representation as a string
than as other scalar types. Money is the first that comes to mind:
$money = new Money(12.5, Money::DOLLARS);
var_dump((string) $money); // string(5) "$12.5"
var_dump((float) $money); // float(12.5)
While there is information loss in the conversion, it is just a userland
example that could well benefit from different representations in the
various scalar types. For example, if an interest were to be calculated
from a Money object, the currency wouldn't matter, and the interest
calculator may as well expect just a float as argument.
Also, there's a huge cognitive gap. For example, if a developer wants an
object to be evaluated as a boolean, she would have to force an empty
string as the __toString() method return. That by itself does not represent
her intention. Without proper documentation, it's just a matter of time for
another developer to find this behavior and remove it or replace it with
something more representative of a string representation of the object.
Cheers!
El mié., 13 de may. de 2015 a la(s) 6:37 p. m., François Laupretre <
francois@php.net> escribió:
Hi,
De : Guido Contreras Woda [mailto:guiwoda@gmail.com]
Multiple frameworks and libraries define interfaces or objects that wrap
around some scalar value and add behavior, the most common case being
arrays and Collection / Hash[Set|List] objects. Although this objects
benefit from the possibility of having behavior themselves, sometimes the
implementation is outside our project scope (an external dependency) and
interaction between this object and other blocks of code that expect
scalar
types usually needs to be controlled by the user, which adds the
necessity
for adapters or some sort of mediator.Now that we have the ability to hint return types, a common interface
could
be built that, given an object that implements it, lets userland code use
this object as if it were the scalar type it is wrapping. Unboxing the
object is pretty easy, and I wouldn't go as far as proposing interfaces
for
rebuilding the object itself (not now, at least).So, what do you think of a set of interfaces that allow userland objects
to
be used as scalar types?First, we probably don't need anything for arrays. The Iterator interface
family already provides everything we need to consider an object as an
array.For other scalar types, while I agree casting should be supported, I don't
like the solution of creating new methods. I would prefer always calling
__toString() and then, convert the string to the desired type. So,
(int)$object would just be a shortcut for (int)(string)$object. Quite easy
to implement, no new method, just need to enrich the convert_to_xxx()
functions to go through convert_to_string first. I already had this idea
for 7.0 but missed time. Maybe for 7.1.I also think it's more consistent as an object could not be transformed to
different unrelated values when cast to different scalar types. If $object,
when cast to int, becomes 10 and, when cast to string, becomes 'foo', is it
consistent in such a loose typing system ? So, I prefer that, by
construction, (int)$obj always gives the same value as (int)(string)$obj.Regards
François
Hi Guido
Am 14.05.15 um 01:03 schrieb Guido Contreras Woda:
@francois I understand your point, but I have a different view of the
userland situation. Let me give some examples:In the array case, Iterator / Traversable doesn't solve casting, it solves
iteration.
Given a Traversable object from a third party library, if a method from
another (or the same) third party library requires an array as an argument,
the developer would have to iterate the object and build an array herself
in a Mediator or Adapter class. If she could cast it, then all she would
need to do is orchestrate the messaging, without fiddling in the middle.As for the other cases, there are plenty of examples that could prove that
a userland object may want to have a different representation as a string
than as other scalar types. Money is the first that comes to mind:$money = new Money(12.5, Money::DOLLARS);
var_dump((string) $money); // string(5) "$12.5"
var_dump((float) $money); // float(12.5)While there is information loss in the conversion, it is just a userland
example that could well benefit from different representations in the
various scalar types. For example, if an interest were to be calculated
from a Money object, the currency wouldn't matter, and the interest
calculator may as well expect just a float as argument.Also, there's a huge cognitive gap. For example, if a developer wants an
object to be evaluated as a boolean, she would have to force an empty
string as the __toString() method return. That by itself does not represent
her intention. Without proper documentation, it's just a matter of time for
another developer to find this behavior and remove it or replace it with
something more representative of a string representation of the object.Cheers!
For Arrays there are already the ArrayAccess-Interface as well as the
ArrayObject-Class which "allows objects to work as arrays"
(http://php.net/arrayobject). That ArrayObjects are not covered by the
typehint "array" is something that I'm sure can be changed. And whether
the typehint "array" covers arrays, ArrayObjects and some special
interface that can be implemented by any arbitrary class is open for
discussion.
But for me the main question is whehter we are just talking about
casting an object to a certain type or letting an object behave like
a certain type.
Let me rephrase your example:
function getprice(int $price) {}
$money = new Money(12.5, Money::DOLLARS);
Will get_price($money) work as the object is cast to int and therefore
we have an int in the method getprice? Or will getprice actually use the
Money-object but all operations are done against the int-representation?
That would also allow something like this
function getprice(int $money) {
$money = $money * CURRENT_VAT;
return $money;
}
which would alter the object in question!
Or are we talking about two completely different things?
Cheers
Andreas
--
,,,
(o o)
+---------------------------------------------------------ooO-(_)-Ooo-+
| Andreas Heigl |
| mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" |
| http://andreas.heigl.org http://hei.gl/wiFKy7 |
+---------------------------------------------------------------------+
| http://hei.gl/root-ca |
+---------------------------------------------------------------------+
Guido Contreras Woda wrote:
So, what do you think of a set of interfaces that allow userland objects to
be used as scalar types?Some simple examples in PHP here:
https://gist.github.com/guiwoda/5d16c8fb97d29e476d20
One of the biggest problems is that it's not always clear which
conversion should be used; consider, for instance,
$obj1 + $obj2
where __toInt() as well as __toFloat() might make sense.
A possible solution would be to force the conversion with type casts,
but I don't see a real advantage in using
(int) $obj1
over
$obj1->toInt()
where the latter is a normal user defined method.
Another problem is that such automatic casting from objects to scalars
might obscure the code.
--
Christoph M. Becker
I do agree that, if I'm the owner and responsible for the object in
question, adding an explicit method would be better than casting it. The
problem is that many times we are dealing with third party libraries, and
modifying them is more complicated. But it is true, and it is true that the
number types would collide if both could be used. That could issue an
E_NOTICE
if not force casted, and default to one of the types (as unquoted
strings do).
El mié., 13 de may. de 2015 a la(s) 8:41 p. m., Christoph Becker <
cmbecker69@gmx.de> escribió:
Guido Contreras Woda wrote:
So, what do you think of a set of interfaces that allow userland objects
to
be used as scalar types?Some simple examples in PHP here:
https://gist.github.com/guiwoda/5d16c8fb97d29e476d20One of the biggest problems is that it's not always clear which
conversion should be used; consider, for instance,$obj1 + $obj2
where __toInt() as well as __toFloat() might make sense.
A possible solution would be to force the conversion with type casts,
but I don't see a real advantage in using(int) $obj1
over
$obj1->toInt()
where the latter is a normal user defined method.
Another problem is that such automatic casting from objects to scalars
might obscure the code.--
Christoph M. Becker
Hi!
One of the biggest problems is that it's not always clear which
conversion should be used; consider, for instance,$obj1 + $obj2
where __toInt() as well as __toFloat() might make sense.
If both are object, I would rather not use either conversion, I'd use
operator support instead, if operator is not supported by the objects
(or the objects do not have same operator handler) then all bets are
off. I guess we could keep them converted to int as they are now, but
it'd be something of "we must return something so we choose arbitrarily
and hope it's what you meant" than something with a robust semantics.
Primarily because for arbitrary objects there's really no semantics that
is natural here.
A possible solution would be to force the conversion with type casts,
but I don't see a real advantage in using(int) $obj1
over
$obj1->toInt()
One advantage is that the former also accepts $obj1 being actual int.
Whether it's useful or not depends on the surrounding code of course.
Another problem is that such automatic casting from objects to scalars
might obscure the code.
True. I think this is one of the main reasons why it wasn't implemented.
Stas Malyshev
smalyshev@gmail.com
Hi!
So, what do you think of a set of interfaces that allow userland objects to
be used as scalar types?Some simple examples in PHP here:
https://gist.github.com/guiwoda/5d16c8fb97d29e476d20
We already have CastToString, it's __toString. I'm not sure we need a
named interface for that, as checking for the method would be basically
the same, and practically I do not see anybody wanting to type against
such type (i.e. you'd do foo(int $a) but not foo(CastToInt $a)). As for
others, we in fact have cast_object handler for the objects, so
extensions can already do that. Giving user functions access to it would
be not hard, though I'm not sure it won't complicate some things. But I
think if enough people need it, enabling full user-space access to
cast_object should not be a problem. Definitely needs a good RFC though
with good convincing examples of use cases.
--
Stas Malyshev
smalyshev@gmail.com