Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding namespace pollution in Haskell

This is a very hairy problem. There are several proposals for fixing the record system. On a related note, see TDNR and related discussion on cafe.

Using the currently available language features, I think the best option is defining the two types in two different modules, and doing a qualified import. On top of this, if you want, you can implement some type class machinery.

In Customer.hs

module Customer where
data Customer = Customer { ..., foo :: Int, ... }

In Product.hs

module Product where
data Product = Product { ..., foo :: Int, ... }

While using them, in Third.hs

module Third where

import qualified Customer as C
import qualified Product as P

.. C.foo ..
.. P.foo ..

Yet, I imagine it won't be too late before you hit the problem about recursively dependent modules.


(FYI, this question is almost certainly a duplicate)

Solutions:

1) Prefix the fields with a tag indicating the type (extremely common)

data Customer = Customer {..., cFoo :: Int, ...}

2) Use type classes (less common, people complain prefixes like cFoo are inconvenient but evidently not so bad that they will write a class and instance or use TH to do the same).

class getFoo a where
    foo :: a -> Int

instance getFoo Customer where
    foo = cFoo

3) Use better field names If the fields are actually different (which isn't always true, my computer has an age as does my employee), then this is the best solution.


There's a language extension DuplicateRecordFields that allows duplication of field functions and makes its type to be inferred by type annotation.

Here is a little example (haskell-stack script):

#!/usr/bin/env stack
-- stack runghc --resolver lts-8.20 --install-ghc

{-# LANGUAGE DuplicateRecordFields #-}

newtype Foo = Foo { baz :: String }
newtype Bar = Bar { baz :: String }

foo = Foo { baz = "foo text" }
bar = Bar { baz = "bar text" }

main = do
  putStrLn $ "Foo: " ++ baz (foo :: Foo) -- Foo: foo text
  putStrLn $ "Bar: " ++ baz (bar :: Bar) -- Bar: bar text

See also the Has package: http://chrisdone.com/posts/duck-typing-in-haskell

And if you really need extensible records now, you can always use HList. But I wouldn't recommend this until you're really familiar and comfortable with medium-advanced Haskell, and even then I'd triple check you need it.

Haskelldb has a slightly more lightweight version: http://hackage.haskell.org/packages/archive/haskelldb/2.1.0/doc/html/Database-HaskellDB-HDBRec.html

And then there's another version of extensible records as part of the grapefruit frp library: http://hackage.haskell.org/package/grapefruit-records

Again, for your purposes, I'd bite the bullet and just rename the fields. But these references are to show that when you really need the full power of extensible records, there are ways to do it, even if none are as pleasant as a well-designed language extension would be.