Avoiding namespace pollution in Haskell

HaskellNamespacesTypesRecords

Haskell Problem Overview


I'm using lots of different records in a program, with some of them using the same field names, e.g.

data Customer = Customer { ..., foo :: Int, ... }
data Product = Product { ..., foo :: Int, ... }

Now as the accessor function "foo" is defined twice, I get the "Multiple declarations" error. One way to avoid this would be using different modules that are imported fully qualified, or simply renaming the fields (which I don't want to do).

What is the officially suggested way of dealing with this in Haskell?

Haskell Solutions


Solution 1 - 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.

Solution 2 - Haskell

(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.

Solution 3 - Haskell

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

Solution 4 - Haskell

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.

Solution 5 - Haskell

One possible solution that will make your code less verbose is to define <.> as:

(<.>) :: (Emiter e1, Emiter e2) => e1 -> e2 -> String
lhs <.> rhs = emit lhs <> emit rhs

Then emitters can look like:

class Emiter n where
    emit :: n -> String 

instance Emiter String where
    emit = id

instance Emiter A where 
    emit A {
        foo = foo'
        bar = bar'
    } = foo' <.> "--" <.> bar'

instance Emiter B where
    emit B {
        foo = foo'
        bar = bar'
    } =  "[" <.> bar' <.> foo' <.> "]"

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser434817View Question on Stackoverflow
Solution 1 - HaskellOzgurView Answer on Stackoverflow
Solution 2 - HaskellThomas M. DuBuissonView Answer on Stackoverflow
Solution 3 - HaskellunclechuView Answer on Stackoverflow
Solution 4 - HaskellsclvView Answer on Stackoverflow
Solution 5 - Haskelluser9303491View Answer on Stackoverflow