|
|
Log in / Subscribe / Register

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

The Engine Yard blog has an introduction to the changes coming in the PHP 7 release. "My personal favorite addition to PHP 7 is the addition of the Combined Comparison Operator, <=>,otherwise known as the spaceship operator. [...] It effectively works like strcmp(), or version_compare(), returning -1 if the left operand is smaller than the right, 0 if they are equal, and 1 if the left is greater than the right. The major difference being that it can be used on any two operands, not just strings, but also integers, floats, arrays, etc."

to post comments

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 3, 2015 15:56 UTC (Fri) by juliank (guest, #45896) [Link] (32 responses)

Having the "<=>" operator return numbers is a bad design decision IMO. I hope there are named constants for -1, 0, and 1 to indicate the meaning, rather than using magic constants.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 3, 2015 16:11 UTC (Fri) by torquay (guest, #92428) [Link] (3 responses)

    ... is a bad design decision IMO

Because everything else in PHP makes sense?

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 3, 2015 16:15 UTC (Fri) by juliank (guest, #45896) [Link] (2 responses)

Well, most of the stuff they have does not make sense, but adding more of that makes even less sense.

I am astonished that they managed to add an AST in PHP 7.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 3, 2015 19:18 UTC (Fri) by mathstuf (subscriber, #69389) [Link] (1 responses)

Did they give sensible names[1] for all of their tokens finally too?

[1]https://stackoverflow.com/questions/2588298/whats-the-mea...

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 4, 2015 22:22 UTC (Sat) by vstinner (subscriber, #42675) [Link]

> Did they give sensible names[1] for all of their tokens finally too?

Sure, PHP 7 gets a new T_SPACESHIP operator...

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 3, 2015 18:53 UTC (Fri) by vonbrand (guest, #4458) [Link] (19 responses)

That particular operator comes directly from Perl, essentially filched from C's strcmp(3)

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 3, 2015 19:06 UTC (Fri) by juliank (guest, #45896) [Link] (18 responses)

Perl and C are not languages too look at when looking at a good language design. Saner languages like Haskell and Python define ordering (and equality) in terms of less-than and equals relations, which is closer to the mathematical definition.

cmp() operations are a good idea in some cases (like string comparisons), but they are a bad idea from a language design point of view, because the result does not have an inherent meaning, but needs to be interpreted (it's some sort of magic value).

This could be fixed by adding enumerations to the language and returning LT, EQ, GT values. But even then, an operator returning a value from an unrelated enumeration would look weird in a modern programming language.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 3, 2015 21:31 UTC (Fri) by nybble41 (subscriber, #55106) [Link] (3 responses)

> Saner languages like Haskell and Python define ordering (and equality) in terms of less-than and equals relations, which is closer to the mathematical definition.

Haskell defines the `Ord` typeclass—`compare`, `(<=)`, `(<)`, `(>)`, and `(>=)`—in terms of the `Ordering` data type: `LT`, `EQ`, or `GT`. Implementers of `Ord` are expected to provide a total order, not just less-than and equals; it's basically the same idea as `(<=>)` in PHP or `strcmp()` in C, though without the magic numbers. (They're not all that magic, though: `a < b` becomes `cmp(a, b) < 0`, `a >= b` is `cmp(a, b) >= 0`, etc. I avoid testing for -1 or 1 directly.)

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 3, 2015 22:13 UTC (Fri) by juliank (guest, #45896) [Link]

Sorry, yes, I was wrong with that. Still applies to Python, though.

The named approach used by Haskell still looks better than just using numbers and is cleaner on the type level.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 4, 2015 7:57 UTC (Sat) by HelloWorld (guest, #56129) [Link] (1 responses)

Actually you can do it both ways in Haskell. You can either implement compare (which returns the Ordering datatype) or (<=) which returns Bool.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 5, 2015 19:32 UTC (Sun) by nybble41 (subscriber, #55106) [Link]

> You can either implement compare ... or (<=) which returns Bool.

You also need to implement (==), since Eq is a prerequisite for Ord. But yes, either `compare` or (<=) is sufficient, along with an Eq instance. There remains an expectation (in `compare` and elsewhere) that two Ord values are exactly one of less-than, equal, or greater-than. It's bad form to leave some comparisons undefined (a partial order) or to implement (<=) and (>=) such that `a <= b && b <= a && not (a == b)`.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 3, 2015 22:22 UTC (Fri) by bmur (guest, #52954) [Link] (12 responses)

I don't understand how returning LT, EQ, GT strings is any better than -1, 0, 1. It's something you learn about the language once and move on.

Have you never worked in the C language before? Because the return values to this new operator make perfect sense to me.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 3, 2015 22:28 UTC (Fri) by juliank (guest, #45896) [Link] (10 responses)

I'm mostly programming in C, I completely get this, thanks.

The values make sense for a lower-level language, but a higher-level language should abstract from raw values and use names instead. Just like they have a boolean type with true/false values instead of just using 1 and 0.

The operator is perfectly usable, it's just not the best design possible.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 4, 2015 1:06 UTC (Sat) by vonbrand (guest, #4458) [Link] (8 responses)

If you want to determine the order of two elements, the <=> gives you that with one operation, not two. Could be relevant if comparison is costly. And calling the result < 0, = 0, > 0 à la strcmp(3) in C or -1, 0, 1 à la Perl, is no big deal. Adding some funky enumeration type you then have to compare against in a separate operation just obfuscates the code.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 4, 2015 10:22 UTC (Sat) by deepfire (guest, #26138) [Link] (7 responses)

So you value questionable[1] efficiency gains over safety -- noted.

We have different values.

--
1. It's questionable, because the compiler ought to optimize such trivialties for you.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 5, 2015 21:20 UTC (Sun) by nix (subscriber, #2304) [Link] (6 responses)

It's not efficiency gains. It's that the <, = and > comparators applied to the return value of <=> produce the same results as the same comparators would applied to <=>'s argument. You can't get that rather useful duality with a bunch of named enumerations, unless you have an ordering over them, and if you do, why not use -1, 0, 1 in any case?

It's not as if "two numbers a unit distance on either side of zero" is a difficult concept, or one that'll need to change when new sorts of number turn up (unless, I suppose, it's extended to deal with complex numbers, and in that case the new return values are obvious: -i, i, etc.)

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 5, 2015 21:57 UTC (Sun) by HelloWorld (guest, #56129) [Link] (5 responses)

> It's not efficiency gains. It's that the <, = and > comparators applied to the return value of <=> produce the same results as the same comparators would applied to <=>'s argument.
I have no idea what you're trying to say here. <, =, > and <=> are all binary operators, you can't apply them to single values.

> You can't get that rather useful duality with a bunch of named enumerations, unless you have an ordering over them,
Haskell's Ordering datatype does.

> and if you do, why not use -1, 0, 1 in any case?
Well, if your compiler does exhaustiveness checking then it's handy. In PHP it doesn't seem too useful, but then nothing in PHP does.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 7, 2015 13:49 UTC (Tue) by nix (subscriber, #2304) [Link] (4 responses)

I shouldn't write comments when flued up.

I meant to say, "the <, = and > comparators applied to the return value of <=> produce the same results as the same comparators would applied to the arguments to the corresponding calls of <=>". i.e. they preserve the same relative ordering.

e.g. iff a < b and b < c, then (a <=> b) < (b <=>c).

You cannot do this with constants (unless you overload the < = > operators to do the appropriate things with those constants, probably by setting the constants to be equivalent to -1, 0, and +1).

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 7, 2015 22:11 UTC (Tue) by mathstuf (subscriber, #69389) [Link]

The `Ordering` enumeration in Haskell is also of type `Ord`:

Prelude> :t LT
LT :: Ordering
Prelude> :t LT < GT
LT < GT :: Bool

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 7, 2015 22:31 UTC (Tue) by nybble41 (subscriber, #55106) [Link] (2 responses)

> e.g. iff a < b and b < c, then (a <=> b) < (b <=>c).

That isn't true. Consider the trivial case where a=1, b=2, and c=2, so a < b and b < c. (a <=> b) and (b <=> c) are both -1, so you have -1 < -1, which is false.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 7, 2015 22:46 UTC (Tue) by juliank (guest, #45896) [Link] (1 responses)

B and C are both 2 in your example, so b=c, not b<c.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 8, 2015 1:21 UTC (Wed) by nybble41 (subscriber, #55106) [Link]

That was supposed to be a=1, b=2, c=3. I fixed the text after previewing, but must have hit "Publish" rather than "Preview" (thus submitting the original version). A simple JS OK/Cancel warning on the "Publish" button when the text is out of sync with the preview would have been nice... with no impact to those without JS.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 4, 2015 7:08 UTC (Sat) by dottedmag (subscriber, #18590) [Link]

Haskell has that because Haskell has first-class sum types. PHP would require a whole new type in a language to deal with <=> return value. Way too overkill.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 4, 2015 8:46 UTC (Sat) by HelloWorld (guest, #56129) [Link]

> I don't understand how returning LT, EQ, GT strings is any better than -1, 0, 1.
They're not supposed to be strings but members of a custom type.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 7, 2015 14:52 UTC (Tue) by niner (guest, #26151) [Link]

"This could be fixed by adding enumerations to the language and returning LT, EQ, GT values."

And indeed, it has been fixed already in Perl 6:

> perl6 -e 'say 1 <=> 2'
Less
> perl6 -e 'say 2 <=> 1'
More
> perl6 -e 'say (1 <=> 2).WHAT'
(Order)

That PHP is copying a feature of Perl 5 (as clearly indicated by the name of the operator) but not look at how Perl 6 does it better is just...

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 4, 2015 10:44 UTC (Sat) by mchapman (subscriber, #66589) [Link] (2 responses)

> Having the "<=>" operator return numbers is a bad design decision IMO.

I don't really agree with that.

I do wish they hadn't specified this operator in terms of the particular integers -1, 0 and 1 though. It would make much more sense for it to be just "a number less than zero", "zero" and "a number greater than zero" respectively. It shouldn't matter what the number is, just that it falls within one of these three domains.

The problem with -1, 0, 1 is that it can lead programmers into thinking that their *own* comparison functions need to have the same behaviour, and so they sully their code with unnecessary logic to make sure only these particular values are returned. But PHP functions like usort(), which take a comparison function, don't require these three particular values.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 4, 2015 14:54 UTC (Sat) by tjc (guest, #137) [Link] (1 responses)

>> Having the "<=>" operator return numbers is a bad design decision IMO.

> I don't really agree with that.

Nor do I. It's commonly held belief that using literals is bad design, but I don't recall having ever seen an error in code caused by the practice. I've seen fabricated examples of possible errors, but nothing in actual production code. I'm sure it does happen, but apparently very infrequently.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 4, 2015 23:22 UTC (Sat) by mathstuf (subscriber, #69389) [Link]

The main problem I have is that the code now has context all over the place. With an enumeration, the context is exactly where you need it: where it is used. I've seen folks mix up 0, 1, and 2 for stdin, stdout, and stderr. The _FILENO constants are much easier to decipher and make it easier to spot errors in isatty calls.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 4, 2015 22:26 UTC (Sat) by vstinner (subscriber, #42675) [Link] (4 responses)

> Having the "<=>" operator return numbers is a bad design decision IMO. I hope there are named constants for -1, 0, and 1 to indicate the meaning, rather than using magic constants.

I was very surprised to see the addition of this operation, since Python decided to *remove* this operator in Python3! cmp() of Python 2 had corner cases with None or float("nan"). For example, list.sort() got a new "key" parameter which is easier to use (you compute the key, you don't compare it directly), and more efficient if I understood correctly.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 5, 2015 8:23 UTC (Sun) by mbunkus (subscriber, #87248) [Link] (3 responses)

If I understand you correctly you mean that first a value »cmpkey« is calculated for each item and then the Python libraries themselves compare those calculated values »cmpkey« in order to determine the order for the elements to sort? Perl's List::MoreUtils package has something simlar (»sort_by«).

However, this approach has its own drawbacks. Often enough you cannot really find a projection from the elements to such cmpkey values that's unambiguous, or even fast. Let's take the example of sorting address records. You want to sort by name first, next by city and by street name last. In such cases you would need »cmpkey« to be some kind of concatenation of all three properties – and the concatenation character itself must not occur in the properties either!

In such cases calculating the keys is rather expensive. Directly comparing the elements like done in traditional sorting routines is way more efficient.

Use the right tool for the job. Both have their uses, so I'm rather surprised to hear that cmp() has been removed from Python 3. I probably don't see the whole picture here, but it seems rather short-sighted.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 5, 2015 12:52 UTC (Sun) by HelloWorld (guest, #56129) [Link]

Your example is trivial to solve since tuples are compared lexicographically by default. sorted(addresses, key = lambda a: (a.name, a.city, a.street)) does the trick and is way shorter than sorted(addresses, cmp = lambda x, y: cmp(x.name, y.name) or cmp(x.city, y.city) or (x.street, y.street)).

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 5, 2015 12:54 UTC (Sun) by ABCD (subscriber, #53650) [Link] (1 responses)

To sort by three different things, you can create a turple as your sort key. Turples with the same length sort first by their first element, then by the second, etc.

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 5, 2015 12:58 UTC (Sun) by mbunkus (subscriber, #87248) [Link]

In languages that support tuples, sure. PHP isn't one, neither is Perl, so in those languages dropping the comparison operator wouldn't make as much sense as it does it Python (or the other way around: introducing <=> makes sense because, unlike Python, PHP doesn't support tuples).

What to Expect When You're Expecting: PHP 7, Part 1 (Engine Yard)

Posted Apr 3, 2015 22:24 UTC (Fri) by leifbk (guest, #35665) [Link]

It's nice that type checking (or "scalar type hints") is finally coming to PHP. I'm surely going to make use of it.

<=> ???

Posted Apr 4, 2015 1:17 UTC (Sat) by gerdesj (subscriber, #5446) [Link] (2 responses)

OK - I am not a programmer, just a sysadmin who has to dabble in PHP/Python/Perl/BASH/Ruby etc at times.

Why is this beast a good thing? I read the article and can see that the new operator is a simplification for the given example. However, I would habitually do something like this in any situation that requires comparison:

if a < b then do stuff1
else if a = b then do stuff2
else if a > b then do stuff3
else do stuff4

It might be a bit long winded but very explicit on what is going on and easy to read by anyone. The final else clause picks up invalid comparisons.

Any reason why I should change this approach?

<=> ???

Posted Apr 4, 2015 7:06 UTC (Sat) by odie (guest, #738) [Link]

The idea is that a and b might be complex objects that reuire a non-trivial amount of processing to compare. In your code, that comparison is run three times, with strcmp in C it need only be run once. In a high-level language, my first thought was that it may make more sense to add caching of the comparison result in the interpretor so that your code example would only run the comparison once anyway.

<=> ???

Posted Apr 4, 2015 7:16 UTC (Sat) by mbunkus (subscriber, #87248) [Link]

Complex sorting by several fields at once. Think of sorting address records. First you compare the name; if those are equal compare the street; if those are equal compare the city etc. In Perl this is trivial to express by using the fact that <=> (or »cmp« which does the same thing on strings instead of numbers) returns 0 for equality.

@sorted = sort { ($a->{name} cmp $b->{name}) || ($a->{street} cmp $b->{street}) || ($a->{city} cmp $b->{city}) } @records;

If those lines become too long just wrap them properly (kind of hard to do here in the comments). Sure you can express that your way, too, but it becomes that much more verbose. Yes, of course this is a matter of style and preference etc. Use what works for you.


Copyright © 2015, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds