Sugar for importing some things unqualified and the rest qualified

The “import some things unqualified and the rest qualified as Something” is very common.

A few examples are

import Data.Text (Text)
import qualified Data.Text as T
import Data.Set (Set)
import qualified Data.Set as S
import Data.Map (Map)
import qualified Data.Map as M
import Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as NE

So here’s a quality-of-life kind of idea:

import qualified Data.Text as T with (Text)
import qualified Data.Set as S with (Set)
import qualified Data.Map as M with (Map)
import qualified Data.List.NonEmpty as NE with (NonEmpty)

Maybe that’s better, I don’t know. It was just a lightning thought.
If anyone’s keen on it feel free to open a proposal.

6 Likes

Interesting approach. In one of my own secret lambda calculus, I added a Python-like using statement that automatically imports qualified, but also imports any data types who’s names start with the module’s last name. Eg:

using Data.Text
-- ^ is the same as
import Data.Text (Text, TextOptions, ...)
import qualified Data.Text as Text

I also had an experiment to allow the as keyword for renaming imported functions / constructs too, and not just modules, eg:

import Data.ByteString (ByteString, foo as fooByteString)
import Data.Text hiding (fooText as foo) -- It also works for hiding!

The idea for that is that it also helps reduce churn.

Eh I find this particular syntax confusing. with makes it seem like it’s adding these imports in the qualified namespace. I think what might work is something like

import Data.Text
  with (Text)
  with qualified as T

Personally, a bigger wishlist item is to force an explicit import list, which may be import Foo (..), which imports everything (borrowing syntax of importing all fields in a type). I’m not terribly bothered by repeating imports like you mentioned, especially with ImportQualifiedPost.

Just drop the qualified keyword

import Data.Text as T
import Data.Set as S
import Data.Map as M
import Data.List.NonEmpty as NE

This way you get all names both qualified and unqualified. Use an unqualified name if it’s unambiguous across all imported modules. Use a qualified name to resolve ambiguities, if any.

Already supported by Haskell. Always has been!

9 Likes

Indeed, well thought of :stuck_out_tongue:

Related to this, don’t forget that we have ImportQualifiedPost

import Data.Map (Map)
import Data.Map qualified as Map

and then no more ugly qualified getting itself in front of your module names!

5 Likes

I do wish libraries could insist “I am meant to be imported qualified as such” with unqualified being an exceptional option.

(Can’t believe i’m saying this) like Golang.

2 Likes

I’d suggest exposing rather than with, but otherwise I like this a lot.

I had a quick look at the ghc-proposals repos and couldn’t find this, do you happen to remember something that would be a good search term?

The latest proposal which allows this is the Local modules porposal by Richard Eisenberg.

It says it is an alternative to the Structured imports proposal, which may be what you’re talking about.

That’s not much of a problem. You’re probably thinking of list functions, what I do is this

import Data.List as L
1 Like

Or just put up with writing imports over two lines? Is it really worth introducing new syntax for this? Any code you write using that syntax will be incompatible with all previous compiler versions

https://osa1.net/posts/2020-01-22-no-small-syntax-extensions.html

2 Likes

Prelude, and all the local definitions.

1 Like

For reference, there’s already a Haskell preprocessor require that implements the desired syntax.

Instead of

import Data.Text (Text)
import qualified Data.Text as Text
import Data.ByteString (ByteString)
import qualified Data.ByteString as ByteString

You can write:

require Data.Text
require Data.ByteString

It has all the pros and cons of being an external compiler plugin instead of being a GHC feature.

7 Likes