I went through a you-tube video and learnt that the person declared a new data type using parameterised data type as :
data Quad a = Quad a a a a
instance (Show a) => Show (Quad a) where
show (Quad a b c d) = (show a) ++ " " ++ -- code continues for printing
My doubt is why do we need the first line of the code. I did something like this and it still works. My code :
data Q = Q Integer Integer Integer Integer
instance Show Q where
show (Q a b c d) = (show a)++" " ++ --code continues for printing
What is the use of the first line namely data Quad a = Quad a a a a
, when instead we can do it by my method as shown just above ? Please Help. Thanks in advance !
I don't know how familiar you are with imperative programming languages like Java, but I will assume that you are familiar with generics. Now your defintion of Q
would be - more or less - equivalent to something in Java like:
public class Q {
private Integer field1;
private Integer field2;
private Integer field3;
private Integer field4;
public Q (Integer field1, Integer field2, Integer field3, Integer field4) {
this.field1 = field1;
this.field2 = field2;
this.field3 = field3;
this.field4 = field4;
}
}
(yeah I know Integer
in Java is not equivalent to Integer
in Haskell, but that's not an issue here).
Now this thus limits us to working with int
s. A problem might occur what to do in case we want a quaternion with double
s, or Car
s. So that won't work very effectively.
The original definition of Quad
however would be something in Java like:
public class Quad<A> {
private A field1;
private A field2;
private A field3;
private A field4;
public Quad (A field1, A field2, A field3, A field4) {
this.field1 = field1;
this.field2 = field2;
this.field3 = field3;
this.field4 = field4;
}
}
So you can work with a Quad<Integer>
which is - more or less - equivalent to the above definition of Q
. But I can easily work with Quad<Double>
and Quad<Car>
to construct quaternions of doubles, cars, horses,...
Now you might think that a quaternion of horses does not make any sense. Quaternions are usually indeed specified over numbers. But there are several data structures to represent numbers: Integer
s can - as the name suggest - only represent integral values. Perhaps you want to allow Double
s as well. Furthermore Integer
can represent all integral values (until the memory is exhausted). Although that's of course a nice feature, it comes with a cost: performance. It usually takes more time to do computations on Integer
s (even if they have the same value). So if you want to boost performance, you may look for Quad Int16
for instance.
By using a type parameter you allow yourself a form of flexibility: you do not have to define a data type that looks almost the same as another one.
Furthermore you can define - like the Youtube lessons shows - type instances where you put constraints on the a
. So for all Quad a
s where a
can be show
ed, then you can show the Quad a
. You can also put constraints on functions. Say for instance you want to provide a way to calculate the sum of the "elements" of the quad. You can define:
sumquad :: Num a => Quad a -> a
sumquad (Q x y z t) = x + y + z + t
So now you have defined a sumquad
function for all Quad a
types where a
is an instance of the Num
typeclass (and thus supports addition). If we would work with Q
, there should be a sumquadQ
for Q
, a sumquadQDouble
for QDouble
(a hypothetical data QDouble = QDouble Double Double Double Double
), etc. So it would require a large amount of work and would result in less elegant code.