haskell

Alternative for methods in Haskell


Suppose I have the following two classes in some object-oriented language:

class List:
  def reverse() -> None:
    ...

class Car:
  def reverse() -> None:
    ...

Now, if I have an instance list of List and an instance car of Car, there is absolutely no ambiguity as to which reverse method I am calling when I write list.reverse() or car.reverse(). This is very nice!

In Haskell, the analogous code would be something like:

data List = ...

reverse :: List -> List
reverse = ...

data Car = ...

reverse :: Car -> Car
reverse = ...

There is an immediate problem: reverse is defined twice. This is not so difficult to solve. We can define two separate modules List and Car, so that the two different reverse functions live in different namespaces. Then if I have a variable list of type List and a variable car of type Car, I could call the respective reverse functions by writing List.reverse list or Car.reverse car. This is the analog of the code list.reverse() and car.reverse() from the previous example.

The most obvious issue is that typing List.reverse list instead of list.reverse() is a lot more annoying (especially if we replace the List type by something with a longer name). Is there some standard way to deal with this issue in Haskell? Note that typeclasses are not a solution here; the two reverse functions have completely different goals.


Solution

  • The standard/most common lightweight idiom in this space is to use the module system in the way you describe. For example, you will frequently see code like:

    import qualified Data.Vector as V
    import qualified Data.Map as M
    
    foo = (V.fromList bar, M.fromList baz)
    

    This is so common that in fact a handful of idiomatic letters have been essentially reserved (in my mind at least) -- if I see M, V, S, T, or BS I don't even bother looking at the import list any more; I just know they're one of the variations on Data.Map, Data.Set, Data.Vector, Data.Text, and Data.ByteString.* Note that this is an especially interesting example of this, as V.fromList does not have any vectors as arguments, so the OOP solution you describe wouldn't even be directly possible here. (Of course each OOP language has its own way of working around such issues.)

    There was at one point a somewhat controversial language extension proposal called type-directed name resolution which sat in this space. Less controversially (but still somewhat controversial) is the extant record field disambiguation language extension, but it is as the name suggests only for record fields. (See also duplicate record fields.)

    Finally, there is the typeclass mechanism, which I think you dismiss too easily. This is used by, e.g., gi-gtk to good effect, and is combined with overloaded symbols, so that for example

    #foo bar baz
    

    might be disambiguated to one of these depending on the type of bar:

    boxFoo bar baz
    alertDialogFoo bar baz
    

    It is somewhat common for wildly different widgets to want method names that are superficially the same but have different meanings, and even different arities, so that is no barrier here.


    * There's probably others that didn't spring readily to mind as I was answering.