Reputation: 93
I am looking for a solution that is quite generic I think. My goal is to find a clean way to do operations on Points (coordinates), either in 2D or 3D. my data Point is an instance of Num that gives functions to do basics maths operations.
data Point = Point2D Float Float
| Point3D Float Float Float deriving Show
add :: Point -> Point -> Point
add (Point2D xa ya) (Point2D xb yb) = Point2D (xa+xb) (ya+yb)
add (Point3D xa ya za) (Point3D xb yb zb) = Point3D (xa+xb) (ya+yb) (za+zb)
divT (Point2D x y) v = Point2D (x / v) (y / v)
divT (Point3D x y z) v = Point3D (x / v) (y / v) (z / v)
fromIntegerP :: Integer -> Point
fromIntegerP v = Point2D (fromIntegral v) 0
--fromIntegerP v = Point3D (fromIntegral v) 0 0
instance Num Point where
(+) = add
fromInteger = fromIntegerP
p2D1 = Point2D 1.0 2.0
p2D2 = Point2D 4.0 5.0
p3D1 = Point3D 1.0 2.0 3.0
p3D2 = Point3D 6.0 6.0 6.0
main = do
putStrLn . show $ sum [p2D1,p2D2]
putStrLn . show $ sum [p3D1,p3D2]
This code outputs :
Point2D 5.0 7.0
*** Exception: pointStackoverflow.hs:(5,1)-(6,75): Non-exhaustive patterns in function add
... because every Point.fromInteger produces a Point2D, even if we expect a Point3D. I would like to have a way to say in "instance Num Point" that if a Point2D is needed then fromInteger is fromIntegerToPoint2D else fromIntegerToPoint3D. But I don't know how to make a choice based on the return type.
Any hint ? Thank you
Upvotes: 2
Views: 137
Reputation: 93
Here is one solution :
class Point a where
add :: a -> a -> a
fromIntegerP :: Integer -> a
data Point2D = Point2D Float Float deriving Show
data Point3D = Point3D Float Float Float deriving Show
instance Point Point2D where
add (Point2D xa ya) (Point2D xb yb) = Point2D (xa+xb) (ya+yb)
fromIntegerP v = Point2D (fromIntegral v) 0
instance Point Point3D where
add (Point3D xa ya za) (Point3D xb yb zb) = Point3D (xa+xb) (ya+yb) (za+zb)
fromIntegerP v = Point3D (fromIntegral v) 0 0
instance Num Point2D where
(+) = add
fromInteger = fromIntegerP
instance Num Point3D where
(+) = add
fromInteger = fromIntegerP
p2D1 = Point2D 1.0 2.0
p2D2 = Point2D 4.0 5.0
p3D1 = Point3D 1.0 2.0 3.0
p3D2 = Point3D 6.0 6.0 6.0
main = do
putStrLn . show $ sum [p2D1,p2D2]
putStrLn . show $ sum [p3D1,p3D2]
I am still looking for improvements (instance Num Point2D where
and instance Num Point3D where
) but it works !
Upvotes: 0
Reputation: 52029
You want to make Point2D
and Point3D
be different types, not different constructors for the same type.
Start with this and fill out the definitions:
data Point2D = Point2D Float Float
data Point3D = Point3D Float Float Float
instance Num Point2D where
fromInteger = ...
(+) = ...
instance Num Point3D where
fromInteger = ...
(+) = ...
Upvotes: 2