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:
We use
Maybe Int
instead of-1
to represent “not found” inslicesIndex
.Haskell’s type system is inherently generic, so we don’t need special syntax for generic functions or types.
We define our own
List
type as a recursive data structure, which is a common pattern in Haskell.The
push
function is implemented recursively, adding the new element at the end of the list.Type inference in Haskell is very powerful, so we rarely need to specify types explicitly.
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.