# Draft GHC Proposal: ScopedImports

**URL:** https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568
**Category:** Links
**Created:** [January 19, 2026, 11:24am UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568 "2026-01-19T11:24:46Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![rgover](https://avatars.discourse-cdn.com/v4/letter/r/6f9a4e/32.png) [@rgover](https://discourse.haskell.org/u/rgover)
#### Post date: [January 19, 2026, 11:24am UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/1 "2026-01-19T11:24:46Z")

</div>

Would appreciate any feedback on this new proposal. Thank you in advance.

[Discussion](https://github.com/ghc-proposals/ghc-proposals/pull/740)

---

<div class="post-metadata">

### Author: ![ozkutuk](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/ozkutuk/32/3992_2.png) [@ozkutuk](https://discourse.haskell.org/u/ozkutuk)
#### Post date: [January 19, 2026, 12:24pm UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/2 "2026-01-19T12:24:57Z")

</div>

Forgive me if this is explained in the proposal as I have only took a very brief look, but would this be subsumed by the [local modules proposal](https://github.com/ghc-proposals/ghc-proposals/pull/283)?

---

<div class="post-metadata">

### Author: ![danidiaz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/danidiaz/32/92_2.png) [@danidiaz](https://discourse.haskell.org/u/danidiaz)
#### Post date: [January 19, 2026, 8:53pm UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/3 "2026-01-19T20:53:41Z")

</div>

If a module `A` defines an instance, and module `B` does an scoped import of `A` for some function, would the instance defined in `A` be available in the entirety of `B`?

---

<div class="post-metadata">

### Author: ![rgover](https://avatars.discourse-cdn.com/v4/letter/r/6f9a4e/32.png) [@rgover](https://discourse.haskell.org/u/rgover)
#### Post date: [January 19, 2026, 11:29pm UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/4 "2026-01-19T23:29:28Z")

</div>

Yes it is subsumed by LocalModules.

[My thoughts on it so far](https://github.com/ghc-proposals/ghc-proposals/pull/740#issuecomment-3770429743)

---

<div class="post-metadata">

### Author: ![rgover](https://avatars.discourse-cdn.com/v4/letter/r/6f9a4e/32.png) [@rgover](https://discourse.haskell.org/u/rgover)
#### Post date: [January 20, 2026, 12:12am UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/5 "2026-01-20T00:12:04Z")

</div>

[Yes, this is correct.](https://github.com/ghc-proposals/ghc-proposals/pull/740#issuecomment-3770500953)

---

<div class="post-metadata">

### Author: ![rgover](https://avatars.discourse-cdn.com/v4/letter/r/6f9a4e/32.png) [@rgover](https://discourse.haskell.org/u/rgover)
#### Post date: [January 20, 2026, 11:43pm UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/6 "2026-01-20T23:43:34Z")

</div>

Updated the proposal to explicitly state that `ScopedImports` does not change how instance/module resolution works already and doesn’t affect parts of the grammar like class declarations, instance declarations, pattern synoynms, or any other top-level declarations. Syntactically, `ScopedImports` only affects where import declarations are allowed. These special import declarations are called scoped import declarations.

Also, updated the proposal based on a commenters idea to force users to explicitly import at least the instances of the module they are using in a scoped import declaration. This simplifies the proposal implementation and aligns with the instance semantics of Haskell.

Hopefully, this clears up some confusion. Still a lot of different interactions to think about and add to the proposal.

---

<div class="post-metadata">

### Author: ![rgover](https://avatars.discourse-cdn.com/v4/letter/r/6f9a4e/32.png) [@rgover](https://discourse.haskell.org/u/rgover)
#### Post date: [January 20, 2026, 11:55pm UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/7 "2026-01-20T23:55:56Z")

</div>

Also added a section on how this is different from the LocalModules proposal.

---

<div class="post-metadata">

### Author: ![rgover](https://avatars.discourse-cdn.com/v4/letter/r/6f9a4e/32.png) [@rgover](https://discourse.haskell.org/u/rgover)
#### Post date: [January 26, 2026, 8:43pm UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/8 "2026-01-26T20:43:57Z")

</div>

Updated the proposal and renamed the extension from `ScopedImports` to `LocalImports`

> <https://github.com/ghc-proposals/ghc-proposals/pull/740#issuecomment-3801602774>
>
> Thank you all for the feedback so far.
> 
> \- Changed the name of the extension fr…om \`ScopedImports\` to \`LocalImports\`. It's shorter and is slightly related to \`LocalModules\` which seems to be pretty popular
> \- I addressed some of the concerns in the proposal about instance resolution
> \- Added an example for QualifiedStrings
> \- Added some more examples of what would be invalid and what would be valid
> \- Still have to think some more about records, @danidiaz I thought it would suffice to prevent record field labels from being imported in local import declarations. The idea was to force the user to put any record field label imports at the top-level. This is what the current iteration of the proposal describes. But now I'm realizing that if \`FieldSelectors\` is on in the source module, then record field labels can be brought into scope with a regular open import...Or am I wrong about this? I have to look into how \`LocalImports\` interacts with record labels more...I'm considering only allowing import declarations that explicitly name the imports they're bringing into scope. For example,
> 
> \`\`\`haskell
> f = let import M in ... -- Brings record labels into scope if FieldSelectors is on? Not good
> g = let import M (..) in ... -- Could bring in whatever. Not good
> h = let import M (x, y, z) in ... -- This is allowed. If a record field label is mentioned, compile-time error
> \`\`\`
>   
> Still trying to figure it out but would appreciate any opinions. Should we make it such that only explicit local import declarations are allowed? Once records are figured out, will add a Records part to the Effects and Interactions section.
>   
> \- I have some more references to LocalModules, including some of the similar motivations, but have to think more about how they would interact and what the transition from LocalImports to LocalModules would look like. Does implementing LocalImports now clash with LocalModules in the future? I don't know.
> \- Have to think more about Template Haskell interaction. I don't think there's anything crazy here, but haven't thought about it deeply yet.
> \- New \`-Wqualifier-shadowing\` warning. Explained in the proposal.
> 
> The proposal is still rough. Will definitely need another pass or two to clean up wording, grammar, punctuation, etc.

---

<div class="post-metadata">

### Author: ![augustss](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/augustss/32/887_2.png) [@augustss](https://discourse.haskell.org/u/augustss)
#### Post date: [January 27, 2026, 11:57am UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/9 "2026-01-27T11:57:37Z")

</div>

If we are going to allow `import` locally in a `let`/`where` I think we should also allow `import` anywhere in a module. The only(?) current argument for keeping them just at the top is that then you can easily find them. But if we start allowing local `import` then they will be all over the place, so we might as well allow it on the top level as well. I think that will also simplify the grammar.

---

<div class="post-metadata">

### Author: ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)
#### Post date: [January 27, 2026, 12:17pm UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/10 "2026-01-27T12:17:36Z")

</div>

The current proposal still requires a top level import of any module that is also imported locally. The proposed local imports are mainly a way to change the visibility of imported names.

---

<div class="post-metadata">

### Author: ![rgover](https://avatars.discourse-cdn.com/v4/letter/r/6f9a4e/32.png) [@rgover](https://discourse.haskell.org/u/rgover)
#### Post date: [January 27, 2026, 6:19pm UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/11 "2026-01-27T18:19:03Z")

</div>

One of the reasons for still requiring top-level imports at the top of the module is so the `Downsweep.hs` part of the GHC pipeline remains unchanged. `Downsweep.hs` only has to parse the top of the module to build a module graph. If we allow users to place top-level imports anywhere within the module file, `Downsweep.hs` will need to be modified to parse the entire file for import declarations.

---

<div class="post-metadata">

### Author: ![augustss](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/augustss/32/887_2.png) [@augustss](https://discourse.haskell.org/u/augustss)
#### Post date: [January 27, 2026, 7:55pm UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/12 "2026-01-27T19:55:39Z")

</div>

The fact that current GHC needs some small modification to handle imports anywhere is a very weak reason. The entire module has to be parsed sooner or later, might as well do it sooner. We should decide on what’s good for the language, not the implementation. There’s a good argument for keeping imports at the top for readability, so I’m not going to argue for imports scattered everywhere. (MicroHs allows imports anywhere, because it was simpler to allow it.)

---

<div class="post-metadata">

### Author: ![rgover](https://avatars.discourse-cdn.com/v4/letter/r/6f9a4e/32.png) [@rgover](https://discourse.haskell.org/u/rgover)
#### Post date: [January 28, 2026, 7:21pm UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/13 "2026-01-28T19:21:30Z")

</div>

> [@augustss](#):
>
> The fact that current GHC needs some small modification to handle imports anywhere is a very weak reason. The entire module has to be parsed sooner or later, might as well do it sooner. We should decide on what’s good for the language, not the implementation.

Good point. The original proposal didn’t require top-level imports at all. The downsweep algorithm can be modified so that it looks at every expression in the module for import declarations (even more complex than just looking at top-level decls). Some concerns from the community were:

- Additional complexity

- Concerns about tooling (like stack’s script command)

- Instance scope isn’t explicit. For example:

And I thought increased compile times were a negative too. Probably not worth thinking about. Any implementation we go with will probably increases compile times for modules that use the extension by a negligible amount.

> [@augustss](#):
>
> We should decide on what’s good for the language, not the implementation.

I agree.

This brings me back to the original iteration of the proposal. Let’s not require top-level imports at all, unless you want to bring a name or module qualifier into the global scope. Tooling and such will probably be affected either way. As long as the language is improved.

> [@augustss](#):
>
> There’s a good argument for keeping imports at the top for readability, so I’m not going to argue for imports scattered everywhere.

I don’t know about this. I guess it’s nice to know that whatever module you open, the imports will be at the top. This is consistent. On the other hand, it’s kind of annoying to have to go to the top of a module in case you need to remember what imports are in scope or what qualifiers represent what modules. If you have good working memory, this probably doesn’t affect you.

When writing code at the bottom of the file you may think “I need this import!”, then you go back to the top of the module to add the import, and then go back down to where you were writing code. Then you may think “I actually don’t need this import!”, then you have to go back to the top and back down. This could be a skill issue. People who know their IDE well aren’t affected by this.

---

<div class="post-metadata">

### Author: ![superstar64](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/superstar64/32/5308_2.png) [@superstar64](https://discourse.haskell.org/u/superstar64)
#### Post date: [January 28, 2026, 9:37pm UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/14 "2026-01-28T21:37:07Z")

</div>

I just feel I should say that [Hazy](https://github.com/Superstar64/Hazy)’s import system is one of the things I’m quite proud of. Not only do I fully support local imports without the top level import requirement, but I also have proper recursive modules without hs-boot files and I even have lazy importing. If you have an import without wildcards where you don’t use the imports, the module never even gets opened.

Here is some code actual code from my tests:

```haskell
module Sloppy where

import Nonexistent (unused)

value, value2 :: ()
value = ()
value2 = value

```

A big reason why I’m able to do this is by not supporting orphan instances whatsoever. If orphan instances don’t exist, then by definition if a class and type are in scope, then all it’s instances are too. The problem of scoped instances just goes away. Orphan instances really shouldn’t be allowed.

---

<div class="post-metadata">

### Author: ![rgover](https://avatars.discourse-cdn.com/v4/letter/r/6f9a4e/32.png) [@rgover](https://discourse.haskell.org/u/rgover)
#### Post date: [January 28, 2026, 10:06pm UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/15 "2026-01-28T22:06:33Z")

</div>

> [@superstar64](#):
>
> A big reason why I’m able to do this is by not supporting orphan instances whatsoever. If orphan instances don’t exist, then by definition if a class and type are in scope, then all it’s instances are too. The problem of scoped instances just goes away. Orphan instances really shouldn’t be allowed.

This reminds me I still have to think about the interaction between local imports and orphan instances for this extension. Hopefully it’s not a blocker.

That’s cool that Hazy Haskell doesn’t have them!

---

<div class="post-metadata">

### Author: ![jeffstyr](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jeffstyr/32/2409_2.png) [@jeffstyr](https://discourse.haskell.org/u/jeffstyr)
#### Post date: [February 1, 2026, 2:07am UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/16 "2026-02-01T02:07:19Z")

</div>

> [@superstar64](#):
>
> Orphan instances really shouldn’t be allowed.

So if someone writes a serialization library like `aeson` and you want to use it for some data type in an existing library, then you are just stuck, right?

---

<div class="post-metadata">

### Author: ![superstar64](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/superstar64/32/5308_2.png) [@superstar64](https://discourse.haskell.org/u/superstar64)
#### Post date: [February 1, 2026, 2:26am UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/17 "2026-02-01T02:26:51Z")

</div>

Well you could use newtypes wrappers. Your not stuck, just massively annoyed.

---

<div class="post-metadata">

### Author: ![evincar](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/evincar/32/3594_2.png) [@evincar](https://discourse.haskell.org/u/evincar)
#### Post date: [February 2, 2026, 12:06am UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/18 "2026-02-02T00:06:04Z")

</div>

I have a few small arguments _against_ keeping imports strictly at the top.

1. It makes generating Haskell code harder, because you have to aggregate imports separately from the things that use them.

2. Making dependency analysis slightly easier isn’t a compelling reason to hold back improvements in the usability of the module system. If that’s really affecting performance, I’d rather have a separate way to spell out dependencies.

3. It’s frankly just silly that “literate” Haskell blog posts always start with something like “Please ignore these imports.” Imports are like entries in an index or bibliography, which usually go either at the end of the text, or near the text that cites them.

I think local imports in `let`/`where` are a great idea. For a section of a file with a group of bindings that share common imports, it’d also be nice to allow top-level imports closer to their use sites, because it is a pain for me to split a small project into several files while I’m prototyping.

Although if you also wanted to keep top-level imports from clogging up the scope of the whole module, it’d go better with top-level `let {…} in {…}`.

```haskell
module Language
  (
    -- * AST
    Program,
    Statement,
    Expression,

    -- * Parsing
    lex,
    parse,

    -- * Evaluation
    run,
    eval,
  ) where

-- | AST

let … in
  data Program where …
  data Statement where …
  data Expression where …

-- | Parsing

let import Text.Megaparsec in
  lex = …
  parse = …
  program = …
  statement = …
  expression = …

-- | Evaluation

let import Data.IORef in
  run = …
  eval = …

```

---

<div class="post-metadata">

### Author: ![rmgaray](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/rmgaray/32/5139_2.png) [@rmgaray](https://discourse.haskell.org/u/rmgaray)
#### Post date: [February 2, 2026, 11:47am UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/19 "2026-02-02T11:47:12Z")

</div>

I am strongly in favour of this proposal 👍 .

While I like the idea of having all the imports at the top - because it helps with some refactorings - for modules where all imports are spelled explicitly, the top of the file becomes unwieldy very fast. I often need to scroll the first screen to start seeing the actual code. I would welcome having imports closer to where they are used.

I would use this feature mostly to be able to do scoped unqualified imports for functions that need to use a specific library. An example would be something like:

```haskell
processText = ...
    where import Data.Text

```

which is much nicer than putting `import Data.Text qualified as T` at the top in a module that may have little to do with processing text.

But I think having to put imports **both** at the top and locally where the imported identifiers are used would add a lot of redundancy and friction to the feature. You lose brevity, which is (in my opinion) the most appealing things about this proposal.

---

<div class="post-metadata">

### Author: ![AndreasPK](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/andreaspk/32/2654_2.png) [@AndreasPK](https://discourse.haskell.org/u/AndreasPK)
#### Post date: [February 2, 2026, 5:56pm UTC](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568/20 "2026-02-02T17:56:28Z")

</div>

I like imports at the top.  
I find it can give an immediate impression of which APIs code uses and where in the program structure it sits.

Python is the only language I know and somewhat regularly use that allows imports “anywhere”. While I have used local imports in python in throwaway scripts and the like I always found putting them anywhere but the top of the file is more likely than not to make a mess of things in the long run. So I think it’s generally a bad idea to use them only locally. To the point where I really couldn’t say if making top level imports optional would actually be better for the language.

I wonder what other people with experience in languages that actually allow local imports think of this.

On the other side people write throw-away scripts or small programms in Haskell too and for that local imports would be nice to have. After all it should be easy to enforce required top level imports via HLint or similar for most projects where they are deemed inappropriate.

So while I don’t think very highly of local imports without a corresponding top level import, maybe they do have a place in the language.

[Next page](https://discourse.haskell.org/t/draft-ghc-proposal-scopedimports/13568.md?page=2)
