haskellfunctional-programmingtype-safetynewtype

What is the proper way of wrapping an Int (not a general type) in another type if type safety is the only motive?


I was using a Map String (Int, Int) where the two Ints were used as the numerator and denominator to form a Rational to be passed to fromList.

Then I realized that in a point in my code I had used those two Ints the other way around (as denominator and numerator, i.e. swapped). It took some time to find out what was wrong, so afterwards I thought that maybe I should use two dedicated types, so I wrote

newtype MyNum = MyNum Int
newtype MyDen = MyDen Int

but then I had to add a few instances for everything else to work (given the uses I make of those Ints, I had to add deriving (Eq, Ord, Show, Read)), and also to add some two functions to unwrap the Ints from within the two types so that I could actually apply things like (+1) to those wrapped Ints.

But this means that code starts looking a bit ugly, with things like (MyNum . (+1) . unwrapMyNum), whereas something like (+1) <$> would be much preferrable.

But that means that MyNum should be a Functor; but it can't because it's a hard type, not a type constructor.

But I don't want to make it a type constructor because I don't want to wrap anything in it other than a Int.

Any suggestion?


Solution

  • I think the actual problem has nothing to do with your concrete question. Just don't use tuples, use a suitable type that expresses what both integers represent together. In this case the obvious choice would be to use Ratio Int, with the caveat that it does not store arbitrary pairs but properly normalises the fractions (which is generally a good thing). If that's not appropriate for you, just write your own Ratio type.

    That said, there are also many things you can do to make a newtype wrapper around a single type more convenient: