5

I am trying to use my own data type in haskell for prime numbers, but i am currently running into a few issues.

newtype Prime = Prime Integer deriving (Eq, Ord, Typeable, Show)

As soon as i am doing any numeric operation on a prime number (e.g. the "phi" function below) i want to handle the result as an Integer but i don't know how to do it.

phi :: Prime -> Prime -> Integer
phi p q = (p-1)*(q-1)

phi should return an Integer because it's not a Prime number anymore. All i get is the expected error message:

    • Couldn't match expected type ‘Integer’ with actual type ‘Prime’
    • In the expression: (p - 1) * (q - 1)
      In an equation for ‘genPhi’: genPhi p q = (p - 1) * (q - 1)

So how can i convert a my custom type to an Integer? I don't have a lot of experience with Haskell.

Iceland_jack
  • 6,848
  • 7
  • 37
  • 46
Leon K.
  • 53
  • 3
  • 1
    I was suggesting to implement `toInteger` from the `Integral` typeclass for a generic conversion, but that would require implementing all the arithmetic operations on your type which doesn't work for the subset of primes. – Bergi Dec 30 '20 at 22:14

2 Answers2

8

You can unwrap the Integer out of the Prime data constructor:

genPhi :: Prime -> Prime -> Integer
genPhi (Prime p) (Prime q) = (p-1) * (q-1)
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
3

If you have a simple newtype wrapper over an existing type, a good trick is to use the DerivingVia extension:

{-# LANGUAGE DerivingVia #-}

newtype Prime = Prime { unPrime :: Integer }
   deriving Num via Integer

phi :: Prime -> Prime -> Integer
phi p q = unPrime $ (p-1)*(q-1)

Given this, you can say:

*Main> phi (Prime 3) (Prime 5)
8

And furthermore all other numeric operations will automatically work on your Prime type, simply using their Integer equivalents.

See deriving via for details.

NB As noted in the comments, this doesn't mean GHC will make sure the result of these operations is Prime by any means; it just lets you lift the underlying operations. (But you could've made the same mistake without the deriving mechanism anyway.) If your program maintains the invariant that the Prime constructor always means the argument is a prime number, then do not use this trick. This is often not an issue as the invariant is either not clearly defined or easily enforceable, and the constructor simply acts as a reminder. But it's best to be clear on that and not use the deriving trick if you crucially depend on it.

alias
  • 28,120
  • 2
  • 23
  • 40
  • 3
    I'm not sure that `deriving Num` is a good idea here. Since it means that we can now use `Prime 2 * Prime 5`, but `Prime 10` is not a prime number. If we make a "smart constructor" for `Prime`, then we can prevent constructing `Prime` objects with non-prime values, but by making it an instance of `Num`, we thus lose this ability. – Willem Van Onsem Dec 30 '20 at 22:51
  • Primes are not numbers in the sense of `Num`, are they? – bipll Dec 30 '20 at 22:51
  • Good point. I added a clarifying edit. It's a cheap way to lift operations, but perhaps not the safest from a programming perspective if you have invariants you want to respect. – alias Dec 30 '20 at 22:54
  • 1
    The documentation for `GHC.Num` suggests that `(+)` and `(*)` are "customarily expected to define a ring". – chepner Dec 31 '20 at 13:57
  • Quite right. But I find `DerivingVia` to be much more descriptive and to read even in these simpler scenarios. – alias Jan 03 '21 at 21:04