Imperative OOP Ceremony: Interfaces

As of March 2020, School of Haskell has been switched to read-only mode.

Interfaces

An interface defines functionalities shared by a group of abstract classes. Since abstract classes are called types in FP lingo, interfaces are referred to as type classes (or classes for short). Therefore, if we want to make Shape of the previous tutorial implement Show (an interface that allows for serializing objects to Strings via the method show), we write class Show and instance Show Shape instead of interface Show and abstract class Shape implements Show in Haskell.

import Prelude hiding ((.))
x.f = f x
-- show
data Shape = Circle    { radius  :: Float }
           | Rectangle { width   :: Float
                       , height  :: Float }

instance Show Shape where
  show(Circle r)      = "Circle {radius = " ++ show r ++ "}"
  show(Rectangle w h) = "Rectangle {width = " ++ show w ++ ", height = " ++ show h ++ "}"

circ = Circle 12
rect = Rectangle 16 9

main = do {
  print(circ.show);
  print(rect.show);
}
-- /show

Haskell provides a default implementation for Show. If we want to use it, we have to use the deriving keyword:

import Prelude hiding ((.))
x.f = f x
-- show
data Shape = Circle    { radius  :: Float }
           | Rectangle { width   :: Float
                       , height  :: Float } deriving (Show)

circ = Circle 12
rect = Rectangle 16 9

main = do {
  print(circ.show);
  print(rect.show);
}
-- /show

It is worth noting that objects implementing the Show interface can be shown without using the show method. Moreover, their string representation is a value expression itself. This means that instead of Rectangle 16 9 we can write out Rectangle { width = 16.0, height = 9.0 }.

Inheritance

Let's define our own interfaces Size with a required function size. For Shapes, we will simply make it return the length of the String representation that show yields.

import Prelude hiding ((.))
x.f = f x
f `compose` g = \(x) -> f(g(x))

data Shape = Circle    { radius  :: Float }
           | Rectangle { width   :: Float
                       , height  :: Float } deriving (Show)

circ = Circle 12
rect = Rectangle 16 9
-- show
class Size a where
  size :: a -> Int

instance Size Shape where
  size = length `compose` show

main = do {
  print(circ.size);
  print(rect.size);
}
-- /show

We can also make interfaces inherit from each other. In FP lingo, we add a type constraint. If we want to make the Size interface inherit from Show we write class Show a => Size a instead of interface Size extends Show.

class Show a => Size a where
  size :: a -> Int

In the next tutorial we shall use interfaces in order to implement Fluent Interfaces. (No pun intended.)