Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Order as a return of <=> instead of Int32 #2913

Closed
wants to merge 2 commits into from

Conversation

makenowjust
Copy link
Contributor

Currently, many language uses integer value as a return of compare function/operator. However, this is a vice coming from C because it makes some magic numbers -1 / 0 / 1. I and almost all programmers hate magic numbers as you know, so I introduce Order enum class. It has three values LT / EQ / GT, they can replace -1 / 0 / 1.

@@ -0,0 +1,53 @@
# Order is the order between two objects. It returns
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"It is used when"

@asterite
Copy link
Member

asterite commented Jun 25, 2016

Not sure I agree, one can do a - b to implement <=>, which is much easier and shorter (and faster!) than having to convert that to an enum value.

@makenowjust
Copy link
Contributor Author

makenowjust commented Jun 25, 2016

a - b say nothing. If one who dosen't know such a rule found this, it cannot understand this code meaning. It is more important than a code length.

Currently, many language uses integer value as a return of compare
function/operator. However, this is a vice coming from C because it
makes some magic numbers -1/0/1. I and almost all programmers hate magic
numbers as you know, so I introduce Order enum class. It has three
values LT/EQ/GT, they can replace -1/0/1.
@makenowjust
Copy link
Contributor Author

I updated some documents.

If you google which a - b means ascending or descending in the past, please you agree this PR. If not, O.K., I admire you in fact.

And I have a question, how many costs is enum's overhead?

@asterite
Copy link
Member

I mean, instead of writing a - b you now have to write a > b ? GT : (a < b ? LT : EQ) which is at most two comparisons, maybe a subtraction is faster. On the other hand, this is already done for numbers here, like that, because a subtraction isn't always good (because of unsigned integers). And if you have a and b numbers you can do a <=> b so it's no problem.

I think your change is good, I just need a bit more time to think it. The first message was my first reaction to it 😊

@rvion
Copy link

rvion commented Jun 30, 2016

I think it is saner this way.
sort_by : ([a], (a -> a -> Order)) -> [a] makes the point of the second argument clearer
types will help preventing many errors LT,EQ,GT are used instead of numbers
(how about renaming Order as Orderding?)

@makenowjust
Copy link
Contributor Author

@rvion I have not understood meaning difference between order and ordering. Of course, I know first is a noun or verb and second is a gerund. But, I think they are same meaning. How difference are thay? Please tell me!

And, I think Ordering is too long to type. We should type full specified name like Order::EQ in Crystal if not included Order in your namespace.

@rvion
Copy link

rvion commented Jun 30, 2016

@makenowjust: I have no strong opinion, but here are my 2 cents
(please, note that I'm not very much involved in crystal. I just take a look at the langage from time to time. I just wanted to share my opinion in this issue, because I would really like this very promising language not to make too many unsafe choices in the base library.)

I have not understood meaning difference between order and ordering. Of course, I know first is a noun or verb and second is a gerund. But, I think they are same meaning. How difference are thay? Please tell me!

When order is a noun, it has several meaning (place an order, etc.), and when it's a verb, it imply that it's doing something. IMHO, several meaning is bad, and the fact that both a verb and a noun share the same word is also bad because some people will misunderstand what it's doing at first sight.

Ordering has only one meaning: you give 2 object to a compare (<=>) function, and that compare function returns an ordering of the two elements given.

And, I think Ordering is too long to type. We should type full specified name like Order::EQ in Crystal if not included Order in your namespace.

I don't think it's too long at all. First of all, few people will need to type it. It's only usefull when crafting custom sorting functions, or checking if a traversable container is sorted.

There will be no need to type order or ordering in general code. This remain valid:

if a > b
  foo
end

Anyway, I also think ordering is better for grepping usage than order. Order will have meanings in lots of applications.


I initially +1'd your proposal because if crystal use ints to encode ordering, people will make bugs in their application code.

when I create a custom class, I would like to be able to easilly write a <=> function that returns an ordering given 2 elements , and get > and < for free.

@makenowjust
Copy link
Contributor Author

makenowjust commented Jun 30, 2016

I initially +1'd your proposal because if crystal use ints to encode ordering, people will make bugs in their application code.

when I create a custom class, I would like to be able to easilly write a <=> function that returns an ordering given 2 elements , and get > and < for free.

I just added Order#eq_or method. This method is the same as Haskell's mappend implementation for Ordering. It is useful to implement your custom <=>. For example in the doc:

record Person, name : String, age : Int32, height : Int32, weight : Int32 do
  include Comparable(self)

  def <=>(other : self)
    (name <=> other.name)
      .eq_or { age <=> other.age }
      .eq_or { height <=> other.height }
      .eq_or { weight <=> other.weight }
  end
end

However, I am not sure eq_or is good naming.

@rvion
Copy link

rvion commented Jun 30, 2016

@makenowjust: sincere question: why do you think that's usefull ?
I can't think of any clever usage of some semigroup-like instances for ordering. why such a custom function ?

if you want to check that a list of conditions are all matched, why not using && in your <=> method, or whatever standard crystal way of checking that all elems of a list match a predicate ?

I would -1 this function (also, I agree that naming eq_or is not great)

@makenowjust
Copy link
Contributor Author

@rvion Sorry, I used meaning of "order" as "lexicographical order", but you didn't. It caused a mistake. When you implement lexicographical order, eq_or works really well.

You want this?

require "partial_comparable"

record Person, name : String, age : Int32, height : Int32, weight : Int32 do
  include PartialComparable(self)

  def <=>(other : self) : Order?
    {% begin %}
      {% props = %w(name age height weight).map &.id %}

      return Order::EQ if {{ props.map { |p| "#{p} == other.#{p}" }.join(" && ").id }}
      return Order::LT if {{ props.map { |p| "#{p} <= other.#{p}" }.join(" && ").id }}
      return Order::GT if {{ props.map { |p| "#{p} >= other.#{p}" }.join(" && ").id }}
      nil
    {% end %}
  end
end

(But I think to need such a case is rarer than to need lexicographical order because it is not totally ordered.)

@asterite
Copy link
Member

After this long discussion, my feeling is that it's much easier with numbers:

  • less than zero means "less",
  • equal to zero means "equal"
  • greater than zero means "greater"

Basically, always take the meaning as comparing the number to zero.

To sort in a descendent way you invert the order: just apply - to the number.

Less classes and named involved and needed to be remembered.

So my vote still stays 👎 for this change.

@makenowjust
Copy link
Contributor Author

@asterite Yes, this memorization is right, but we are not learning for an exam of C now.

I have one question: where do you write this in a document? Don't tell me this should be written in sort document! This rule is for not only sort. In fact, I want to use Order enum for new Range#bsearch semantics.

Most important thing is "ordering is not just a integer value." . For example, now SemanticVersion#<=> definition is:

  def <=>(other : self) : Int32
    r = major <=> other.major
    return r if r != 0
    r = minor <=> other.minor
    return r if r != 0
    r = patch <=> other.patch
    return r if r != 0

    pre1 = prerelease
    pre2 = other.prerelease

    prerelease <=> other.prerelease
  end

By This PR, I can rewrite:

  def <=>(other : self) : Int32
    (major <=> other.major)
      .eq_or { minor <=> other.minor }
      .eq_or { patch <=> other.patch }
      .eq_or { prerelease <=> other.prerelease }
  end

If we have Order, we could give clearly semantics to ordering. This is most important. (I am sad 😢 if you consider to add such a method to Int32 class.)

@asterite
Copy link
Member

@makenowjust We can also do:

{major, minor, patch, prerelease} <=>
  {other.major, other.minor, other.patch, other.prerelease}

@makenowjust
Copy link
Contributor Author

makenowjust commented Jun 30, 2016

@asterite Ah, it looks good.

We can call +, -, *, /, abs or some integer methods if orderings are integer values. But almost all methods are meaningless. How do you think?

@asterite
Copy link
Member

@makenowjust I'm just not sure the benefits are much. Plus it'll break all code out there. But I'd like to know others' opinions.

@ysbaddaden
Copy link
Contributor

{major, minor, patch} <=> {other.major, other.minor, other.patch}

❤️

@ozra
Copy link
Contributor

ozra commented Jul 1, 2016

Conceptually it's better, but as always I'm one of those cycle-squeezers; what does a benchmark say? (aside that it's just a flip of the coin, breaking code and all..)

@skull-squadron
Copy link

This PR seems like style bikeshedding and Sayre's Law, over-engineered code-churn.

@mverzilli
Copy link

Closing as the consensus seems to be that using ints to model results in more idiomatic code. Ironically I do like having a specific Order type :P, but Crystal tries not to introduce types unless there's a very compelling and pragmatic reason.

@mverzilli mverzilli closed this Jun 17, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants