Well, I do recognized I'm puzzled with haskell and that this is my first weekend with it.
I just wonder if the following design of a OO-class Point2D
is supposed to be written in Haskell as follows:
import Prelude hiding ( sum )
-- ...............................................................
-- "class type" : types belonging to this family of types
-- must implement distance and sum functions
-- ...............................................................
class PointFamily p where
-- p is a type of this family, not a point
distance :: p -> p -> Float -- takes two things of type p and returns a Real
sum :: p -> p -> p -- takes two things of type p and returns a p thing
-- ...............................................................
-- data type: Point2D
-- a new type with x and y coordinates
-- ...............................................................
data Point2D = Point2D { x :: Float , y :: Float }
deriving (Show) -- it is "showable/printable"
-- ...............................................................
-- Point2D belongs to PointFamily, so let's say it and
-- how to compute distance and sum for this type
-- ...............................................................
instance PointFamily Point2D where
-- ............................................................-
distance p1 p2 = sqrt (dx*dx + dy*dy)
where
dx = (x p1) - (x p2)
dy = (y p1) - (y p2)
-- ............................................................-
sum p1 p2 = Point2D { x = (x p1)+(x p2), y = (y p1)+(y p2) }
-- ...............................................................
-- global constant
-- ...............................................................
origin = Point2D 0.0 0.0
-- ...............................................................
-- main
-- ...............................................................
main = do
putStrLn "Hello"
print b
print $ distance origin b
print $ sum b b
where
b = Point2D 3.0 4.0
Yes, I know I should not try do "think OOP" in Haskell, but ... well, 1) that's going to take a long time, and 2) in practice I guess you're gonna find several OOP designs to be rewriten in Haskell
Haskell isn't an object-oriented language. All of the functionality built here from scratch already exists in a much more powerful form, using Haskell's type system.
Haskell also supports a notion of class extension. For example, we may wish to define a class Ord which inherits all of the operations in Eq, but in addition has a set of comparison operations and minimum and maximum functions: class (Eq a) => Ord a where. (<), (<=), (>=), (>) :: a -> a -> Bool. max, min :: a -> a -> a.
Overloaded Functions. A polymorphic function is called overloaded if its type contains one or more class constraints. (+) :: Num a ⇒ a -> a -> a.
An instance of a class is an individual object which belongs to that class. In Haskell, the class system is (roughly speaking) a way to group similar types. (This is the reason we call them "type classes"). An instance of a class is an individual type which belongs to that class.
First off: indeed, you should try not to "think OOP" in Haskell!
But your code isn't really OOP at all. It would be OO if you started to try virtual inheritance etc., but in this example it's more that the OO implementation happens to resemble the obvious Haskell implementation.
Only, it should be emphasised that the type class PointFamily
really doesn't have any particular 1:1 relation with the data type Point2D
, like their bundling in the OO class. You would in practise make instances of this class for any type where it conceivably works. Unsurprisingly, all of this has been done before; the most widespread equivalent of PointFamily
is AffineSpace
from the vector-spaces
package. That is a lot more general, but has in principle much the same purpose.
Just to illustrate leftroundabout's point about not needing to think OO, I took the liberty of removing the typeclass, to show how straightforward the code can be. Don't vote for this if you currently need the ability to write code that works unmodified against 2D and 3D points. But I suspect what you really need right now is a 2D point and this code does that nicely. This is on the "You ain't gonna need it" principle. If, later, it turns out you do need it, there are several ways of introducing it.
I also added bang patterns on the x and y fields, since typical 2D applications usually want those fields to be strict.
import Prelude hiding ( sum )
data Point2D = Point2D { x :: !Float , y :: !Float }
deriving (Read,Show,Eq)
distance :: Point2D -> Point2D -> Float -- takes two things of type p and returns a Real
distance p1 p2 = sqrt (dx*dx + dy*dy)
where
dx = (x p1) - (x p2)
dy = (y p1) - (y p2)
sum :: Point2D -> Point2D -> Point2D -- takes two things of type p and returns a p thing
sum p1 p2 = Point2D { x = (x p1)+(x p2), y = (y p1)+(y p2) }
origin = Point2D 0.0 0.0
main = do
putStrLn "Hello"
print b
print $ distance origin b
print $ sum b b
where
b = Point2D 3.0 4.0
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With