[Haskell-cafe] Avoiding undecidables

José Iborra pepeiborra at gmail.com
Sat Dec 5 15:33:10 EST 2009


On Dec 5, 2009, at 6:58 PM, Michael Snoyman wrote:

> Hi all,
> 
> Well, I've got two problems which both want to be solved with undecidable and overlapping instances. Obviously, I'd like to try and avoid them. For the record, the problems have to do with the control-monad-failure and convertible packages. The code below *should* make clear what I'm trying to accomplish:
> 
> -- failure should not be limited to just monads, so...
> class Failure e f where
>     failure :: e -> f v
> class (Functor f, Failure e f) => FunctorFailure e f
> instance (Functor f, Failure e f) => FunctorFailure e f -- undecidable, overlaps
> class (Applicative f, Failure e f) => ApplicativeFailure e f
> instance (Applicative f, Failure e f) => ApplicativeFailure e f -- undecidable, overlaps
> class (Monad f, Failure e f) => MonadFailure e f
> instance (Monad f, Failure e f) => MonadFailure e f -- undecidable, overlaps
> 

(Functor|Monad|Applicative)Failure are little more than class synonyms, right ?
Or equivalently, do you envision the need of writing, say, a MonadFailure instance for a type A which which works differently than the existing Failure instance for A?
If the answer is no as I presume, then you don't want overlapping instances.

Regarding undecidable instances, I will say that from my point of view they do not constitute a language extension.
MPTCs are the language extension here. Since MPTCs can lead to non-terminating type checking,
a compiler can either allow any use of them, employ a termination prover to ensure that only well-behaved instances are defined,
or impose a set of restrictions that ensure termination. GHC does the latter, rather conservatively in some cases, and undecidable
instances is just a compiler flag that puts the burden of termination checking on the user.

In this case it is obvious that non-termination is not going to be a problem for the instances defined above.
Since you have already decided MPTCs are ok, in my opinion undecidable instances are fine here.
But I realize this is not the usual stance, so I might be wrong.


> And now the convertible issue. I want two type classes: Convert for anything which *might* be convertible, but might not. For example, sometimes a String can be converted to an Int (like the string "5"), but sometimes it will fail (like "five"). TotalConvert is when something *is* convertible, such as Int to String (simply the show function). Thus the following:
> 
> class Convert x y where
>     convert :: x -> Maybe y
> class Convert x y => TotalConvert x y where
>     totalConvert :: x -> y
> instance TotalConvert x y => Convert x y where -- Boom!
>     convert = Just . totalConvert


In this case Convert is not just a class synonym, so you are going to run into trouble with that code.

I would do this as follows:

class Convert x y => TotalConvert x y where
    totalConvert :: x -> y
    totalConvert = fromJust . convert

For instance,

instance Convert Integer Double where convert = Just . fromIntegral
instance TotalConvert Double    -- that's all


Cheers,
pepe


More information about the Haskell-Cafe mailing list