Generics in Haskell

Haskell has supported parametric polymorphism (which is similar to generics) since its inception. Here’s how we can implement the same concepts in Haskell:

import Data.List (elemIndex)

-- SlicesIndex is a function that takes a list of any Eq type and an element of that type
-- and returns the index of the first occurrence of v in s, or Nothing if not present.
slicesIndex :: Eq a => [a] -> a -> Maybe Int
slicesIndex s v = elemIndex v s

-- As an example of a generic type, List is a singly-linked list with values of any type.
data List a = Empty | Cons a (List a)

-- We can define functions on our generic List type
push :: a -> List a -> List a
push v Empty = Cons v Empty
push v (Cons x xs) = Cons x (push v xs)

-- AllElements returns all the List elements as a regular list
allElements :: List a -> [a]
allElements Empty = []
allElements (Cons x xs) = x : allElements xs

main :: IO ()
main = do
    let s = ["foo", "bar", "zoo"]

    -- When using generic functions in Haskell, type inference often determines the types automatically
    putStrLn $ "index of zoo: " ++ show (slicesIndex s "zoo")

    -- We could also specify the types explicitly if needed
    let _ = slicesIndex (s :: [String]) ("zoo" :: String)

    -- Create and populate a List of integers
    let lst = foldr push Empty [23, 13, 10]
    putStrLn $ "list: " ++ show (allElements lst)

In this Haskell version:

  1. We use Maybe Int instead of -1 to represent “not found” in slicesIndex.

  2. Haskell’s type system is inherently generic, so we don’t need special syntax for generic functions or types.

  3. We define our own List type as a recursive data structure, which is a common pattern in Haskell.

  4. The push function is implemented recursively, adding the new element at the end of the list.

  5. Type inference in Haskell is very powerful, so we rarely need to specify types explicitly.

  6. We use foldr to build our list in reverse order to match the original example.

To run this program, save it as generics.hs and use:

$ runhaskell generics.hs
index of zoo: Just 2
list: [10,13,23]

Haskell’s type system and its support for parametric polymorphism make it well-suited for generic programming. The language’s strong type inference often allows us to write very generic code without explicitly specifying types.