8

Until today I had the assumption that fromInteger in the Num class was a ring homomorphism. I had assumed this because integer is is coterminal so every ring must have a unique homomorphism from integer, so it made sense that Num, which is basically the ring class of the standard library, would include that homomorphism.

However today I was reading the laws for Num and saw that fromInteger is not required to be a homomorphism, but only required to preserve identities. So for example we can implement the Klein 4 group, and have fromInteger map 1 to multiplicative identity and everything else to the addative identity, and the result is a legal Num instance but not a homomorphism.

type Klein4
  = ( Bool
    , Bool
    )

instance
  (
  )
    => Num Klein4
  where
  ( a, b ) + ( c, d )
    = ( a /= c
      , b /= d
      )
  ( a, b ) * ( c, d )
    = ( a && b
      , b && d
      )
  negate
    = id
  fromInteger x
    = ( x == 1
      , x == 1
      )

This is a little surprising to me. So my question is why is this the case? Is the intention that fromInteger is a ring homomorphism and I'm just being pedantic, or is there a good use case where you might want to define fromInteger in a way that it is not a homomorphism (and still follows the Num laws)?

Wheat Wizard
  • 3,982
  • 14
  • 34
  • 3
    I don't think `Num` was ever expected to be formally equivalent to a ring. It was just meant to cover the basic overloading of `(+)` and `(*)` that your average programmer might expect. See http://hackage.haskell.org/package/numeric-prelude for a more concerted effort to model various structures. – chepner Jan 26 '21 at 13:28
  • 3
    The average programmer probably doesn't know what a ring homomorphism is, but will notice when `a*(b+c) != a*b + a*c`. – chepner Jan 26 '21 at 13:29
  • 1
    Does `Num` *have* laws? I had always thought it was just the wild west as far as implementations go, but if you have a link to some canonical laws, I'd love to see them – Silvio Mayolo Jan 26 '21 at 13:45
  • @SilvioMayolo Technically `Num` does not, except the documentation lists properties it is "expected to have" [here](https://hackage.haskell.org/package/base-4.14.1.0/docs/Prelude.html#t:Num). I think the distinction between laws and expected properties is pretty fine, but maybe I should have been more precise in my wording. – Wheat Wizard Jan 26 '21 at 13:58
  • @SilvioMayolo practically speaking `Num` is, for better or for worse, indeed a pretty Wild-West class. But I agree that it should at least approximate the class of rings. – leftaroundabout Jan 26 '21 at 14:32
  • 2
    Also sadly, `Num` includes `abs` and `signum`, which aren't ring operations. I'd love to see those diked out. – dfeuer Jan 28 '21 at 19:34

1 Answers1

4

Personally, I expect fromInteger to be a ring homomorphism and would be very surprised and annoyed to find an instance that didn't have that property.

Of course, Float and Double have to be the exception to this, as to every good property. Suffice it to say that I am very surprised and annoyed by this:

> fromInteger (2^64) + fromInteger (-1) == (fromInteger (2^64-1) :: Double)
False

Most of the other proposed Num properties are broken by Float and Double, too.

I don't know of any practical types that satisfy all the current Num laws but not fromInteger x + fromInteger y == fromInteger (x+y).

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380