Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Current best-practice regarding record global namespacing

Tags:

haskell

I'm checking that there isn't some current practice with template-haskell/some lens fanciness to deal with the simple case of:

data Person = Person {
  name :: String,
  ...
}

data Company = Company {
  name :: String,
  ...
}

Currently I'm avoiding polluting the global namespace by qualifying the import, but it does make the record-access clumsy.

import Person as P

isFred :: Person -> Bool
isFred p = (P.name p) == "Fred"

Is there really still no better way to access record fields?


I'm accepting @Emmanuel Touzery's answer because of the useful link to another question covering the same ground. The other question doesn't show up for a search on "haskell namespace". Nothing wrong with the other answers, but I can only accept the one.

The solution mentioned there uses Template Haskell, lenses, type-classes and more to basically create one typeclass for each field "HasName" with a single function "name". Each data-type is then an instance of that class with its own implementation. Then there's some magic I don't entirely understand allowing different types to be involved.

For any Haskell newbies wondering what this is all about, it's because records are basically tuples with field-selectors implemented as ordinary functions selecting (e.g.) the second element of that tuple. If you export these field-selector functions then they sit in the global namespace and sooner or later (usually sooner) you get a clash.

So - you either qualify the imports (as in my example above) or try to come up with names that don't clash (prefix the name and hope for the best).

The lens stuff is all the rage as of 2013 and allows composition of field selectors / getters + setters etc. The basic idea of lenses isn't too complicated but the implementation goes right over my head.


For the record (ha!) I think the solution in the other post is probably what I'm after, but it does involve a large amount of magic (5 extensions just to fake record namespacing).

like image 353
Richard Huxton Avatar asked Oct 04 '13 08:10

Richard Huxton


2 Answers

Generally there are only two approaches, but unfortunately there's no consesus about them in the community:

  1. Place your records with functions against them in separate files and use them qualified with complete alias as in:

    import qualified Data.Person as Person; import Data.Person (Person)
    
    isFred :: Person -> Bool
    isFred p = (Person.name p) == "Fred"
    

    Consider this approach the same as in languages like Java, where a file contains just one class.

  2. Place your records in the same file, while preceding the field names with the names of records, e.g.:

    data Person = Person {
      personName :: String,
      personAge :: Int,
      ...
    }
    

Neither of the lenses-libraries approaches this problem.

like image 183
Nikita Volkov Avatar answered Oct 06 '22 19:10

Nikita Volkov


A similar question was already asked, and you can see an answer suggesting lenses here: https://stackoverflow.com/a/17488365/516188

Right now lenses are a big buzzword in the Haskell community, they definitely have their uses, and they may be part of the solution to this namespacing problem, long-term. But currently people using lenses only to solve that problem would be a small minority I think. As Nikita Volkov said, qualified imports and prefixing would be the typical solutions at this point.

UPDATE: found out about this other option that is not finalized yet but seems promessing. It is mentioned here at the end of this blog post.

like image 25
Emmanuel Touzery Avatar answered Oct 06 '22 19:10

Emmanuel Touzery