Interfaces in Idris

module Main

import System.REPL

-- Here's a basic interface for geometric shapes.
interface Geometry a where
  area : a -> Double
  perim : a -> Double

-- For our example we'll implement this interface on
-- 'Rect' and 'Circle' types.
record Rect where
  constructor MkRect
  width, height : Double

record Circle where
  constructor MkCircle
  radius : Double

-- To implement an interface in Idris, we need to create
-- instances for our types. Here we implement 'Geometry' on 'Rect'.
implementation Geometry Rect where
  area r = r.width * r.height
  perim r = 2 * r.width + 2 * r.height

-- The implementation for 'Circle'.
implementation Geometry Circle where
  area c = pi * c.radius * c.radius
  perim c = 2 * pi * c.radius

-- If a function takes an interface type as an argument, then we can call
-- methods that are in the named interface. Here's a
-- generic 'measure' function taking advantage of this
-- to work on any 'Geometry'.
measure : Geometry a => a -> IO ()
measure g = do
  putStrLn $ "Area: " ++ show (area g)
  putStrLn $ "Perimeter: " ++ show (perim g)

main : IO ()
main = do
  let r = MkRect 3 4
  let c = MkCircle 5
  
  -- The 'Circle' and 'Rect' types both
  -- implement the 'Geometry' interface so we can use
  -- instances of these types as arguments to 'measure'.
  measure r
  measure c

This Idris code demonstrates the concept of interfaces, which are similar to type classes in Idris. Here’s a breakdown of the translation:

  1. We define a Geometry interface with area and perim methods.

  2. We create Rect and Circle record types to represent rectangles and circles.

  3. We implement the Geometry interface for both Rect and Circle using the implementation keyword.

  4. The measure function takes any type that implements Geometry and prints its area and perimeter.

  5. In the main function, we create instances of Rect and Circle and call measure on both of them.

To run this program, you would typically save it in a file with a .idr extension (e.g., geometry.idr) and then use the Idris compiler:

$ idris geometry.idr -o geometry
$ ./geometry
Area: 12.0
Perimeter: 14.0
Area: 78.53981633974483
Perimeter: 31.41592653589793

This example showcases how Idris handles interfaces (type classes) and how they can be used to create generic functions that work with multiple types. The concept is similar to interfaces in other languages, but Idris’s approach is more closely related to Haskell’s type classes.