[Haskell-cafe] Check a lack of a constraint?

Olaf Klinke olf at aatal-apotheke.de
Tue Jul 13 23:09:30 UTC 2021


> 
> Hello cafe,
> 
> Is it possible in Haskell to check a lack of a certain constraint?
> 
> For example,
> 
> ```
> foo :: C => a
> foo = undefined
> 
> ```
> 
> Here `foo` can only be compiled if called with C satisfied. How do I write the 
> opposite, so that `foo` is only possible to use when C is not satisfied?
> 
> With best regards.

Indeed I found myself wanting this, too. Such a feature might reduce
the amount of overlapping instances, but of course it bears the danger
that others pointed out. 

For example, suppose you have a type 
T :: (* -> *)
and the library author of T already defined an instance 

instance C a => C (T a).

Now suppose you have a concrete type A which you know can never have a
(lawful) C A instance, but you can define an instance C (T A). The
latter would be formally overlapping with the former, even if you know
that the former will never be implemented. 
Thus, instead of local non-satisfiability, a token for the proof that
an instance can not exist might be more useful (and more safe). This
would correspond to case 2 in Sylvain Henry's GHC proposal. I have no
idea, however, how such a proof can be presented [*] in a way that can
not be circumvented by a third author and lead to unsoundness. 

As a concrete example, I defined an instance 
MonadParsec (StateT String Maybe)
which makes a wonderfully fast parser that you can swap in for a
Megaparsec parser, but it is overlapping because of
MonadParsec Maybe => MonadParsec (StateT s Maybe)
although it is absurd to try to make Maybe an instance of MonadParsec. 

Another example: Think about why the mtl library has an instance 
MonadFoo m => MonadFoo (BarT m)
for every pair (Foo,Bar) of transformers. 
(And there are quadratically many of these!)
It is precisely because a catch-all instance like
(MonadTrans t, MonadFoo m) => MonadFoo (t m)
will be overlapping as long as we can't rule out MonadFoo (t m) by
other means. 

Olaf

[*] The logically natural way would be to have a Void constraint, so
that we can say: 
MonadParsec Maybe => Void



More information about the Haskell-Cafe mailing list