Return type overloading in Haskell

Function overloading is very common in many programming languages like C# but most of the languages that I have used only support overloading based on function arguments. For example in C# there are several overloads on the String.IndexOf function where each overload differs in what argument type it accepts.

image

This overloading support doesn’t extend to function return types. If an additional overload is added (bool String.IndexOf(char)) that returns true if the character is found in the string the compiler will give an error like “Type ‘String.IndexOf’ already defines a member called ‘IndexOf’ with the same parameter types”.

The Haskell Way

Haskell supports both function argument type and return type overloading using type classes.

Given two functions indexOfExists and indexOfPos.

indexOfPos value str =
  case elemIndex value str of
    Just i -> i
    Nothing -> -1

indexOfExists value str =
  case elemIndex value str of
    Just _ -> True
    Nothing -> False

I can create a type class which defines an indexOf function and add both indexOfPos and indexOfExists to this class.

class StringOps a where
  indexOf :: Char -> String -> a

instance StringOps Int where
  indexOf value str = indexOfPos value str

instance StringOps Bool where
  indexOf value str = indexOfExists value str

Now when I use the indexOf method Haskell will figure out whether to use indexOfExists or indexOfPos based on the return type.

*Main> indexOf 'e' "matthew" :: Bool
True
*Main> indexOf 'e' "matthew" :: Int
5

 

Real World Use Case

This functionality is used beautifully in the Text.Regex.Posix module.  This module contains regular expression functions and in particular exposes the =~ operator. This operator lets you match a regex pattern against a string and its behavior is overloaded based on its return type. This is useful since whether you want to know if the regex matches, what the first match is or get a list of all matches you can just use the =~ operator and the return value type will match the code you write. Here are some examples of output from the =~ operator based on return type:

*Main Text.Regex.Posix> "mississippi" =~ "i(s|p)" :: Bool
True

*Main Text.Regex.Posix> "mississippi" =~ "i(s|p)" :: String
"is"

*Main Text.Regex.Posix> "mississippi" =~ "i(s|p)" :: Int
3

*Main Text.Regex.Posix> "mississippi" =~ "i(s|p)" :: (String,String,String)
("m","is","sissippi")

*Main Text.Regex.Posix> "mississippi" =~ "i(s|p)" :: (Int,Int)
(1,2)