Modularity in Instance Chains?

In the official docs on Instance Chains an example is given for a typeclass “MyShow”.

But what I don’t understand is how does one add an instance to that class in another module than MyShow is defined in?

In the dissertation of the original creators of Instance Chains there’s a way to do it, but it relies on “if” constraint clauses that Purescript explicitly doesn’t support.

And in Haskell, more specific instances are used before less specific instances, so there’s no conflict between some other instance of MyShow to a type and “showA” in Haskell.

If there’s not a way to do it, I can see at least three possibilities:

• Add constraints like in the original idea of Instance Chains (the most complicated one to implement probably, and would make the code that used it much more verbose, requiring a second typeclass just for “showA”)

• Perhaps merge Haskell-style OverridableInstances with Instance Chains: if one instance is more specific (eg, an instance of a concrete Type) and the other less specific (eg, a parameter/default/catch-all instance) then the more specific one is used, and if they’re the same (or close) specificity, then the ordering given in instance chains is used.

• Consider instance chains to be providing a partial ordering on instances, so someone could add, say, “else instance showA” reusing it after an instance of MyShow to their type in another module and it conveys an explicit ordering between their type’s instance and showA (the default instance).
(allowing the reuse of instance names like “showA” here could be nice)

And an OverlappingInstances error would only be given if the partial ordering was absent or inconsistent.

• (or a combination of the last two might be nice, with it only giving an error if two instances are of equal specificity and there’s no valid explicit partial ordering between them)

1 Like

But what I don’t understand is how does one add an instance to that class in another module than MyShow is defined in?

You might find this disappointing, unfortunately; the answer is that you don’t. Instance chains are inherently anti-modular. The problem arises when you try to extend a chain in two different places: how do we know what order to do them in? If you just emit an error in that case, then adding a new dependency can break your project, which sucks, especially if you don’t really care about that type class.

Generally we are quite reluctant to extend the type classes feature in this sort of way; see e.g. https://github.com/purescript/purescript/issues/3596 and https://github.com/purescript/purescript/issues/3120 for similar-ish requests.

3 Likes

Okay, thanks for responding so quickly!

I suppose since I gave a few examples of how to overcome the problem you mentioned, that this is just how the Purescript community wants Purescript to be? No problem; we all have different things we want to see in languages.

And no worries, it’s not disappointing to me since I haven’t invested in Purescript yet and now I’ll probably just keep using Haskell or Java instead, so no harm done.

2 Likes

I don’t think I can speak for the community as a whole, and I think I lie further towards the “avoid using type classes” end of the spectrum than most people: my preferred solution if I’m running into these limitations is to rework my architecture so that it doesn’t rely on type classes so much.

I know you’re not the only person who has wanted overlapping instances or more flexible instance chains, I just feel that attempting to implement them is quite risky and likely more expensive (in terms of maintainer attention) than it’s worth. The issue with type system extensions is that they can easily end up appearing to work at first and then turning out to have serious issues and requiring as much work again (or more) to get them working properly; we’ve just seen this with Coercible, for instance. I think GHC style overlapping instances (where you have to mark one of the instances involved as either overlapping or overlappable) could potentially happen some day, but it’s relatively low on my personal list of priorities.

1 Like

I think the idea of adding the constraints to instance chains has been mentioned a few times, it was just skipped at first as it wasn’t strictly required to have instance chains be a useful feature for certain cases (mostly where the chain being closed doesn’t matter, as it’s for inject-style trickery). Just nobody has had the time or inclination to actually do it since, and there are perhaps some discussion points around what problems the constraints would solve vs those that would still be present, etc.

Typeclasses as they are in PS aren’t really modular even without instance chains, since we favour global coherence instead, which is inherently anti-modular. There’s some old discussion here about making typeclasses modular, but that’s just historical interesting reading really since we haven’t considered it at all for a long time (and realistically, won’t be returning to in PS as it is these days).

1 Like