7

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

UML design of 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

cibercitizen1
  • 20,944
  • 16
  • 72
  • 95

2 Answers2

10

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.

Community
  • 1
  • 1
leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
2

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
cibercitizen1
  • 20,944
  • 16
  • 72
  • 95
GarethR
  • 724
  • 5
  • 7
  • Just curious about what !Float means. – cibercitizen1 Dec 02 '13 at 11:24
  • 1
    @cibercitizen1: it makes the data fields _strict_, essentially an optimisation. (Normally, Haskell values all start out as lazy thunks and may only be evaluated to concrete values later. Putting in `!` forces this right when the `Point` as a whole is evaluated.) — BTW, [using `Float` doesn't make much sense in Haskell](http://www.haskell.org/haskellwiki/Performance/Floating_point), except sometimes in _unboxed arrays_. – leftaroundabout Dec 02 '13 at 11:30
  • @GarethR are you sure those `p` in the type signatures are not too general? – Ingo Dec 02 '13 at 13:19
  • @Ingo, thanks for the heads up. I've replaced them by `Point2D`. – GarethR Dec 02 '13 at 16:04