The Haskell Unfolder Episode 28: Type Families and Overlapping Instances

(Will be streamed today, 2024-07-03, 1830 UTC on YouTube.)

8 Likes

Thanks Andres, Edsko

Have we actually won anything? [at about 18:30]

I think your type family solution – that also needs a Proxy for some reason – is intolerably cluttered. So no, “we”'ve lost.

  • Firstly, there’s no precise specification for Overlapping instances (for classes – for TFs they’re not allowed).
  • GHC’s behaviour is different to Hugs; and frankly I find GHC’s an unfortunate mess: you can get instances apparently accepted but unusable. And it’s more of a mess under separate compilation.
  • OTOH Hugs rejects sets of instances that would be useful.
  • There’s a proposal to address this whole topic. And you have to consider it hand-in-hand with Functional Dependencies.

So I can write this

class Add a b c  | a b -> c, a c -> b, b c -> a  where ..    -- three FunDeps

instance                Add Z      b b  where ...            -- base case

instance Add a' b c' => Add (S a') b (S c')   | b /~ (S c')  -- apartness guard
                         where ...      -- recursive case

I’m not saying it’s universally better. I believe it’s a technique worth knowing, because sometimes it helps and unlocks new design options. But yes, it comes at a price.

It also is a technique that works right now and is applicable quite universally, as opposed to various proposals that have been made over the years to fix this problem in some other way, at least going back to Instance Chains by Garret Morris and Mark Jones in 2010, http://web.cecs.pdx.edu/~mpj/pubs/instancechains.pdf, and probably further (I recall seeing the talk about instance chains and thinking “oh, it’s another proposal to ‘fix’ overlapping instances”, but in the meantime I forgot what the previous proposals were).

Anyway, we’re not making a statement about language design in this episode. If you know the technique and decide not to use it, that’s perfectly fine.

1 Like

it doesn’t, if you’re happy with -XAllowAmbigousTypes. Whether you should is a different topic.

1 Like

No I’m not. My “intolerably cluttered” is pondering why we need in effect to pass the same type twice to MyShow' [at around 16:30]: once as the type-with-value [a] that we’re showing; second as the result of applying Type Family IsString [a]. (Then using a Proxy is to document we’re interested in its type, not value.) We wouldn’t do that in a term-level method instantiation, we’d merely call the function inside the method instance.

The justification for introducing Type Families was/is allegedly that it makes type-level coding more alike term-level coding. (Because allegedly some learners find class/instance selection/type improvement too unfamiliar.) The demo for MyShow' is failing to demonstrate “more alike” IMO.

Wellll … You’re making a statement about poor language design for Overlapping (class) Instances. And I would agree with you the state of play in GHC is awkward, particularly it too often needs constraints on instances to deliver the type improvement that it can’t express in the instance head.

Prelude class Show dates back to Haskell pre-history. It wouldn’t have used Overlapping Instances back then; so the showList trick is quite cunning. But these days why object to:

instance {- OVERLAPPING -}  MyShow [Char]  where ...
instance {- OVERLAPS -}     MyShow [a]     where ...
instance {- OVERLAPPABLE -} MyShow a       where ...

I don’t object to this. It’s fine.

1 Like

These videos are awesome. :facepunch::+1: I learn a lot, thanks for your effort making them. Looking forward to the next episode.

1 Like