# Extra bindings in instance declarations

**URL:** <https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846>\
**Category:** Uncategorized\
**Created:** [August 28, 2025, 11:22am UTC](https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846 "2025-08-28T11:22:24Z")\
**Posts on this page:** 16\
**Page:** 1

<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:** [August 28, 2025, 11:22am UTC](https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846/1 "2025-08-28T11:22:24Z")

</div>

I have often wished that an instance declaration could have extra bindings, i.e., bindings that are not the class methods. Today you would have to make such bindings global. I dislike making bindings have a larger scope than they have to, since that impedes understanding. It would be a bad idea to allow the extra bindings with no indication that they are extra, since that would make a misspelled method name not be an error. So there should be some syntax that indicates what is extra.

Example:

```haskell
class C a where
  m1 :: a -> a
  m2 :: a -> a

instance C Int where
  m1 x = f x + 1 -- method binding
  m2 x = f x + 2 -- method binding
 where
  f x = x * x -- extra binding

```

This is just a suggested syntax, but it seems to make sense.

Is there anyone else who has missed this feature? I’ve implemented in MicroHs. It was trivial.

---

<div class="post-metadata">

**Author:** ![Kleidukos](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/kleidukos/32/1213_2.png) [@Kleidukos](https://discourse.haskell.org/u/Kleidukos)\
**Post date:** [August 28, 2025, 12:19pm UTC](https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846/2 "2025-08-28T12:19:30Z")

</div>

There are times where I just provide the implementation as a separate function that the end-user can call without going through the indirection of the typeclass constraint. Of course it’s not very practical for classes like `Num` with lots of operators, for classes with one function it usually provides a nicer interface with we try to keep most domain-related functions monomorphic.

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [August 28, 2025, 12:58pm UTC](https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846/3 "2025-08-28T12:58:09Z")

</div>

Yes, although I don’t like the idea of making type classes even _more_ of a special thing than they already are. I’m considering only ever defining type classes in the future that have a single method `<Class>Impl`, like this:

```haskell
class C a where
  cImpl :: CImpl a

data CImpl a = MkCImpl {
  m1Impl :: a -> a,
  m2Impl :: a -> a
}

instance C Int where
  cImpl =
    let f x = x * x
    in MkCImpl {
         m1Impl = f x + 1,
         m2Impl = f x + 1
       }

m1 :: C a => a -> a
m1 = m1Impl cImpl

m2 :: C a => a -> a
m2 = m2Impl cImpl

```

This is much more uniform and allows us to use any normal Haskell machinery to implement type classes and instances. For example you can do stuff like

```haskell
fromF :: (a -> a) -> CImpl a
fromF f = MkCImpl { m1Impl = f x + 1, m2Impl = f x + 2 }

instance C Integer where
  cImpl = fromF (\x -> x * x * x)

```

---

<div class="post-metadata">

**Author:** ![Lysxia](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/lysxia/32/2916_2.png) [@Lysxia](https://discourse.haskell.org/u/Lysxia)\
**Post date:** [August 28, 2025, 1:46pm UTC](https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846/4 "2025-08-28T13:46:06Z")

</div>

> Yes, although I don’t like the idea of making type classes even _more_ of a special thing than they already are.

There is another way to look at this that makes type classes _less_ of a special thing. A type class conflates two things: it is both a data type (of dictionaries) and a constraint. The problem is that the language hides the “data type” side of type classes, with `instance` being an ad hoc syntax for defining dictionaries. If type classes were explicitly identified as data types, then a dictionary could be defined as just a value, and we could just use `where` clauses as usual.

Using single-method classes is one way to identify a type class and a data type. There is also this old GHC proposal for another approach: [Allow direct access to underlying concrete class dictionaries by tysonzero · Pull Request #324 · ghc-proposals/ghc-proposals · GitHub](https://github.com/ghc-proposals/ghc-proposals/pull/324)

---

<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:** [August 28, 2025, 4:34pm UTC](https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846/5 "2025-08-28T16:34:48Z")

</div>

In reply to the original post by @augustss:

Yep, I’ve wanted this on numerous occasions. Would be a nice convenience to have.

---

<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:** [August 28, 2025, 5:01pm UTC](https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846/6 "2025-08-28T17:01:28Z")

</div>

It doesn’t have to be a special case for instance declarations. We can take a cue from Luca Cardelli’s version of ML from early 80s. Instead if having bindings simply be a list of bindings we could also allow

```haskell
let
  bindingsL
in
  bindingsG

```

Which would make `bindingsL` and `bindingsG` visible to each other, but only `bindingsG`would be visible outside. Similarely, we should have

```haskell
  bindingsG
where
  bindingsL

```

Example:

```haskell
let
  let sqr x = x*x
  in f x = sqr x + 1
      g x = sqr x - 1
in. ... use f and g ... -- sqr not visible here

```

I’ve wished for this feature now and then as well. It would make modules without an export list nicer as well.

```haskell
module M where. -- only exports f, g
f x = sqr x + 1
g x = sqr x - 1
where
sqr x = x * x

```

With this my proposed extension to `instance` would just be a use case.

---

<div class="post-metadata">

**Author:** ![gtf21](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/gtf21/32/5210_2.png) [@gtf21](https://discourse.haskell.org/u/gtf21)\
**Post date:** [August 30, 2025, 8:51pm UTC](https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846/7 "2025-08-30T20:51:38Z")

</div>

I frequently miss this for instance declarations, so end up with the helper function being in the module scope which annoys me but am resigned to it.

---

<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:** [August 31, 2025, 1:31pm UTC](https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846/8 "2025-08-31T13:31:57Z")

</div>

Maybe I’ll make a GHC proposal.

---

<div class="post-metadata">

**Author:** ![lortabac](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/lortabac/32/4597_2.png) [@lortabac](https://discourse.haskell.org/u/lortabac)\
**Post date:** [September 1, 2025, 8:48am UTC](https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846/9 "2025-09-01T08:48:14Z")

</div>

I apologize in advance for being a killjoy.

Given how bloated GHC already is, I think (for everybody’s sanity) the usefulness threshold for proposed features should become higher and higher, especially regarding syntactic extensions. And I don’t think this proposal meets my own subjective threshold.

These extra bindings are a nice convenience, but ultimately they are not substantially different from a toplevel definition that is not exported. Yes, visibility restrictions cannot be more granular than the file level. But that’s a design choice that Haskell made a long time ago, and keeps at least this part of the language simple.

---

<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:** [September 1, 2025, 9:24am UTC](https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846/10 "2025-09-01T09:24:48Z")

</div>

I’m used to having my Haskell proposals rejected, just to show up later on. 😀 (E g., tuple sections, overloaded strings, record wildcards, …)

---

<div class="post-metadata">

**Author:** ![andreasabel](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/andreasabel/32/1764_2.png) [@andreasabel](https://discourse.haskell.org/u/andreasabel)\
**Post date:** [September 1, 2025, 9:48am UTC](https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846/11 "2025-09-01T09:48:54Z")

</div>

ML has `local decls1 in decls2 end` to limit the scope of `decls1` to `decls2`. I think such a construct would be a principled solution to limiting scope. In particular, I’d like to also have local datatypes.

However, the problem with local definitions show up when you want to have checked documentation and tests for them. So, in practice, one simply makes `.Internal` modules that export everything (for testing and documentation) and then some official modules that export the actual API.

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [September 1, 2025, 4:13pm UTC](https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846/12 "2025-09-01T16:13:40Z")

</div>

> [@lortabac](#):
>
> I think (for everybody’s sanity) the usefulness threshold for proposed features should become higher and higher, especially regarding syntactic extensions.

I agree. Small syntax extensions can make things much nice for Haskell experts whilst raising a barrier for non-experts. Ömer Sinan Ağacan has written an article I like about this at [osa1 - Some arguments against small syntax extensions in GHC](https://osa1.net/posts/2020-01-22-no-small-syntax-extensions.html)

I would make an exception for syntax extensions that remove irrelevant exceptions, unify disparate concepts, or are otherwise an overall simplification. I haven’t looked at the proposal in this thread closely enough to know whether it satisfies that criterion.

---

<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:** [September 1, 2025, 11:32pm UTC](https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846/13 "2025-09-01T23:32:49Z")

</div>

I think extending `let binds in expr` to also allow `let binds in binds` is a pretty natural extension. But I will not argue strongly for it.

---

<div class="post-metadata">

**Author:** ![exaexa](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/exaexa/32/4753_2.png) [@exaexa](https://discourse.haskell.org/u/exaexa)\
**Post date:** [September 2, 2025, 1:41pm UTC](https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846/14 "2025-09-02T13:41:29Z")

</div>

+1 here; I’d certainly use this.

On a minor note I’d prefer to _not_ have the extra `where` keyword there as a separator; in particular it reads better to me to have helper functions on the top (again as with `let..in`). I’m not sure if a separator is needed for technical reasons (I see no such one now though).

```haskell
instance C Int where
  f x = x * x
  m1 x = f x + 1
  m2 x = f x + 2

```

(I guess this might prevent the future GHC proposal from breaking the parser.)

* * *

Extra shower thought: let’s generalize the `where` and `let` in instances and classes:

```haskell
let m1 :: a -> a
    m2 :: a -> a
 in class C a

```

```haskell
let f x = x * x
 in instance C Int where
      m1 x = ...

```

---

<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:** [September 2, 2025, 1:44pm UTC](https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846/15 "2025-09-02T13:44:35Z")

</div>

One thing I’ve thought about in the past is to just allow any normal bindings in the `where` block of an instance declaration. Then you can bundle your methods like this:

```haskell
class C a where
  m1 :: a -> a
  m2 :: a -> a

data CImpl a = MkCImpl {
  m1 :: a -> a,
  m2 :: a -> a
}

fromF :: (a -> a) -> CImpl a
fromF f = MkCImpl { m1 = f x + 1, m2 = f x + 2 }

instance C Integer where
  cube x = x * x * x -- cube isn't a method, so it is ignored
  MkCImpl {..} = fromF cube -- defines the m1 and m2 methods

```

This is a simplification in some sense: it unifies the behaviour of `instance ... where ...` with `module ... where ...` and `... = ... where ...`.

---

<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:** [September 29, 2025, 8:54pm UTC](https://discourse.haskell.org/t/extra-bindings-in-instance-declarations/12846/16 "2025-09-29T20:54:57Z")

</div>

Fun: I suppose you could imagine if Haskell had instead extended `data`, for defining classes.

```haskell
data class <signature> = <record-def>
data class C a = C {

  m1 :: a → a,

  …

}

```

And then instance became an extension of top-level patbind:

```haskell
instance <head> = <rhss>
instance C Integer = C { m1 = …, … } where cube x = …

```
