Skip to content

Ord for PortNnumber behaves strangely #346

Description

@MichaelXavier

I don't know if this is something you all want to solve but there's some really weird behavior that happens due to eagerly converting for endiannes at construction of PortNumber (I assume that's what's going on)

50000 < (51000 :: PortNumber)
False

50000 < (52000 :: PortNumber)
True

I discovered this when attempting to validate a port range (PortNumber, PortNumber) such that the first is <= the second. I expect one solution would be to covert back to integer for Ord. Another would be to not pre-convert the word into network byte order until you need it internally (perhaps even creating an internal newtype to convert between the two), but that sounds very tricky to get right and a much larger diff.

Activity

  1. kazu-yamamoto commented on Aug 29, 2018

    @kazu-yamamoto
    Collaborator

    PortNumber holds an Word16 in network-byteoder.
    So, if you are using a little endian CPU, this happens.
    Since I'm not the original designer for PortNumber, I don't know what is the correct behavior.

  2. kazu-yamamoto commented on Aug 29, 2018

    @kazu-yamamoto
    Collaborator

    Perhaps, deriving Ord is an evil.

    newtype PortNumber = PortNum Word16 deriving (Eq, Ord, Typeable)
    

    Should we implement instance Ord PortNumber with portNumberToInt?

    @eborden @Mistuke What do you think?

  3. eborden commented on Aug 29, 2018

    @eborden
    Collaborator

    There are a few questions here:

    1. Should PortNumber have an Ord instance?
      Being able to predicate on port ranges seems like a reasonable use case. Even generating port ranges with randomR. As well I might want a Set of them, Hashable can provide that, but not ranges. So I'd say yes.

    2. What is the most natural instance?
      We typically think of these artifacts as integer-like. Thinking in endians is an implementation artifact of a given environment. Using an integer based order seems more natural.

    3. Is there a "correct" instance?
      Is the byte order instance more correct? Should the given environment influence how this order behaves. To me that sounds surprising. I don't know if it is more correct or just a choice.

    4. Is changing the instance a breaking change?
      I'm sure someone is using this instance for something. I'd call this a breaking change.

  4. Mistuke commented on Aug 29, 2018

    @Mistuke
    Collaborator

    Hmm yes that would work, you'll need both Eq and Ord. But looking at the code, it seems we convert back to Int order for almost every operation using portNumberToInt, which makes me wonder if there's any point in maintaining PortNumber in network byte order.

    It seems to me the only place this actually matters is the Storable instance. I'm more inclined to say we should drop all the current instances, use GeneralizedNewtypeDeriving to derive them, then they're internally consistent, and change the Storable instance to convert back and forth between network byte order.

    That would make the instances simpler I think.

    Is there a "correct" instance?
    Is the byte order instance more correct? Should the given environment influence how this order behaves. To me that sounds surprising. I don't know if it is more correct or just a choice.

    I don't know about correct, but it's inconsistent with the other instances such as Num. We don't perform for instance the addition on the network byte-ordered version, so why would we do comparisons?

  5. eborden commented on Aug 29, 2018

    @eborden
    Collaborator

    Consistency is a fantastic argument in favor of a change. I'm 👍 for what @Mistuke proposed.

  6. kazu-yamamoto commented on Aug 29, 2018

    @kazu-yamamoto
    Collaborator

    OK. I will work according to @Mistuke's approach.

  7. MichaelXavier commented on Aug 29, 2018

    @MichaelXavier
    ContributorAuthor

    Thanks y'all! Sounds like a good solution to me!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions