# The Hazy Haskell Compiler

**URL:** <https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497>\
**Category:** Announcements\
**Created:** [January 7, 2026, 9:35am UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497 "2026-01-07T09:35:20Z")\
**Posts on this page:** 17\
**Page:** 2

<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 7, 2026, 7:39pm UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497/21 "2026-01-07T19:39:36Z")

</div>

Are you sure you can free an STRef when you leave the runST? You can hide an STRef in an existential (if you have those) and smuggle it out. You can’t do anything very interesting with it, but it is reachable.

---

<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 7, 2026, 7:40pm UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497/22 "2026-01-07T19:40:30Z")

</div>

I’m looking forward to running some code soon then.

---

<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 7, 2026, 8:49pm UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497/23 "2026-01-07T20:49:43Z")

</div>

The only thing you can with an `STRef` without the the state token is compare them for equality and it shouldn’t be an issue to compare dead pointers for equality if they never move. It’s impossible to compare dead pointer to live ones too because the `s` parameters have to be the same in the `Eq` instance. I am a bit worried that LLVM or some other C optimizer would freak out if it saw dead pointer comparisons though.

---

<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 9, 2026, 2:09pm UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497/24 "2026-01-09T14:09:21Z")

</div>

Small update. I implemented `putStrLn` (and `Text.pack`, `Text.putStrLn`) so now this example works.

```haskell
module Main where

type Peano :: *
data Peano = Zero | Succ Peano

length :: Peano -> String
length (Succ peano) = '+' : length peano
length Zero = ""

class Double a where
  double :: a -> a

instance Double Peano where
  double (Succ peano) = Succ (Succ (double peano))
  double Zero = Zero

quadruple :: (Double a) => a -> a
quadruple peano = double (double peano)

three, twelve :: Peano
three = Succ (Succ (Succ Zero))
twelve = quadruple three

main :: IO ()
main = putStrLn (length twelve)

```

Here’s is what the generated code looks like: [Peano.js - Pastebin.com](https://pastebin.com/p0s1YDFK)

Also, this is in already Readme, but let me reiterate the process of generating this.

```plaintext
$ cp -R runtime/ output
$ cabal run hazy -- -I library/runtime/ library/base -o output
Configuration is affected by the following files:
- cabal.project
$ cabal run hazy -- -I library/runtime/ -I library/base/ Peano.hs -o output
Configuration is affected by the following files:
- cabal.project
$ esbuild output/index.mjs --bundle --format=esm --platform=node --outfile=Peano.js

  Peano.js 4.6kb

Done in 13ms
$ node Peano.js
++++++++++++
$

```

---

<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:** [April 22, 2026, 3:55am UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497/25 "2026-04-22T03:55:10Z")

</div>

Today is my birthday and I’d figure I would celebrate by giving a progress report on Hazy.

Here’s a quick list of what I’ve done over these past 3 months

- Syntax sugar
  - Lambda expressions
  - Case expressions
  - Lambda case expressions
  - Let pattern binding
  - Integer literals
  - Floating point literals
  - Do notation
  - Right sections
  - Type annotation expressions

- Language features
  - Default methods
  - Monomorphic generalization
  - Newtypes
  - Strict fields

- Library / Tooling
  - A significant part of the Prelude has been implemented
  - A bare minimum package format for saving and loading sets of modules

I also have another new extension: `LevityPolymorphicFields`. You can now parameterize a field by it’s strictness.

```haskell
type List :: Levity -> Type -> Type
data List s a
  = Nil
  | Cons
      { head :: ~!s a,
        tail :: ~!s (List s a)
      }

```

Here `List Strict a` would be a fully strict list while `List Lazy a` would be a lazy one. I have more details in my readme.

Of course all of this isn’t enough for Hazy to be usable yet as there’s still some missing parts, but I am getting a lot closer.

---

<div class="post-metadata">

**Author:** ![Ambrose](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/ambrose/32/5672_2.png) [@Ambrose](https://discourse.haskell.org/u/Ambrose)\
**Post date:** [April 22, 2026, 6:06am UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497/26 "2026-04-22T06:06:20Z")

</div>

Why is the arrow naked here? I’d expect either a new arrow or some other syntax.

Naked, my System F brain sees no reason Levity affects other args here. I don’t like that. System F is the north star.

you seem to pass it along..but not in a meaningful. dependent way. You’re close (the extension is a good idea) but no cigar.

Highly recommend being brave enough to submit this to ghc-proposals`. That’ll ensure it passes bona fide muster.

---

<div class="post-metadata">

**Author:** ![chrisdone](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/chrisdone/32/1408_2.png) [@chrisdone](https://discourse.haskell.org/u/chrisdone)\
**Post date:** [April 22, 2026, 11:08am UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497/27 "2026-04-22T11:08:54Z")

</div>

I wonder, is the Levity attached to the arrow? In Coalton they attached \* to the arrow, like “Int \* Char → Int,” or “Int → Char \* Text”. I.e one can give multiple inputs and output multiple outputs for a function, but there isn’t a free-standing “a\*b”. I find this really neat in a strict language like Coalton. (PureScript has a less neat mechanism, but similar idea.)

---

<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:** [April 22, 2026, 1:04pm UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497/28 "2026-04-22T13:04:19Z")

</div>

> I wonder, is the Levity attached to the arrow?

Well, I did make this post: [Levity should be on the arrow, not on the kind](https://discourse.haskell.org/t/levity-should-be-on-the-arrow-not-on-the-kind/8949). So far I only implemented levity polymorphic fields and not levity polymorphic arrows, but it’s something I plan on doing eventually.

---

<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:** [April 22, 2026, 1:07pm UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497/29 "2026-04-22T13:07:05Z")

</div>

I also have a post on this: [Levity should be on the arrow, not on the kind - #35 by superstar64](https://discourse.haskell.org/t/levity-should-be-on-the-arrow-not-on-the-kind/8949/35). If you have levity on the arrow in system-f, then these levity polymorphic fields appear in scott encoding.

---

<div class="post-metadata">

**Author:** ![mchav](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/mchav/32/5143_2.png) [@mchav](https://discourse.haskell.org/u/mchav)\
**Post date:** [April 22, 2026, 2:11pm UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497/30 "2026-04-22T14:11:03Z")

</div>

Happy belated birthday!!!

---

<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:** [June 14, 2026, 3:35pm UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497/31 "2026-06-14T15:35:36Z")

</div>

Let me post another update. I’ve implemented the following:

- Binding groups
  - Global generalization (across mutually recursive modules too)
  - Local generalization

- List comprehensions
- Prefix negation
- Record updates

Hazy is now at the point where nearly all of Haskell 2010’s language features work. The main missing feature now is deriving. There are these deficiencies that I need to fix too:

- Contained type variables are not generalizated
- Constraints are limited to unique type class / type variable pairs
- Some missing builtin instances
- Other small issues

Lastly, these are the _language_ features. Almost none of Hazy’s base is implemented.

---

<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:** [September 26, 2026, 3:01am UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497/32 "2026-09-26T03:01:30Z")

</div>

Okay, another update. I’ve spent most of the last few months in refactoring hell so I haven’t done too much.

- Hazy now has preliminary support for `deriving`
- Hazy now implements `MultilineStrings`
- Hazy now has a `HygenicHiding` extension

The deriving support is very bare bones at the moment. I only support `deriving instance` at the moment because that’s easier to implement. I also only support deriving `Eq` at the moment. Still this is a start, adding other classes should be easy.

The `HygenicHiding` is a tiny extension that fixes what I consider to be a flaw in Haskell 2010. It makes it so `hiding` declarations don’t have top level constructors. This makes it symmetric with standard import lists. This wouldn’t hide anything for example: `import Prelude hiding (Left)`.

Lastly, I want to show off a fun parlor trick. This works as expected:

```haskell
equal :: forall a. Eq a => a -> a -> Bool
equal a b = Wrapper a == Wrapper b where
  newtype Wrapper = Wrapper a
  deriving instance Eq Wrapper

```

---

<div class="post-metadata">

**Author:** ![ApothecaLabs](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/apothecalabs/32/3215_2.png) [@ApothecaLabs](https://discourse.haskell.org/u/ApothecaLabs)\
**Post date:** [September 26, 2026, 3:17am UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497/33 "2026-09-26T03:17:37Z")

</div>

> HygenicHiding

From one compilateur to another, I too think that Haskell’s import system could use some love. Its often hard to put into terms precisely what it means, but I often wish for things like a `foo as bar` syntax in imports to alias conflicting names.

---

<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:** [September 26, 2026, 3:25am UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497/34 "2026-09-26T03:25:53Z")

</div>

> [@ApothecaLabs](#):
>
> I too think that Haskell’s import system could use some love.

Have you seen the import proposals? [Several proposals for Import syntax](https://discourse.haskell.org/t/several-proposals-for-import-syntax/14504). I’m kinda like `exposing` (with my addendum: [import-exposing-impqual by akhra · Pull Request #763 · ghc-proposals/ghc-proposals · GitHub](https://github.com/ghc-proposals/ghc-proposals/pull/763#issuecomment-5284903499)).

> [@ApothecaLabs](#):
>
> but I often wish for things like a `foo as bar` syntax in imports to alias conflicting names.

If there were implemented, there would have to be some questions regarding fields and instance definitions that have to be answered. Consider something like this:

```haskell
import Point (Point, x as abc, y as xyz)
p1 = Point { abc = 1, xyz = 2 }
p2 = Point { x = 1, y = 2 }

```

Here, `p1` would be legal if selectors are their own standalone thing while `p2` would be legal if fields are attached to constructors. Haskell 2010 leans more toward `p1`, while Hazy (with `ConstructorsFields`) would use `p2`. A similar problem would happen with instance definitions too.

---

<div class="post-metadata">

**Author:** ![ApothecaLabs](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/apothecalabs/32/3215_2.png) [@ApothecaLabs](https://discourse.haskell.org/u/ApothecaLabs)\
**Post date:** [September 26, 2026, 5:23am UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497/35 "2026-09-26T05:23:09Z")

</div>

Mmmm I have seen them now, haven’t I? Certainly food for thought.

> [@superstar64](#):
>
> Here, `p1` would be legal if selectors are their own standalone thing while `p2` would be legal if fields are attached to constructors. Haskell 2010 leans more toward `p1`, while Hazy (with `ConstructorsFields`) would use `p2`. A similar problem would happen with instance definitions too.

Turning off field selectors aside (I usually have them turned disabled), my gut reaction is to ask “what do you do upon any other naming collision?” which of course is always a choice and not a mathematical given. I would personally probably throw a potential collision error, but allow shadowing unless they make it fatal.

My reasoning is: It’s an import, the consumer has control and is choosing how to read the interface. This is symmetric to an export being when how the producer has control and chooses to expose it.

---

<div class="post-metadata">

**Author:** ![simonmar](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/simonmar/32/5058_2.png) [@simonmar](https://discourse.haskell.org/u/simonmar)\
**Post date:** [September 28, 2026, 11:18am UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497/36 "2026-09-28T11:18:10Z")

</div>

> [@ApothecaLabs](#):
>
> I often wish for things like a `foo as bar` syntax in imports to alias conflicting names.

Fun fact: Haskell 1.2 had this feature, and it was [removed in Haskell 1.3](https://www.haskell.org/definition/from12to13.html#qualnames). According to the rationale: “Instead of using renaming to resolve name clashes, Haskell 1.3 uses qualified names.”

I don’t remember much about the discussion at the time, but I do remember that renaming was a pain to implement and pretty complex to specify in the report too. Given that there are alternatives, my guess is it’s not a feature worth its weight.

---

<div class="post-metadata">

**Author:** ![ApothecaLabs](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/apothecalabs/32/3215_2.png) [@ApothecaLabs](https://discourse.haskell.org/u/ApothecaLabs)\
**Post date:** [September 28, 2026, 4:49pm UTC](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497/37 "2026-09-28T16:49:32Z")

</div>

Intriguing! I would like to investigate this more to understand why

I found it relatively easy to implement as a renaming pass in my toy compiler a few years ago, basically giving the module import the same name shadowing powers as a let-in declaration (if I remember properly, that’s precisely how I did it). To be fair, _that was a toy compiler_ so I didn’t have the same bulk to deal with as GHC, so maybe I didn’t run into the same problems because I didn’t reach a large enough scale.

On the other hand, I am fully intending on bringing that feature back in my SECD machine, and it has a significant advantage in that I’m designing the compiler for first-class content-addressability (memory, cryptography, recursion-schemes, oh my), which I hope will trivialize renaming.

* * *

_does his investigating_

Aha! So in 1.2, there were no qualified names, and module import renaming was the only way to deal with conflicts. In 1.3, qualified names were added, and so module import renaming was removed, likely due to a mix of wanting to shepherd people into using the new qualified names, and not wanting to support multiple mechanisms at the time. That seems sensible.

Qualified names being equally powerful, there was a decision to prefer one over the other, which made sense at the time because Haskell was much much smaller and the overhead of doing so was far more meaningful. I think it was the right decision at the time.

However, I’d argue that the decision was more of an economic decision of “is it worth it to support both?”, rather than an academic “Is it possible to support both?”. It wasn’t worth the weight back then - but what about now?

For reference, `FunctionalDependencies` and `TypeFamilies` are similarly-competing features with significant overlap, carrying far more implementation weight - yet we choose to support both. I’m going to implement this in my compiler, and see if maybe the juice is worth the squeeze now.

[Previous page](https://discourse.haskell.org/t/the-hazy-haskell-compiler/13497.md?page=1)
