This is a dumb question that's been bugging me for a bit. Why can't I write a newtype with multiple parameters,
newtype A = A Int Int
while the tuple version is just fine?
newtype A = A (Int, Int)
the former is much nicer in things like pattern matching.
newtype A = A Int
creates a type isomorphic to Int
. That is, it behaves exactly like Int
w.r.t. e.g. bottom
, but under a different name.
This is in contrast with data A = A Int
which creates a lifted type that behaves differently from Int. There's another value added that is not in Int
: A undefined
(which is distinct from undefined::A
).
Now, newtype A = A (Int, Int)
creates a type isomorphic to (Int, Int)
. Which is, incidentally, exactly exactly what data A = A Int Int
does.
So if we admit newtype A = A Int Int
as being equivalent to newtype A = A (Int, Int)
, what do we have? newtype A = A Int Int
is equivalent to newtype A = A (Int, Int)
which is equivalent to data A = A Int Int
.
newtype A = A Int Int
is equivalent to data A = A Int Int
(so newtype
is redundant in this case), but
newtype A = A Int
is not equivalent to data A = A Int
(which is the whole point of having newtype
in the first place).
So we must conclude that newtype A = A Int Int
being equivalent to newtype A = A (Int, Int)
creates redundancy and inconsistency, and we're better off not allowing it.
There's probably no way to give newtype A = A Int Int
some other meaning which is free from these inconsistencies (or else it would be found and used I suppose ;)