Designing Haskell libraries for qualified import

14 Likes

Ah I was also talking to @ners about this subject at NixCon. There’s also this neat little GHC option that simplifies the syntax behind these qualified import rules you listed called require. It’s not up to date with latest GHCs and it doesn’t quite line up with your rules but maybe this can be some inspiration to standardizing the pattern.

5 Likes
2 Likes

Great article, thanks a lot!

1 Like

Thank you for this, I’m switching to ImportQualifiedPost.

This brings up the fact that whenever you bring in a new library you’re not sure how the imports will clash. Even a name like Span.ID could clash with something else. So I always import qualified by default with and maybe refactor later if I know the names well enough. Sometimes with a new library it helps to just convert the unqualified import tutorial code to qualified imports so that you know what is where.

The other thing is that if you only work on a codebase occaisionally you might forget if you import it as Span.ID or ID when you come back to it and then you start a new file with a different import name.

At first I thought that a solution might be to allow the module to define a default alias name but now I feel like maybe it’s best if there could be a file in the project that keeps track of import names.

So if I can put import Effectful.OpenTelemetry.Tracing.Span.ID qualified as Span.ID in one file and then in the rest of the code use import Span.ID qualified. Then at least if some other library uses Span.ID I can add import OtherLibrary.Span.ID qualified as OtherSpan.Id in the imports file.

1 Like

And I believe @taylorfausak is a huge fan of qualified imports. I was amazed when I read the source code of cabal-gild.

4 Likes

Indeed I am! Qualified imports are the best. I even built Imp, a GHC plugin for automatically importing modules based on qualified usage.

4 Likes

I’m also a fan.

3 Likes

This is basically the style I like to use. Thanks for writing it up!

1 Like

So colloquially call this Java style Haskell and I follow this very heavy in Hazy. I’m pretty happy with the result, I rarely ever have name conflicts and I think the end result is pretty readable.

Let me talk about my major issue I’ve run into following philosophy and that is .hs-boot hell. Anytime I need types or functions there are mutually recursive, I need to painfully create new .hs-boot modules. Sometimes I even need to reorganize my code because the resulting hs-boot files would be mutually recursive (see There are mutually recursive programs that GHC can't compile). For reference I currently I have 445 normal Haskell files and 95 .hs-boot modules, so 20% of my modules need .hs-boot files (this could probably be reduced).

Still, even with this major problem, I think the end result is much better then alternative.

2 Likes