Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I deal with infix symbols looking ugly with qualified names

I am normally a firm believer of using namespaces (qualified module names) in most languages I program in since it is very good to know at a glance where a certain identifier came from. In Haskell there is also the added advantage of avoiding common name clashes with Prelude functions.

However, I feel like having to put a namespace on an infix symbol (or other short, DSL-y identifiers) looks really weird so I am tempted to reexport values, like this:

import qualified Data.Sequence as Seq
(|>) = (Seq.|>)
(<|) = (Seq.<|)

What is bugging me now is that

  • Manually reexporting values feels like boring boilerplate.

  • Manually reexporting values goes around the existing module system and does not seem to work with data constructors (and possibly other things I didn't come across yet)

    import qualified Data.Sequence as Seq
    (:>) = (Seq.:>)  --gives me a parse error:
                     --"Not in scope: data constructor `:>'"
    

How do I reconcile infix symbols and namespacing? Should I just give up and learn to namespace everything? Are there estabilished Haskell best-practices regarding namespacing and symbols?

like image 211
hugomg Avatar asked Jan 28 '12 05:01

hugomg


2 Answers

Well, one thing you can do is import it twice:

import Data.Sequence ((|>), (<|), ViewR ((:>)))
import qualified Data.Sequence as Seq

This will import only :>, |> and <| unqualified, leaving everything else qualified. Note that since :> is a data constructor, you also have to import its data type (ViewR), but you do not have to import the rest of ViewR's constructors.

Additionally, if you're worried about conflicts, you should just hide the operator as appropriate:

import Prelude hiding ((.))

If you're using a sane library, a conflict with Prelude means the library function is designed to replace that Prelude function (e.g. Control.Category) so you want to let it replace the default meaning.

As far as best practices go, I have never seen anybody use qualified operators unless there is a conflict or they're at GHCi. All told, even factoring in the advantage of knowing where an operator is from, it makes the code much less readable.

like image 118
Tikhon Jelvis Avatar answered Nov 09 '22 13:11

Tikhon Jelvis


I generally import the type names, constructors and operators unqualified, and everything else qualified:

import Data.Sequence (Seq, ViewL(..), ViewR(..), (|>), (<|))
import qualified Data.Sequence as Seq

This double-import, unqualified-type-name style is recommended by the documentation to Data.Map and other standard containers.

Still, you can't always import operators unqualified — for instance, if you're using Array/Vector and Map in the same module, you can't import the (!) from both unqualified. In that case, I'd usually just use it qualified. It looks weird, but it's better than the other options (like coming up with your own name for one of them to avoid a clash). Of course, perhaps this is a good thing if it stops people using unsafe functions like (Data.Map.!) :)

like image 41
ehird Avatar answered Nov 09 '22 14:11

ehird