Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheritance to extend a data structure in Haskell

Tags:

A C++ programmer trying to learn Haskell here. Please excuse this probably easy question. I want to translate a program that represents 3D shapes. In C++ I have something like:

class Shape { public:   std::string name;   Vector3d position; };  class Sphere : public Shape { public:   float radius; };  class Prism : public Shape { public:   float width, height, depth; }; 

I am trying to translate this to Haskell (using records?) so that I can have some functions which know how to operate on a Shape (like accessing its name and position), and others than know only how to operate on spheres, like calculating something based on its position and radius.

In C++ a member function could just access these parameters but I'm having a hard time figuring out how to do this in Haskell with records, or type classes, or whatever.

Thanks.

like image 500
Steve Avatar asked Nov 05 '09 00:11

Steve


1 Answers

The straight-forward translation.

type Vector3D = (Double, Double, Double)  class Shape shape where     name :: shape -> String     position :: shape -> Vector3D  data Sphere = Sphere {     sphereName :: String,     spherePosition :: Vector3D,     sphereRadius :: Double }  data Prism = Prism {     prismName :: String,     prismPosition :: Vector3D,     prismDimensions :: Vector3D }  instance Shape Sphere where     name = sphereName     position = spherePosition  instance Shape Prism where     name = prismName     position = prismPosition 

You usually wouldn't do this, though; it's repetitious and polymorphic lists require language extensions.

Instead, sticking them into a single closed datatype is probably the first solution you should go for.

type Vector3D = (Double, Double, Double)  data Shape   = Sphere { name :: String, position :: Vector3D, radius :: Double }   | Prism { name :: String, position :: Vector3D, dimensions :: Vector3D } 

You can certainly simulate multiple levels of inheritance by creating more typeclasses:

class (Shape shape) => Prism shape where     dimensions :: Vector3D data RectangularPrism = ... data TriangularPrism = ... instance Prism RectangularPrism where ... instance Prism TriangularPrism where ... 

You can also simulate it by embedding datatypes.

type Vector3D = (Double, Double, Double)  data Shape = Shape { name :: String, position :: Vector3D }  data Sphere = Sphere { sphereToShape :: Shape, radius :: Double } newSphere :: Vector3D -> Double -> Shape newSphere = Sphere . Shape "Sphere"  data Prism = Prism { prismToShape :: Shape, dimensions :: Vector3D }  data RectangularPrism = RectangularPrism { rectangularPrismToPrism :: Prism } newRectangularPrism :: Vector3D -> Vector3D -> RectangularPrism newRectangularPrism = (.) RectangularPrism . Prism . Shape "RectangularPrism"  data TriangularPrism = TriangularPrism { triangularPrismToPrism :: Prism } newTriangularPrism :: Vector3D -> Vector3D -> TriangularPrism newTriangularPrism = (.) TriangularPrism . Prism . Shape "TriangularPrism" 

But simulating OO in Haskell is not anywhere near as satisfying as actually thinking in a Haskellish way. What are you trying to do?

(Also note that all of these solutions only permit upcasts, downcasting is unsafe and disallowed.)

like image 56
ephemient Avatar answered Sep 20 '22 14:09

ephemient